ddrescue 在读取超时时重置 USB 设备

ddrescue 在读取超时时重置 USB 设备

我的外置 USB 硬盘损坏了。当我将设备连接到 PC 时,我可以访问文件系统大约一分钟。此后,磁盘继续旋转,但每个 I/O 操作都会超时。

为了挽救我的数据,我想使用ddrescue,但由于设备每分钟都会停止工作,如果我不每次发生读取超时时重置 USB 设备,那么恢复效果不会很大,因为最可能的原因是设备再次挂断。有没有办法让 ddrescue 在发生读取超时时执行 shell 命令之类的操作?

无法通过 SATA 连接外部硬盘,因为里面没有可访问的 SATA 连接器。

答案1

我知道没人会读到这篇文章,所以我就不详细阐述了。很遗憾,因为这是我发现的唯一可行的方法。

问题:尝试从有大量坏扇区的硬盘中拯救大量有价值的照片。当读取软件遇到坏扇区时,它会挂起(变得无响应),您唯一能做的就是拔掉 USB。

尝试过的方法:(无效)

所有软件都承诺“不停地跳过坏扇区”。错。

  • DataRescue DD。过于简单且过时。
  • EaseUS Data Recovery。恢复直至找到坏扇区。
  • HDD 原始复制工具。
  • Parted Magic。它与使用普通 Linux 发行版 + ddrescue 相同。
  • Clonezilla。尝试了几种设置:VM、Live CD;USB 和 SATA 连接。
  • AOMEI Backupper 选项“磁盘克隆”。不克隆任何内容。
  • WinHex 选项克隆磁盘。
  • EaseUS 分区大师。

此外,这些工具不允许选择恢复块。

未尝试:DeepSpar Disk Imager(硬件,价格为 3,000 美元以上)。

接近解决方案

救援是一个复杂的程序,其中所有内容都是可配置的。它可在 Linux 和 Windows(使用 Cygwin)上运行。教程. 我无法运行 DDRescue-GUI(一些 XOpenDisplay 错误),此外 GUI 比命令行更简单。

尝试使用 ddrescue。有些人提出了一些解决方法:

  • 循环运行 ddrescue 并设置停止执行的超时选项。
  • 更改 OS HDD 超时以加快速度。/sys/block/sdb/device/timeout
  • 从内部重置 USB HDD。/sys/bus/usb/drivers/usb unbind-bind; usb_modeswitch -v 0x.... -p 0x.... --reset-usb。使用 Windows 的设备管理器也是一样的。
  • 结束ddrescue进程(killall、taskkill)。示例脚本。不起作用,因为进程没有响应。

解决方案

  1. 带有虚拟机 (VirtualBox) 的主机。主机运行一个服务器,该服务器侦听附加/分离命令并将其发送到 VM。

  2. 运行管理脚本的 Windows 客户虚拟机,该脚本控制工作流程:启动/停止 ddrescue、向主机发送连接/分离命令以及在 mapfile 中向前移动位置。

Linux(Debian)虚拟机无法工作。分离后,VirtualBox 显示:“无法将 USB 设备连接到虚拟机”

####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)
    

显然,您需要使配置适应您的系统。

答案2

有没有办法让其ddrescue在读取超时时执行 shell 命令等?

否,但您可以使用这些

-T interval
--timeout=interval
自上次成功读取后允许的最大时间,之后再放弃。默认为无穷大。[…]

-X n
--max-read-errors=n
放弃前允许的最大读取错误数。默认为无穷大。如果遇到的读取错误数1超过上限,则退出并显示状态。[…]n

并与“shell 命令等”一起ddrescue循环运行(mapfile 是必须的,因此可以恢复而不是重新启动)。ddrescue

我想在某些情况下这可能会有所帮助:

-O
--reopen-on-error
在复制阶段遇到每个读取错误后,关闭 infile 然后重新打开它。[…]

否则这个问题可能是:在 Ubuntu 10.04 中硬重置 USB

答案3

为了挽救我的数据,我想使用 ddrescue,但由于设备每分钟都会停止工作

一般来说,解决此问题的唯一方法是对驱动器进行电源循环。

我不确定是否可以以某种方式设置它以使用 ddrescue 自动运行,但是 HDDSuperClone 可以。

虽然这一页指的是专业版,这不再是最新版本,因为 HDDSuperClone 现在是开源

HDDSuperClone Pro 现在还能够直接控制 YEPKIT YKUSH 系列可切换 USB 集线器。这是一个选项,而不必修改 USB 延长线以与通用继电器一起使用。有关 YEPKIT 板的更多信息,请访问以下链接:

https://www.yepkit.com

最简单的方法可能是使用 YKUSH '内联' 可切换 USB 集线器。那么硬件设置如下:

host > usb > ykush > usb drive

在此处输入图片描述

相关内容