指定ブロックがどのファイルに属するか調べる

提供: ArchWiki
移動先: 案内検索

この記事では、ディスク上の指定のブロックがどのファイルに属するか調べる方法を説明します。主な目的は、ストレージに破損ブロックが生じたときに、どのファイルが破損したかを調べることです(そして重要なデータが失われたかどうかを確認できます)。

この記事の中のほとんどのコマンドを実行するには、root か、ディスクに直接読み込みアクセスができるユーザーになる必要があります(disk グループのメンバーであれば十分です)。いつもの通り、現在のバックアップを取っておくとよいでしょう。もうすぐディスクが壊れそうな場合は特にそうです。壊れそうかどうかを調べるには S.M.A.R.T. が役に立ちます。

今のところ、この記事は JFS と EXT ファイルシステムだけに向けて書かれています。

破損ブロックを見つける

badblocks コマンドを使います。このコマンドにはいくつかのスキャンモードがあります。リードオンリーモード(デフォルト)は正確さが一番低くなります。破壊的書き込みモード(-w オプション)は最も正確ですが、より時間がかかり、(当然ながら)ディスク上の全データを破壊します。ですのでこれはブロックが属するファイルを見つけるのには使えません。そして最後に非破壊的読み書きモードがあります。これはおそらく破壊的モードと同じくらい正確で、唯一のデメリットはおそらく一番遅いことです。しかし、ディスクが壊れつつあるとわかっているときは、リードオンリーモードが一番安全でしょう。

次のどちらかのコマンドを実行し、-v オプション(冗長表示)つきでリードオンリースキャンを実行します(x はディスクを表す文字で、y はパーティション番号です)。

ディスク全体をスキャンする:

# badblocks -v /dev/sdx

1個のパーティションをスキャンする:

# badblocks -v /dev/sdxy

ディスク全体をスキャンすると計算が少し面倒になります。各ファイルシステムはパーティションの先頭からブロック番号を数えるため、例えば 2 番目のパーティション上に破損ブロックがあり、そのパーティションがブロック 1000 から始まっていた場合、求めるブロック番号を得るには badblocks が表示するブロック番号から 1000 を引かなければなりません。つまりディスク全体のスキャンでブロック 1005 が破損していると表示された場合、2 番目のパーティションのブロック 5 が求める破損ブロックの番号になります。

あるいは、ディスク全体をスキャンして破損ブロックが見つかった場合、それがどのパーティションにあるかを計算して、そのパーティションに対して再度スキャンすることでもブロック番号が得られます。

もう一つ覚えておくべきなのは、badblocks はデフォルトでは 1024 バイトを 1 ブロックとすることです。そのため、-b オプションでブロックサイズを指定してファイルシステムのブロックサイズに一致させるか、後の手順において手計算でブロック番号を変換する必要があります。

パーティションの開始・終了位置を調べるには fdisk を使います。

ノート: 古いバージョンではデフォルトでシリンダー単位になるかもしれません。その場合は -u オプションでセクタ単位に変更できます。

fdisk で表示されるブロックサイズをメモしておいて、後でブロック数を変換できるようしておいてください。

# fdisk -l /dev/sdx
255 heads, 63 sectors/track, 19457 cylinders, total 312581808 sectors
'''Units = sectors of 1 * 512 = 512 bytes'''
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

ここまでで、破損ブロックのブロック番号(そのパーティションの開始位置からの相対位置)が分かりました。

ファイルシステムをデバッグする (JFS)

jfs_debugfs を使うと、JFS ファイルシステム内の全ての低レベル構造にアクセスできます。EXT など他のファイルシステムでも同じようなツールがあります。これを行う前に全てのファイルシステムをアンマウントしておくとよいでしょう。次を実行します:

# jfs_debugfs /dev/sdxy

このコマンドを実行するとコマンドコンソールに入ります。最初にすべきことは aggregate block size を確認することです。これは(おそらく)ファイルシステムのブロックサイズと同じです。JFSのデフォルトは 4096 バイトのようです。

badblocks を実行したときのブロックサイズがこのファイルシステムのブロックサイズと一致していなかった場合は、ブロック番号を変換する必要があります(パーティションの開始位置からの相対ブロック番号を使うことに注意してください)。

つまり、ブロックサイズが 1024 バイトの場合のブロック番号 100 は、ブロックサイズが 4096 バイトの場合のブロック番号 25 になります。変換公式は:

(original block number) / ((filesystem block size) / (badblocks block size))

さて、本来の目的は inode 番号を取得することです。このコマンドを実行してください:

d ブロック番号 0 i

この構文の意味は、d コマンドはディスプレイ、ブロック番号、オフセット(ここでは 0 にしている)、表示フォーマット i は inode となっています。

ノート: ブロックが使用中でないと表示される場合は、そのブロックが割り当てられておらず、空きスペースになっていることを意味します。これは、何も重要なデータが失われていないということなので良いことです。

di_number にセットされている 10 進数が私達の求めるものです。ここで x とタイプしてディスプレイモードから抜けます。全ての破損ブロックに対してこのディスプレイコマンドを繰り返し、それらの inode 番号を記録しておきます。inode に関するパーミッションやファイルタイプなど他の情報を得るには、次のようにタイプします:

i inode番号

inode 番号が分かったら q とタイプして終了します。

破損ファイルを見つける (JFS/Universal)

これでようやく破損ファイルを見つけられます。GNU find ユーティリティを使います。ファイルシステムをマウントして次を実行します:

# find / -inum inode番号

"/" はその inode が属するファイルシステムのマウントポイントで置き換えてください。/ を検索して 2 個以上のファイルシステムがマウントされていたら(普通はそうなっているでしょう)、異なるファイルシステム上で同じ inode 番号のファイルが複数見つかるでしょう。そしてその場合は明らかに時間が長くかかります。inode はファイルシステムの中でだけユニークなことに注意してください。

ファイルシステムをデバッグする (Ext2/3/4)

tune2fs を使うと、各種 EXT ファイルシステム内の全ての低レベル構造にアクセスできます。これを行う前に全てのファイルシステムをアンマウントしておくとよいでしょう。

最初にやることは、ファイルシステムのブロックサイズを取得することです。以下のコマンドを実行します:

# tune2fs -l /dev/sdxy | grep Block
Block count:              29119820
Block size:               4096

この場合は 4096 がブロックサイズです(これがデフォルトのようです)。

badblocks を実行したときのブロックサイズがこのファイルシステムのブロックサイズと一致していなかった場合は、ブロック番号を変換する必要があります(パーティションの開始位置からの相対ブロック番号を使うことに注意してください)。

つまり、ブロックサイズが 1024 バイトの場合のブロック番号 100 は、ブロックサイズが 4096 バイトの場合のブロック番号 25 になります。変換公式は:

(original block number) / ((filesystem block size) / (badblocks block size))

さて、本来の目的は inode 番号を取得することです。以下のコマンドを実行してください:

# debugfs

そして debugfs のコンソールにおいて、破損セクタを含んでいる EXT のパーティションを引数に open コマンドを実行します:

debugfs:  open /dev/sdxy

最後に testb コマンドを実行してそのブロックに関する情報を取得します(この例ではブロック 1000 番):

debugfs:  testb ブロック番号
ノート: ブロックが使用中でないと表示される場合は、そのブロックが割り当てられておらず、空きスペースになっていることを意味します。これは、何も重要なデータが失われていないということなので良いことです。

ブロックが使用中なら、このコマンドで inode 番号を取得できます:

icheck ブロック番号

これは 2 個の数字を表示します。ブロック番号と inode 番号です。

破損ファイルを見つける (Ext2/3/4)

inode 番号 (icheck コマンドで表示される 2 番目の番号) を引数に ncheck コマンドを実行します:

ncheck inode番号

この破損ブロックを使っているファイルのフルパスが表示されます。これで実際に何が破損したのかがわかります。

もし inode 番号が非常に小さくて ncheck がパス表示に失敗する場合は、おそらくジャーナル自体が壊れています。ジャーナルを削除するには、このコマンドをパーティションに対して実行します:

# tune2fs -O ^has_journal /dev/sdxy

もう一度 debugfs コンソールで testb コマンドを使ってそのブロックを調べると、もし本当にジャーナルに使われていたのであれば、今度は使われていると表示されないはずです。 次のコマンドで新しいジャーナルを構築します:

# tune2fs -j /dev/sdxy

強制的に破損ブロックを代替処理させる

まず、smartctl コマンドでハードディスク上にいくつの破損ブロックが認識されているかを調べられます:

# smartctl -t long /dev/sdx

テストが完了するまで待ってから以下のコマンドを実行してください:

# smartctl -l selftest /dev/sdx
ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
'''5 Reallocated_Sector_Ct     0x0033   100   100   005    Pre-fail  Always       -       0'''
'''196 Reallocated_Event_Count 0x0032   100   100   000    Old_age   Always       -       0'''
197 Current_Pending_Sector  0x0022   100   100   000    Old_age   Always       -       0
'''198 Offline_Uncorrectable   0x0008   100   100   000    Old_age   Offline      -       0'''

透過的に破損ブロックをスペアの予備セクタへ代替させるには、単に root で dd コマンドを使って破損ブロックにゼロを書き込むだけです。注意すべきなのは、このコマンドを実行するとき、ファイルシステムと同じブロックサイズを使うことと、ブロックの位置をディスク全体でなくファイルシステムがあるパーティションからの相対位置で指定することです:

# dd if=/dev/zero of=/dev/sdxy bs=4096 count=1 seek=2269012
# sync

本当に破損セクタが代替処理されたかどうかを確認するには、smartctl コマンドを使って Reallocated_Sector_Ct か Reallocated_Event_Count の値が増えているかを確認します。

# smartctl -A /dev/sdx
ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
'''5 Reallocated_Sector_Ct     0x0033   100   100   005    Pre-fail  Always       -       1'''
'''196 Reallocated_Event_Count 0x0032   100   100   000    Old_age   Always       -       1'''
197 Current_Pending_Sector  0x0022   100   100   000    Old_age   Always       -       0
'''198 Offline_Uncorrectable   0x0008   100   100   000    Old_age   Offline      -       1'''

Offline_Uncorrectable を 0 に戻すには、SMART のロングテストとセルフテストを実行する必要があります:

# smartctl -t long /dev/sdx

テストが完了するまで待ってから以下を実行してください:

# smartctl -l selftest /dev/sdx

参考