1

I have a damaged external usb hdd. When i connect the device to the pc i can access the file system for round about a minute. After that period the disk keeps spinning but every io operation times out.

To rescue my data i want to use ddrescue but since the device stops working every minute, this won't recover much, when i don't reset the usb device each time a read timeout occurs, since the most probable reason for that is, that the device hang up again. Is there a way to let ddrescue execute a shell command or so, whenever a read timeout occurs?

It is not possible to connect the external hdd via sata, since there isn't a accessible sata connector inside.

1

Is there a way to let ddrescue execute a shell command or so, whenever a read timeout occurs?

No, but you can use these:

-T interval
--timeout=interval
Maximum time since last successful read allowed before giving up. Defaults to infinity. […]

-X n
--max-read-errors=n
Maximum number of read errors allowed before giving up. Defaults to infinity. Exit with status 1 if more than n read errors are encountered. […]

and run ddrescue in a loop (mapfile is a must so ddrescue resumes rather than reinitiates) along with "a shell command or so".

I guess in some circumstances this may help:

-O
--reopen-on-error
Close infile and then reopen it after every read error encountered during the copying phase. […]

Otherwise this question maybe.

1

I know nobody's gonna read this, so I won't elaborate it too much. And it's a pity because this is the only method that works.

PROBLEM: Trying to rescue lots of valuable photos from a HDD with lots of bad sectors. When the reading software stumbles on a bad sector, it hangs (becomes unresponsive) and the only thing you can do is to unplug the USB.

THINGS TRIED: (didn't work)

All software promise that they "skip bad sectors without stop". False.

  • DataRescue DD. Simplistic and obsolete.
  • EaseUS Data Recovery. Recovers until it finds a bad sector.
  • HDD Raw Copy Tool.
  • Parted Magic. It's the same as using plain Linux distro + ddrescue.
  • Clonezilla. Tried several setups: VM, Live CD; USB and SATA connections.
  • AOMEI Backupper option "Disk Clone". Clones nothing.
  • WinHex option Clone Disk.
  • EaseUS Partition Master.

Besides, these tools don't allow to select a block o resume.

Not tried: DeepSpar Disk Imager (hardware, costs $3.000+).

APPROACHING THE SOLUTION

ddrescue is a complex program where everything is configurable. It runs on Linux and on Windows (with Cygwin). Tutorial. I wasn't able to run DDRescue-GUI (some XOpenDisplay error), besides the GUI simpler than the command-line.

Things tried with ddrescue. Some people suggested some workarounds:

  • Run ddrescue in a loop and set timeout options that stop execution.
  • Change OS HDD timeout to speed up. /sys/block/sdb/device/timeout
  • Reset the USB HDD from the inside. /sys/bus/usb/drivers/usb unbind-bind; usb_modeswitch -v 0x.... -p 0x.... --reset-usb. Using Window's Device Manager is the same.
  • End the ddrescue process (killall, taskkill). Example script. Doesn't work, as the process is unresponsive.

SOLUTION

  1. A host with a Virtual Machine (VirtualBox). The host runs a server that listens to attach/detach commands and sends them to the VM.

  2. A Windows guest VM that runs a manager script that controls the workflow: start/stop ddrescue, send attach/detach commands to host, and move forward the position in the mapfile.

A Linux (Debian) VM didn't work. After detaching, VirtualBox says: "Failed to attach the USB device to the VM"

####Script1 server.py runs in host####
import subprocess
from bottle import route, run

exe = "C:/Program Files/Oracle/VirtualBox/VBoxManage.exe"
vm = "Win7"
id = "54a7249b-930a-4d49-a679-9a7b8810adcc"  # VBoxManage list usbhost

def execute(cmd):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
    (output, err) = p.communicate()
    p_status = p.wait()
    res = output.decode("utf-8")
    if "error" in res:
        return "1"
    else:
        return "0"
    return

@route('/attach')
def cmd1():
    res = execute('"' + exe + '" controlvm ' + vm + " usbattach " + id)
    return res

@route('/detach')
def cmd2():
    res = execute('"' + exe + '" controlvm ' + vm + " usbdetach " + id)
    return res

run(host='0.0.0.0', port=80)

.

####Script2 manager.py runs in guest####
import requests 
import subprocess
import time
import re
import os

def get_id(disk):
    p = subprocess.Popen("wmic diskdrive get Index, Model", stdout=subprocess.PIPE, shell=True)
    (output, err) = p.communicate()
    p_status = p.wait()
    res = output.decode("utf-8")
    lines = res.splitlines()
    id = ""
    for line in lines:
        if line.find(disk) > 1:
            id = line[0]
    return id

def check_disk(disk):
    p = subprocess.Popen("wmic diskdrive get Model", stdout=subprocess.PIPE, shell=True)
    (output, err) = p.communicate()
    p_status = p.wait()
    res = output.decode("utf-8")
    if disk in res:
        return 1
    else:
        return 0

def mod_disk(disk, cmd):
    v=1 if cmd=='attach' else 0
    for i in range(3):
        response = requests.get('http://192.168.1.20/'+cmd)
        res = response.content.decode("utf-8")
        if "0" in res:
            while True:
                if(check_disk(disk)==v):
                    break
                time.sleep(1)
                print("[" + disk + " " + cmd + " wait]")
            print("[" + disk + " " + cmd + " ok]")
            break
        time.sleep(3)

def dec2hex(n):
    return "%X" % n

def hex2dec(s):
    return int(s, 16)

def update_mapfile():
    skip = 5000000 #5Mb
    with open(mapfile, 'r') as file:
        data = file.readlines()
    line7 = data[6]
    p = re.compile('^0x(.*?) ')
    hval = p.findall(line7)[0] 
    dval = hex2dec(hval)
    dval2 = dval + skip
    hval2 = dec2hex(dval2)
    line7b = re.sub(hval, hval2, line7)
    data[6] = line7b
    with open(mapfile, 'w') as file:
        file.writelines(data)

############################
disk = "WD 3200AAJ"
mapfile = "E:/mapfile.log"
inpt = "/dev/sdb" if get_id(disk) == "1" else "/dev/sdc"
cmdl = 'c:/cygwin/bin/ddrescue.exe -v -d -n -O ' + inpt + ' E:/image.img E:/mapfile.log'

#Start ddrescue
proc = subprocess.Popen(cmdl, shell=True, stdin=None, stdout=None, stderr=None, close_fds=True)
print("Start ddrescue")
time.sleep(60)

while True:
    ft = os.path.getmtime(mapfile)
    n = time.time()
    if n-ft > 60:
        #send sigterm
        p = subprocess.Popen('taskkill /f /fi "imagename eq ddrescue.exe"', stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
        (output, err) = p.communicate()
        p_status = p.wait()
        print("send sigterm")
        #detach HDD
        mod_disk(disk, 'detach')
        time.sleep(2)
        print("detach HDD")
        #update modfile
        update_mapfile()
        print("update modfile")
        #attach HDD
        mod_disk(disk, 'attach')
        time.sleep(2)
        print("attach HDD")
        #restart ddrescue
        inpt = "/dev/sdb" if get_id(disk) == "1" else "/dev/sdc"
        cmdl = 'c:/cygwin/bin/ddrescue.exe -v -d -n -O ' + inpt + ' E:/image.img E:/mapfile.log'
        proc = subprocess.Popen(cmdl, shell=True, stdin=None, stdout=None, stderr=None, close_fds=True)
        print("restart ddrescue")
    time.sleep(60)

Obviously you will need to adapt the configuration to your system.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.