在 Linux 中,给定原始磁盘设备的偏移量,是否可以映射回分区 + inode?
例如,假设我知道字符串“xyz”包含在 /dev/sda 上的字节偏移量 1000000 处:(例如 xxd -l 100 -s 1000000 /dev/sda 显示以“xyz”开头的转储)
1)我如何确定偏移量 1000000 位于哪个分区(如果有的话)?(我想这很容易,但为了完整性我把它包括在内)
2)假设偏移量位于分区中,我该如何找到它属于哪个 inode(或确定它是可用空间的一部分)?据推测这是文件系统特定的,在这种情况下,有人知道如何对 ext4 和 ext3 执行此操作吗?
答案1
我刚好要做类似的事情,所以我想分享我的解决方案。
您可以通过检查 udisks --show-info 输出的“偏移量”和“大小”元素来查看驱动器字节偏移属于哪个分区;例如
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
运行以下命令来找出哪个 inode 占用了该块:
user@host:~$ sudo debugfs -R "icheck 2185" /dev/sda1
debugfs 1.41.11 (14-Mar-2010)
Block Inode number
2185 123456
然后使用以下命令找出该 inode 的文件名:
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
答案2
Greg Knox 的答案是正确的,但可能更简单。我写了一个 shell 脚本,lba2文件,它为您执行所有算术运算,源代码如下。
[更新:脚本不再依赖于udisks
二进制]。
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
SMART 使用示例
如果您的硬盘驱动器有坏扇区,您可能希望在通过向扇区写入零来重新映射扇区之前找出损坏的文件。您可以使用smartctl
和轻松完成此操作lba2file
。
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”)而不是字节。这是因为 LBA 是smartctl
当驱动器上有坏块时工具会报告的内容。但是,如果您想指定字节而不是扇区,只需给出标志即可-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 May 2020
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
if [[ $(id -u) -ne 0 ]]; then
echo "Please run as root using 'sudo $@'" >&2
exit 1
fi
lba=$1
drive=$2
drive=${drive#/dev/} # Remove /dev/ prefix, if any.
if [[ "$drive" =~ ^(.*)[0-9]$ ]]; then # Either user specified a partition.
searchparts="/sys/class/block/$drive"
drive=${BASH_REMATCH[1]}
else # Or user specified a drive.
shopt -s nullglob # Don't use '?' literally.
searchparts=$(eval echo /sys/class/block/${drive}?)
fi
for partition in $searchparts; do
device=/dev/${partition#/sys/class/block/}
cd "$partition" || continue
start=$(cat "$partition/start")
partitionsize=$(cat "$partition/size")
hwsectorsize=$(cat "/sys/class/block/$drive/queue/hw_sector_size")
# Typically: e2blocksize==4096, hwsectorsize==512
# Example: start=1048576, partitionsize=640133980160
# Do a sanity check.
if [[ -z "$start" || -z "$partitionsize" || -z "$hwsectorsize" ]]; then
echo "Error reading data for $device" >&2
continue
fi
# Scale everything to bytes since we'll use that for debugfs.
start=$((start * hwsectorsize))
partitionsize=$((partitionsize * hwsectorsize))
# If not using byte flag, scale the address, too.
if [[ -z "$BYTESFLAG" ]]; then
byteaddress=$((lba * hwsectorsize))
else
byteaddress=$lba
fi
if [[ $byteaddress -lt $start ||
$byteaddress -ge $((start+partitionsize)) ]]; then
#echo "Address $byteaddress is not within $partition"
continue # Not in this partition
fi
if ! e2blocksize=$(tune2fs -l $device 2>/dev/null |
grep '^Block size' | egrep -o '[0-9]+'); then
echo "Skipping $device, not an Ext2/3/4 partition"
continue
fi
# Scale address by filesystem blocksize to find filesystem block number
e2blockaddress=$(( (byteaddress - start) / e2blocksize))
Sector=${BYTESFLAG:-Sector}
echo "Disk $Sector $lba is at filesystem block $e2blockaddress in $device"
inode=$(debugfs -R "icheck $e2blockaddress" $device 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" $device 2>/dev/null
else
echo "$Sector is not in use."
fi
done
答案3
该脚本可以进一步完善为以下一行:
sudo tune2fs -l /dev/<disk> \
| grep '^Block size:' \
| sed 's/.* //' \
| xargs expr '(' <LBA> '*' 512 - <partition offset in bytes> ')' / \
| sudo xargs -I{} debugfs -R 'icheck {}' /dev/<partition>
sudo debugfs -R 'ncheck <inode from prev commend>' /dev/<partition>
但是如果使用 LVM,您可能还需要做一些额外的映射。