Rescue a Raspberry Pi SD card with ddrescue! After 10 years, the card failed, and dd couldn’t read it. ddrescue recovered the data, and journal removal fixed the ext4 superblock issue. Finally, PiShrink reduced the image size.
相模原市で IoT 設計を受託しているファームロジックスです。
「諦めない話」が続きます。
私事ですが先日、仕事場の UPS(無停電電源)がトラブルを起こしたとき、長いこと(10年くらいでしょうか)使っていた Raspberry Pi が再起動しなくなりました。そこに特別重要なデータが入っているとは思わなかったので放置していたのですが、先日とある事情で SD カードの中身を確認したくなりました。
再起動しない理由は SD カードの故障(wear)だと予想したのですが、とりあえず Linux(Ubuntu )の dd コマンドで読み出しを試みました。
以下のようなコマンドを実行します。
$ sudo dd if=/dev/sdb of=raspberrypi_backup.img bs=4M status=progress
しかし、全然進みません。dmesg
してみると、案の定 SD カードからデータが読めていません。
I/O error, dev sdb, sector 149504 op 0x0:(READ) flags 0x80700 phys_seg 16 prio class 0
やっぱりダメか。諦めかけたのですが、ChatGPT に聞いてみます。すると、ddrescue というツールがあることが分かりました。それは知らなかった! さっそく試してみます。
GNU ddrescue で読み出す
$ sudo ddrescue -d -r3 /dev/sdb raspberrypi_rescue.img rescue.log
面白いツールです。こんなふうにレスキュー作業が始まりました。
$ sudo ddrescue -d -r3 /dev/sdb raspberrypi_rescue.img rescue.log GNU ddrescue 1.23 Press Ctrl-C to interrupt ipos: 2823 MB, non-trimmed: 5832 kB, current rate: 60854 B/s opos: 2823 MB, non-scraped: 0 B, average rate: 531 kB/s non-tried: 13126 MB, bad-sector: 0 B, error rate: 0 B/s rescued: 2799 MB, bad areas: 0, run time: 1h 27m 47s pct rescued: 17.56%, read errors: 89, remaining time: 5h 52m time since last successful read: 0s Copying non-tried blocks... Pass 1 (forwards)
しかし時間がかかります。総計 2日以上は費やしたでしょうか。最終的に、99.99% 以上のブロックが救出できました。(途中で一度うっかり Ctrl-C で停めてしまい再開したので、以下の時間表示はあてになりません。)
GNU ddrescue 1.23 Press Ctrl-C to interrupt Initial status (read from mapfile) rescued: 15930 MB, tried: 733184 B, bad-sector: 733184 B, bad areas: 520 Current status ipos: 15317 MB, non-trimmed: 0 B, current rate: 0 B/s opos: 15317 MB, non-scraped: 0 B, average rate: 1 B/s non-tried: 0 B, bad-sector: 530944 B, error rate: 12 B/s rescued: 15931 MB, bad areas: 376, run time: 1d 9h 41m pct rescued: 99.99%, read errors: 2779, remaining time: 8h 11m time since last successful read: 11m 7s Finished
なんとか希望が持てました。
fsck がスーパーブロックを見つけられない!?
次は fsck でファイルシステムをチェック、および修正します。
その前に、作業を間違えて 2日間の苦労を無駄にしないよう、まずはバックアップを作ります。
$ sudo chown yokoyama.yokoyama raspberrypi_rescue.img $ cp -p raspberrypi_rescue.img raspberrypi_rescue_backup.img
loop デバイスをセットアップします。
$ sudo losetup -Pf --find raspberrypi_rescue.img $ losetup -a
fsck をかけます。
$ sudo fsck -y /dev/loop5p2
しかし無情にもエラーです。
$ fsck from util-linux 2.37.2 e2fsck 1.46.5 (30-Dec-2021) ext2fs_open2: Bad magic number in super-block fsck.ext2: Superblock invalid, trying backup blocks... /dev/loop5p2: recovering journal fsck.ext2: unable to set superblock flags on /dev/loop5p2 /dev/loop5p2: ***** FILE SYSTEM WAS MODIFIED ***** /dev/loop5p2: ********** WARNING: Filesystem still has errors **********
どうも、スーパーブロックをちゃんと読めていないようです。予備のスーパーブロックは大丈夫でしょうか。ChatGPT にお伺いを立てたところ、mke2fs
というコマンドで、バックアップスーパーブロックの位置を確認できるそうです。(-n
オプションを忘れないように!)
$ sudo mke2fs -n /dev/loop5p2 Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, ...
得られたスーパーブロックのバックアップ位置を参考に、次のようにしてスーパーブロックを明示的に指定して fsck します。
$ sudo fsck.ext4 -b 32768 -y /dev/loop5p2 $ sudo fsck.ext4 -b 98304 -y /dev/loop5p2 (snip)
どれを指定してもやっぱりだめです。ディスクブロックの 99.99% を復旧できているのに、全てのスーパーブロックが壊れているなんてことはあるのでしょうか? 私の作業が何か間違っているのでしょうか。本当に /dev/loop5p2
は ext4 なのでしょうか。
$ sudo blkid /dev/loop5p2 /dev/loop5p2: UUID="f24a4949-f4b2-4cad-a780-a138695079ec" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="0009bf4f-02"
大丈夫そうですね。次に、dumpe2fs
してみます。
$ sudo dumpe2fs /dev/loop5p2 dumpe2fs 1.46.5 (30-Dec-2021) Filesystem volume name: <none> Last mounted on: / Filesystem UUID: f24a4949-f4b2-4cad-a780-a138695079ec Filesystem magic number: 0xEF53 Filesystem revision #: 1 (dynamic) Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent flex_bg sparse_super large_file uninit_bg dir_nlink extra_isize Filesystem flags: signed_directory_hash Default mount options: user_xattr acl Filesystem state: clean (snip)
これも大丈夫そうです。ChatGPT に再度助けを求めます。
すると、次のような回答が得られました。
問題の可能性
-
- スーパーブロックは読み取れるが、ジャーナル (
has_journal
) に問題があるfsck
で「ジャーナルの回復ができない」とエラーが出ていたので、ジャーナルが破損している可能性 が高い。- 解決策: ジャーナルを強制的に削除し、再作成する。
- マウントに必要なメタデータが破損
- スーパーブロックは
dumpe2fs
で表示されるが、実際のファイルシステム構造が破損している。 - 解決策:
fsck
を ジャーナルを無効化した状態で強制実行 する。
- スーパーブロックは
- スーパーブロックは読み取れるが、ジャーナル (
ジャーナルを強制削除する
まずは、ジャーナルの削除から試してみましょう。
$ sudo tune2fs -O ^has_journal /dev/loop5p2
なにも返事がありませんが、次に進みます。
$ sudo fsck.ext4 -f -y /dev/loop5p2
おーっ。無事に fsck が走りました。たくさんのメッセージ(作業)が表示されることもなく、ファイルシステムが復旧されました。
ジャーナルを再作成します。
$ sudo tune2fs -j /dev/loop5p2
ファイルシステムは無事に直りました。
なお、FAT のほうもチェックしておきます。
$ sudo fsck.vfat -y /dev/loop5p1
ファイルシステムを mount
してみると、ちゃんとファイルにアクセスできます。おそらくファイルシステム上のデータにはエラーがある(ファイルの一部が壊れている)のでしょうか、とりあえずできることはしました。
ファイルシステムをシュリンクする
次に、このイメージファイル raspberrypi_rescue.img
には無駄な領域があって大きすぎるので、ファイルシステムをシュリンクしましょう。このツールが非常に便利です。
$ sudo pishrink.sh raspberrypi_rescue.img shrunk_rescue.img
無事にシュリンクできました!
まとめ
今回の作業にあたって、ChatGPT のおかげで 2つのことを助けて貰いました。
- Raspberry Pi の SD カードが壊れて、
dd
で読めなくなっていても諦めない!
→ GNU ddrescue を使おう! - fsck で ext4 のスーパーブロックが見つからない?
→ ジャーナルの破損を疑おう! 一度ジャーナルを削除してから fsck しよう。
皆さんも、困ったときには諦めずにファイルシステムの復旧にチャレンジしてください。