web-dev-qa-db-ja.com

Linuxおよびext3 / 4上のrawデバイスのオフセットからのiノード/ファイルの逆ルックアップ?

Linuxでは、rawディスクデバイスへのオフセットが与えられた場合、パーティション+ iノードにマップバックすることは可能ですか?

たとえば、文字列「xyz」が/ dev/sdaのバイトオフセット1000000に含まれていることがわかっているとします(たとえば、xxd -l 100 -s 1000000/dev/sdaは「xyz」で始まるダンプを示します)

1)オフセット1000000がどのパーティション(存在する場合)にあるかをどのように把握しますか?(これは簡単だと思いますが、完全を期すために含めています)

2)オフセットがパーティションにあると仮定すると、オフセットが属するiノードを見つける(または空き領域の一部であると判断する)にはどうすればよいですか?おそらくこれはファイルシステム固有のものですが、その場合、ext4とext3でこれを行う方法を知っている人はいますか?

7
lilinjn

私は同じようなことをしなければならなかったので、私は自分の解決策を共有したいと思いました。

Udisks --show-info出力の「offset」要素と「size」要素をチェックすることで、ドライブのバイトオフセットがどのパーティションに属しているかを確認できます。例えば.

user@Host:~$ Sudo udisks --show-info /dev/sda1 | grep -i 'offset'
    offset:                    1048576
    alignment offset:          0

ディスクオフセットからこのオフセットを減算して、バイトオフセットをパーティションに取り込みます。したがって、/ dev/sdaのディスクオフセット(10000000)は、/ dev/sda1のパーティションオフセット(10000000-1048576)= 8951424です。

次のコマンドを使用して、パーティション内のブロックの大きさを確認できます。

user@Host:~$ Sudo tune2fs -l /dev/sda1  | grep -i 'block size'
Block size:               4096

パーティションのバイトオフセットをブロックサイズで除算して、ブロックオフセットを決定します。この場合は8951424/4096 = 2185

次のコマンドを実行して、そのブロックを占めるiノードを確認します。

user@Host:~$ Sudo debugfs -R "icheck 2185" /dev/sda1
debugfs 1.41.11 (14-Mar-2010)
Block   Inode number
2185    123456 

次に、次のコマンドを使用して、そのiノードのファイル名を確認します。

user@Host:~$ Sudo debugfs -R "ncheck 123456" /dev/sda1
debugfs 1.41.11 (14-Mar-2010)
Inode   Pathname
123456  /tmp/some-filename.txt

これがどのように行われるかについてのより長い説明があります http://www.randomnoun.com/wp/2013/09/12/determining-the-file-at-a-specific-vmdk-offset

14
Greg Knox

グレッグノックスの答えは正しいですが、もっと簡単かもしれません。以下のソースコードで、すべての演算を実行するシェルスクリプト lba2file を作成しました。

Lba2fileの使用例

質問で提起された問題の解決(アドレスをバイト単位で指定):

kremvax$ Sudo lba2file -b 1000000 /dev/sda
Disk Byte 1000000 is at filesystem block 124744 in /dev/sda1
Block is used by inode 21762939
Searching for filename(s)...
Inode           Pathname
21762939        /home/lilnjn/backups/adhumbla_pics_2.Zip

S.M.A.R.Tでの使用例.

ハードドライブに不良セクタがある場合は、セクタにゼロを書き込んで再マッピングする前に、破損しているファイルを確認することをお勧めします。 smartctllba2fileを使用して簡単に行うことができます。

kremvax$ Sudo smartctl -C -t short /dev/sdd    
kremvax$ Sudo smartctl -a /dev/sdd | grep '^# 1'
# 1  Short captive   Completed: read failure   90%   20444   1218783739

最終的な数値1218783739は、バイトではなくブロック単位のディスクアドレスであることに注意してください。

kremvax$ Sudo lba2file 1218783739 /dev/sdd
Disk Sector 1218783739 is at filesystem block 152347711 in /dev/sdd1
Block is used by inode 31219834
Searching for filename(s)...
Inode           Pathname
31219834        /home/mryuk/2020-11-03-3045-us-la-msy.jpg
31219834        /home/mryuk/web/2020-11-03-3045-us-la-msy.jpg

討論

私のスクリプトはデフォルトでバイトではなくセクターアドレス(「LBA」と呼ばれることが多い)になっていることに注意してください。これは、ドライブに不良ブロックがある場合にsmartctlなどのツールが報告するのはLBAであるためです。ただし、セクターではなくバイトを指定する場合は、-bフラグを指定するだけです。

ソースコード

ファイルにカットアンドペーストするか、 ここ をクリックしてダウンロードします https://github.com/hackerb9/lba2file/

#!/bin/bash

# lba2file: Given an LBA number and a drive in /dev/, print which
# filename(s), if any, use that sector.

# This is the opposite of `hdparm --fibmap /foo/bar`

# B9 Feburary 2019

if [[ "$1" == "-b" ]]; then
    BYTESFLAG=Byte
    shift
fi

if [[ $# -lt 2 ]]; then
    echo "Usage: lba2file  [-b]  <sector number>  /dev/sdX"
    echo "      -b: Use byte address instead of sector"
    exit 1
fi

lba=$1
drive=$2


for partition in ${drive}?; do
    info=$(udisks --show-info $partition)
    if ! e2blocksize=$(tune2fs -l $partition 2>/dev/null |
                              grep '^Block size' | egrep -o '[0-9]+'); then
        continue                # Not an Ext2/3/4 partition
    fi
    offset=$(grep '^    offset:' <<< "$info" | egrep -o '[0-9]+')
    partitionsize=$(grep '^  size:' <<< "$info" | egrep -o '[0-9]+')
    diskblocksize=$(grep '^  block size:' <<< "$info" | egrep -o '[0-9]+')

    # Typically: e2blocksize==4096, diskblocksize==512
    # Example: offset=1048576, partitionsize=640133980160
    if [[ -z "$BYTESFLAG" ]]; then
        byteaddress=$((lba * diskblocksize))
    else
        byteaddress=$lba
    fi
    if [[ $byteaddress -lt $offset ||
          $byteaddress -ge $((offset+partitionsize)) ]]; then
        echo "Not in $partition"
        continue                # Not in this partition
    fi

    # Shift to byteaddress within partition
    partitionbyteaddress=$((byteaddress - offset))

    # Scale address by filesystem blocksize to find filesystem block number
    e2blockaddress=$((partitionbyteaddress / e2blocksize))

    Sector=${BYTESFLAG:-Sector}
    echo "$Sector $lba is at filesystem block $e2blockaddress in $partition"
    inode=$(debugfs -R "icheck $e2blockaddress" $partition 2>/dev/null |
                   tail -1 | cut -f2)
    if [[ "$inode" && "$inode" != "<block not found>" ]]; then
        echo "$Sector is used by inode $inode"
        echo "Searching for filename(s)..."
        debugfs -R "ncheck $inode" $partition 2>/dev/null
    else
        echo "$Sector is not in use."
    fi
done
0
hackerb9