web-dev-qa-db-ja.com

ddrescueは読み取りタイムアウト時にUSBデバイスをリセットします

外部USBHDDが破損しています。デバイスをPCに接続すると、ファイルシステムに約1分間アクセスできます。その期間の後、ディスクは回転し続けますが、すべてのio操作はタイムアウトします。

データをレスキューするためにddrescueを使用したいのですが、デバイスが毎分動作を停止するため、読み取りタイムアウトが発生するたびにUSBデバイスをリセットしないと、これはあまり回復しません。その理由は、デバイスが再びハングアップするためです。読み取りタイムアウトが発生するたびに、ddrescueにシェルコマンドなどを実行させる方法はありますか?

内部にアクセス可能なsataコネクタがないため、sataを介して外部hddを接続することはできません。

1
Pretasoc

誰もこれを読むつもりはないので、あまり詳しく説明しません。そして、これが機能する唯一の方法であるため、それは残念です。

[〜#〜]問題[〜#〜]:不良セクタがたくさんあるHDDからたくさんの貴重な写真を救出しようとしています。読み取りソフトウェアが不良セクタでつまずくと、ハングし(応答しなくなります)、実行できるのはUSBを抜くことだけです。

試してみたもの:(機能しませんでした)

すべてのソフトウェアは、「不良セクタを停止せずにスキップする」ことを約束しています。誤り。

  • DataRescueDD。単純で時代遅れです。
  • EaseUSデータ復旧。不良セクタが見つかるまで回復します。
  • HDDRawコピーツール。
  • 別れの魔法。これは、プレーンなLinuxディストリビューション+ ddrescueを使用するのと同じです。
  • Clonezilla。いくつかのセットアップを試しました:VM、Live CD; USBおよびSATA接続。
  • AOMEIBackupperオプション「ディスククローン」。クローンはありません。
  • WinHexオプションのクローンディスク。
  • EaseUSPartitionMaster。

さらに、これらのツールでは、ブロックまたは履歴書を選択することはできません。

試していません:DeepSpar Disk Imager(ハードウェア、価格は$ 3.000以上)。

ソリューションへのアプローチ

ddrescue は、すべてを構成できる複雑なプログラムです。 LinuxおよびWindows(Cygwinを使用)で実行されます。 チュートリアル 。コマンドラインよりも単純なGUI以外に、DDRescue-GUIを実行できませんでした(XOpenDisplayエラーが発生しました)。

Ddrescueで試してみました。一部の人々はいくつかの回避策を提案しました:

  • Ddrescueをループで実行し、実行を停止するタイムアウトオプションを設定します。
  • OSHDDのタイムアウトを変更して高速化します。/sys/block/sdb/device/timeout
  • USBHDDを内側からリセットします。/sys/bus/usb/drivers/usb unbind-bind; usb_modeswitch -v 0x .... -p 0x ....-- reset-usb。ウィンドウのデバイスマネージャの使用は同じです。
  • Ddrescueプロセス(killall、taskkill)を終了します。例 スクリプト 。プロセスが応答しないため、機能しません。

[〜#〜]ソリューション[〜#〜]

  1. 仮想マシン(VirtualBox)を備えたホスト。ホストは、アタッチ/デタッチコマンドをリッスンしてVMに送信するサーバーを実行します。

  2. ワークフローを制御するマネージャースクリプトを実行するWindowsゲストVM:ddrescueの開始/停止、ホストへのアタッチ/デタッチコマンドの送信、およびマップファイル内の位置の前方への移動。

Linux(Debian)VMは機能しませんでした。切り離した後、VirtualBoxは「USBデバイスを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)

明らかに、構成をシステムに適合させる必要があります。

1
John Doe

読み取りタイムアウトが発生するたびに、ddrescueにシェルコマンドなどを実行させる方法はありますか?

いいえ、ただし使用できます これら

-T interval
--timeout=interval
最後に正常に読み取られてから、あきらめるまでの最大許容時間。デフォルトは無限大です。 […]

-X n
--max-read-errors=n
あきらめる前に許可される読み取りエラーの最大数。デフォルトは無限大です。 nを超える読み取りエラーが発生した場合は、ステータス1で終了します。 […]

そして、「シェルコマンドなど」と一緒にループ内でddrescueを実行します(mapfileは必須なので、ddrescueは再開するのではなく再開します)。

状況によっては、これが役立つと思います。

-O
--reopen-on-error
infileを閉じ、コピーフェーズ中に読み取りエラーが発生するたびに再度開きます。 […]

そうでなければ この質問 多分。

1