ext4 recovery detail - hanyong/note GitHub Wiki
ext4 文件系统恢复详细记录
winpe 下操作恢复一个 ghost 文件到一个 windows 分区时, 网上的垃圾一键 ghost 软件逻辑不严谨, 误把 ghost 恢复到一个 linux 下的 lvm 分区。 我的 500G ext4 格式数据卷被恢复成 ntfs 分区, 并重写覆盖了约 10G 的空间。 这可是我从大学攒到现在的几乎所有资料, 赶紧研究下有没有办法恢复。
google 到一篇帖子, 按照其指引尝试操作: http://askubuntu.com/questions/533496/accidentally-formatted-ext4-partition
编辑 /etc/fstab
取消挂载 repo, 避免对 repo 卷做任何操作.
新建一个丢失分区两倍大小 + 100GiB 的物理卷 pv2, pv2 上新建一个相同大小逻辑卷 repo2 备用.
sudo vgextend vg /dev/sda8
sudo lvcreate -L 500g -n repo2 vg /dev/sda8
丢失数据卷创建快照, 尝试在快照上操作恢复数据, 避免对原 repo 卷做任何操作.
sudo lvcreate -s vg/repo -n repo0s0 -L 100g
testdisk
首先尝试使用 testdisk 恢复。
安装 testdisk:
sudo aptitude install testdisk
简单看了一下 man testdisk
, 支持指定设备参数, 尝试对快照进行恢复.
sudo testdisk /dev/vg/repo0s0
显示设备信息, 确认处理:
TestDisk 7.0, Data Recovery Utility, April 2015
Christophe GRENIER <[email protected]>
http://www.cgsecurity.org
TestDisk is free software, and
comes with ABSOLUTELY NO WARRANTY.
Select a media (use Arrow keys, then press Enter):
>Disk /dev/vg/repo0s0 - 536 GB / 500 GiB
>[Proceed ] [ Quit ]
Note: Disk capacity must be correctly detected for a successful recovery.
If a disk listed above has incorrect size, check HD jumper settings, BIOS
detection, and install the latest OS patches and disk drivers.
选择分区表类型, 自动选择了 None
。数据卷已经是一个分区, 没有分区表, 确认选择 None
:
TestDisk 7.0, Data Recovery Utility, April 2015
Christophe GRENIER <[email protected]>
http://www.cgsecurity.org
Disk /dev/vg/repo0s0 - 536 GB / 500 GiB
Please select the partition table type, press Enter when done.
[Intel ] Intel/PC partition
[EFI GPT] EFI GPT partition map (Mac i386, some x86_64...)
[Humax ] Humax partition table
[Mac ] Apple partition map
>[None ] Non partitioned media
[Sun ] Sun Solaris partition
[XBox ] XBox partition
[Return ] Return to disk selection
Hint: None partition table type has been detected.
Note: Do NOT select 'None' for media with only a single partition. It's very
rare for a disk to be 'Non-partitioned'.
选择操作, 自动选择了 Advanced
操作文件系统。已确定的分区, 确认选择 Advanced
:
TestDisk 7.0, Data Recovery Utility, April 2015
Christophe GRENIER <[email protected]>
http://www.cgsecurity.org
Disk /dev/vg/repo0s0 - 536 GB / 500 GiB
1048576000 sectors - sector size=512
[ Analyse ] Analyse current partition structure and search for lost partitions
>[ Advanced ] Filesystem Utils
[ Geometry ] Change disk geometry
[ Options ] Modify options
[ Quit ] Return to disk selection
Note: Correct disk geometry is required for a successful recovery. 'Analyse'
process may give some warnings if it thinks the logical geometry is mismatched.
文件系统操作。由于已经被 ghost 格式化成 NTFS, 自动识别为 NTFS 文件系统:
TestDisk 7.0, Data Recovery Utility, April 2015
Christophe GRENIER <[email protected]>
http://www.cgsecurity.org
Disk /dev/vg/repo0s0 - 536 GB / 500 GiB - 1048576000 sectors
Partition Start End Size in sectors
> P NTFS 0 1048575999 1048576000
[ Type ] >[ Boot ] [ List ] [Undelete] [Image Creation] [ Quit ]
Boot sector recovery
我们要恢复 ext4 分区数据, 操作 Type
修改为 ext4
:
TestDisk 7.0, Data Recovery Utility, April 2015
Christophe GRENIER <[email protected]>
http://www.cgsecurity.org
P NTFS 0 1048575999 1048576000
Please choose the partition type, press Enter when done.
Unknown HFSX NTFS
BeFS HPFS OpenBSD
btrfs ISO OS2 Multiboot
CramFS JFS ReiserFS 3.5
exFAT Linux SWAP ReiserFS 3.6
ext2 Linux SWAP 2 ReiserFS 3.x
ext3 Linux SWAP ReiserFS 4
>ext4 Linux SWAP 2 Sun
FAT12 Linux SWAP 2 SysV 4
FAT16 Linux LUKS UFS
FAT32 Linux LVM UFS 2
FreeBSD Linux LVM2 UFS - Little Endian
GFS2 Linux md 0.9 RAID UFS 2 - Little Endian
HFS Linux md 1.x RAID VMFS
HFS+ Netware WBFS
Next
[ Proceed ]
切换到 ext4 操作选项, 自动选择定位 superblock:
TestDisk 7.0, Data Recovery Utility, April 2015
Christophe GRENIER <[email protected]>
http://www.cgsecurity.org
Disk /dev/vg/repo0s0 - 536 GB / 500 GiB - 1048576000 sectors
Partition Start End Size in sectors
> P ext4 0 1048575999 1048576000
[ Type ] >[Superblock] [ List ] [Image Creation] [ Quit ]
Locate ext2/ext3/ext4 backup superblock
确认执行定位 superblock 操作, 显示结果如下:
TestDisk 7.0, Data Recovery Utility, April 2015
Christophe GRENIER <[email protected]>
http://www.cgsecurity.org
Disk /dev/vg/repo0s0 - 536 GB / 500 GiB - 1048576000 sectors
Partition Start End Size in sectors
ext4 0 1048575999 1048576000
superblock 819200, blocksize=4096 []
superblock 884736, blocksize=4096 []
superblock 1605632, blocksize=4096 []
superblock 2654208, blocksize=4096 []
superblock 4096000, blocksize=4096 []
superblock 7962624, blocksize=4096 []
superblock 11239424, blocksize=4096 []
superblock 20480000, blocksize=4096 []
superblock 23887872, blocksize=4096 []
superblock 71663616, blocksize=4096 []
To repair the filesystem using alternate superblock, run
fsck.ext4 -p -b superblock -B blocksize device
>[ Quit ]
Return to Advanced menu
阅读理解提示信息, 应该是要使用定位到的 superblock 和 blocksize 设置参数调用 fsck.ext4
执行恢复。
查了一下 man fsck.ext4
, 确认支持这俩参数。
根据定位的 superblock
应执行 fsck.ext4
如下:
sudo fsck.ext4 -b 819200 -B 4096 /dev/vg/repo0s0
sudo fsck.ext4 -b 884736 -B 4096 /dev/vg/repo0s0
sudo fsck.ext4 -b 1605632 -B 4096 /dev/vg/repo0s0
sudo fsck.ext4 -b 2654208 -B 4096 /dev/vg/repo0s0
sudo fsck.ext4 -b 4096000 -B 4096 /dev/vg/repo0s0
sudo fsck.ext4 -b 7962624 -B 4096 /dev/vg/repo0s0
sudo fsck.ext4 -b 11239424 -B 4096 /dev/vg/repo0s0
sudo fsck.ext4 -b 20480000 -B 4096 /dev/vg/repo0s0
sudo fsck.ext4 -b 23887872 -B 4096 /dev/vg/repo0s0
sudo fsck.ext4 -b 71663616 -B 4096 /dev/vg/repo0s0
尝试执行恢复第一个 superblock:
$ sudo fsck.ext4 -b 819200 -B 4096 /dev/vg/repo0s0
e2fsck 1.42.13 (17-May-2015)
/dev/vg/repo0s0 is in use.
e2fsck: 无法继续, 中止.
先退出 testdisk, 再尝试执行:
$ sudo fsck.ext4 -b 819200 -B 4096 /dev/vg/repo0s0
e2fsck 1.42.13 (17-May-2015)
超级块包含无效的 ext3 日志(inode 8).
清除<y>? 是
*** ext3 journal has been deleted - filesystem is now ext2 only ***
Resize inode not valid. 重建<y>? 是
第一步: 检查inode,块,和大小
The bad 块 inode looks 无效的. 清除<y>? 是
根inode不是一个目录. 清除<y>? 是
Quota inode is not in use, but contains data. 清除<y>? 是
Quota inode is not in use, but contains data. 清除<y>? 是
保留的inode 6 (<未删除的目录 inode>) 的模式无效. 清除<y>? 是
Inode 6, i_size is 562949983955569, 应为 0. 处理<y>? 是
日志 inode is not in use, but contains data. 清除<y>? 是
保留的inode 9 (<保留的 inode 9>) 的模式无效. 清除<y>? 是
... ...
提示存在各种异常情况, 应该是数据已经被破坏。
重新重建快照:
sudo lvremove -f vg/repo0s0
sudo lvcreate -s vg/repo -n repo0s0 -L 100g
看了下 man fsck.ext4
, 可添加 -p
参数减少交互, 或者 -y
/-n
全部回答.
尝试添加 -p
执行, 报错说只能手动恢复(?):
$ sudo fsck.ext4 -b 819200 -B 4096 /dev/vg/repo0s0 -p
/dev/vg/repo0s0: 超级块包含无效的 ext3 日志(inode 8).
已清除.
*** ext3 journal has been deleted - filesystem is now ext2 only ***
/dev/vg/repo0s0: Resize inode not valid.
/dev/vg/repo0s0: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.
(i.e., without -a or -p options)
分区前面的数据被破坏, 后面的数据应该没有被重写, 能否正常恢复(?)。 再次重建快照, 尝试先恢复最后一个 superblock, 还是一样报错。
看帖子上的说明, 应该是 root inode 被破坏, 使用 testdisk 不行, 尝试手动定位 superblock。
手动定位 superblock
重建快照尝试手动定位 superblock。
按照帖子指引找到这篇说明: Repair a broken Ext4 Superblock in Ubuntu
首先执行检查, 妥妥的说 "超级块无效":
sudo fsck.ext4 -v /dev/vg/repo0s0
然后文档说执行 mke2fs -n
, man mkfs.ext4
看了一下,
-n
是假装创建文件系统, 然后输出 backup superblocks
的位置,
这样我们就知道当初创建文件系统时 backup superblocks
在哪里了。
由于我是使用 mkfs.ext4
创建文件系统的, 执行:
sudo mkfs.ext4 -n /dev/vg/repo0s0
输出:
mke2fs 1.42.13 (17-May-2015)
/dev/vg/repo0s0 contains a ntfs file system labelled 'Windows10'
无论如何也要继续? (y,n) y
Creating filesystem with 131072000 4k blocks and 32768000 inodes
Filesystem UUID: b8acf54e-cd40-4129-964a-68426fe599f0
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
102400000
对比了一下, 找到的 superblock 比 testdisk 多, 有部分是重叠的。 按照文档提示, 使用一个 superblock 执行恢复, 如果不行, 再尝试另外一个。 是否每个 superblock 都是完整的, 不用全部恢复(?)。
所有 superblock 试了一遍, 全部报错.
ext4 superblock 恢复测试
猜想是不是要先格式化成 ext4 格式才能恢复 superblock, 但是担心格式化 ext4 会破坏 superblock。 先研究下如何将 superblock 备份到文件。
了解了一下 ext4 将整个分区划为 n 个 block, 而 superblock 是其中一些元信息 block。 superblock 下标是从 0 开始还是从 1 开始呢, 创建了一个 test 卷进行测试。 test ext4 格式保存数据后创建快照 test0, 然后格式化为 ntfs, 创建快照 test0s0。
分别假设 superblock 下标为 0 或 1, dd 拷贝 superblock 出来计算 md5:
sudo bash -ec 'i0=1 ; for i in 32768 98304 163840 229376 294912 819200 884736 1605632 2654208 ; do dd if=/dev/vg/test0s0 bs=4096 skip=$(( i - $i0 )) count=1 of=$i0-$i ; done'
看到设下标为 0 时 md5 各不相同, 而下标为 1 时若干 block md5 相同 (sort 的 key 内字符默认从空白开始计算) :
$ sudo md5sum 1-* | sort -k 2.5n
620f0b67a91f7f74151bc5be745b7110 1-32768
a0b877693086b9411c7c93b418ec5c23 1-98304
6a8622cf8436e44ca7ca9cdb79337635 1-163840
30dbe08fecfdd5565e1faef666480d62 1-229376
147b2a1962092a32e04db42a2269a688 1-294912
620f0b67a91f7f74151bc5be745b7110 1-819200
620f0b67a91f7f74151bc5be745b7110 1-884736
620f0b67a91f7f74151bc5be745b7110 1-1605632
620f0b67a91f7f74151bc5be745b7110 1-2654208
再用 od -t x1
检查下文件内容:
sudo bash -xc 'for i in 0-* ; do od -t x1 $i | head ; done'
发现下标为 0 的都有内容,
而下标为 1 相同 md5 的 block 实际内容都为 0
, 包括第一个主 block 32768。
最后一个不为 0 的 block 294912 在 294912.0 * 4 / 1024 = 1152MiB
的位置,
分区内测试数据 1.5g 多, 应该正好是分区内的测试数据。
故下标应是从 0 开始。
而头疼的是各个 block 的内容各不相同。
对比 ext4 和 ntfs block 数据差异:
sudo bash -ec 'i0=0 ; for i in 32768 98304 163840 229376 294912 819200 884736 1605632 2654208 4096000 ; do dd if=/dev/vg/test0 bs=4096 skip=$(( i - $i0 )) count=1 of=a-$i ; done'
sudo bash -ec 'i0=0 ; for i in 32768 98304 163840 229376 294912 819200 884736 1605632 2654208 4096000 ; do dd if=/dev/vg/test0s0 bs=4096 skip=$(( i - $i0 )) count=1 of=b-$i ; done'
sudo md5sum a-* b-* | sort -k 2.5n -k 2.3,2.3
发现如果只是简单的重新格式化而没有重写数据, 那么各 block 的数据还是一样的, 没有被覆盖。
ace0dfaed93aad5d3ab63b131627ed03 a-32768
ace0dfaed93aad5d3ab63b131627ed03 b-32768
b49e0d5a6012cc9f1fd6a1338e7e5b2b a-98304
b49e0d5a6012cc9f1fd6a1338e7e5b2b b-98304
7081e4935c35304fa421742806555d7e a-163840
7081e4935c35304fa421742806555d7e b-163840
8776e844b77dc2af5eb959fcab9f32b7 a-229376
8776e844b77dc2af5eb959fcab9f32b7 b-229376
3859e3a519c0dc464c3969eb775ed384 a-294912
3859e3a519c0dc464c3969eb775ed384 b-294912
d5cb215dedb5f67832cc4d05ee53049b a-819200
d5cb215dedb5f67832cc4d05ee53049b b-819200
b87deb764927aacb0fce3a527b7aebc3 a-884736
b87deb764927aacb0fce3a527b7aebc3 b-884736
a4e117986fc375bc25fe3e61d1eac682 a-1605632
a4e117986fc375bc25fe3e61d1eac682 b-1605632
92d9362366e7d7b0dfeccda1aca54378 a-2654208
92d9362366e7d7b0dfeccda1aca54378 b-2654208
a18a725e980dfce052e74165dfa0e48c a-4096000
a18a725e980dfce052e74165dfa0e48c b-4096000
重新 ext4 格式化后果然所有 block 内容都变化, superblock 数据被破坏。
恢复 backup superblock:
sudo bash -ec 'i0=0 ; for i in 32768 98304 163840 229376 294912 819200 884736 1605632 2654208 4096000 ; do dd of=/dev/vg/test0s0 bs=4096 seek=$(( i - $i0 )) count=1 if=a-$i ; done'
再用 fsck.ext4
检查:
$ sudo fsck.ext4 -v /dev/vg/test0s0
e2fsck 1.42.13 (17-May-2015)
/dev/vg/test0s0 primary superblock features different from backup, 强制检查.
第一步: 检查inode,块,和大小
第二步: 检查目录结构
第3步: 检查目录连接性
Pass 4: Checking reference counts
第5步: 检查簇概要信息
11 inodes used (0.00%, out of 1310720)
0 non-contiguous files (0.0%)
0 non-contiguous directories (0.0%)
# of inodes with ind/dind/tind blocks: 0/0/0
Extent depth histogram: 3
126289 blocks used (2.41%, out of 5242880)
0 bad blocks
1 large file
0 regular files
2 directories
0 character device files
0 block device files
0 fifos
0 links
0 symbolic links (0 fast symbolic links)
0 sockets
------------
2 files
然后挂载卷, 发现并没有数据:
$ sudo mount /dev/vg/test0s0 /mnt/
$ ls /mnt/
lost+found
$ df -h /mnt
文件系统 容量 已用 可用 已用% 挂载点
/dev/mapper/vg-test0s0 20G 44M 19G 1% /mnt
$ sudo umount /mnt
可见 fsck.ext4
应该是认为 backup superblock 有问题, 然后进行订正。
要使用 backup superblock, 应该指定 -b
参数。
重新格式化 test0s0, 恢复最后一个 superblock, 添加 -b
检查, 报错, 大概是说不能自动恢复, 跟之前的检查报错类似。
sudo mkfs.ext4 /dev/vg/test0s0
sudo bash -ec 'i0=0 ; for i in 4096000 ; do dd of=/dev/vg/test0s0 bs=4096 seek=$(( i - $i0 )) count=1 if=a-$i ; done'
sudo fsck.ext4 -v /dev/vg/test0s0 -b 4096000 -p
再次重新格式化, 恢复最后一个 superblock, 使用 -y
检查。
输出若干问题, 等了一会儿后最终检查成功。
$ sudo fsck.ext4 -v /dev/vg/test0s0 -b 4096000 -y
... ...
Free inodes count wrong (1441781, counted=1310709).
处理? 是
/dev/vg/test0s0: ***** 文件系统已修改 *****
11 inodes used (0.00%, out of 1310720)
0 non-contiguous files (0.0%)
0 non-contiguous directories (0.0%)
# of inodes with ind/dind/tind blocks: 0/0/0
Extent depth histogram: 3
126289 blocks used (2.41%, out of 5242880)
0 bad blocks
1 large file
0 regular files
2 directories
0 character device files
0 block device files
0 fifos
0 links
0 symbolic links (0 fast symbolic links)
0 sockets
------------
2 files
hanyong@han2015:~/tmp$ sudo fsck.ext4 -v /dev/vg/test0s0
e2fsck 1.42.13 (17-May-2015)
/dev/vg/test0s0: clean, 11/1310720 files, 126289/5242880 blocks
但挂载数据卷后发现依然没有数据。
再次格式化恢复所有 superblock, 使用 -b 32768 -p
检查, 发现检查直接通过, 但是文件并没有恢复。
$ sudo fsck.ext4 -v /dev/vg/test0s0 -b 32768 -p
/dev/vg/test0s0 was not cleanly unmounted, 强制检查.
11 inodes used (0.00%, out of 1310720)
0 non-contiguous files (0.0%)
0 non-contiguous directories (0.0%)
# of inodes with ind/dind/tind blocks: 0/0/0
Extent depth histogram: 3
126289 blocks used (2.41%, out of 5242880)
0 bad blocks
1 large file
0 regular files
2 directories
0 character device files
0 block device files
0 fifos
0 links
0 symbolic links (0 fast symbolic links)
0 sockets
------------
2 files
重新恢复, 去掉 -p
检查, 有一个问题回答 y
, 这次提示 "文件系统已修改"。
$ sudo fsck.ext4 -v /dev/vg/test0s0 -b 32768
e2fsck 1.42.13 (17-May-2015)
/dev/vg/test0s0 was not cleanly unmounted, 强制检查.
第一步: 检查inode,块,和大小
第二步: 检查目录结构
第3步: 检查目录连接性
Pass 4: Checking reference counts
第5步: 检查簇概要信息
Inode位图差异: -(655361--786432)
处理<y>? 是
/dev/vg/test0s0: ***** 文件系统已修改 *****
11 inodes used (0.00%, out of 1310720)
0 non-contiguous files (0.0%)
0 non-contiguous directories (0.0%)
# of inodes with ind/dind/tind blocks: 0/0/0
Extent depth histogram: 3
126289 blocks used (2.41%, out of 5242880)
0 bad blocks
1 large file
0 regular files
2 directories
0 character device files
0 block device files
0 fifos
0 links
0 symbolic links (0 fast symbolic links)
0 sockets
------------
2 files
mount 看了一下, 文件还是没有恢复。
推测结论:
(1) superblock 保留越多越容易恢复。
(2) 使用 -y
才会尽可能采纳 backup superblock 的信息执行修改。
另外发现, 随着反复格式化和恢复, 快照 test0s0 占据的空间逐渐增加, 不知为何, 暂忽略:
hanyong@han2015:~/tmp$ sudo lvs -a
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
test vg owi-a-s--- 20.00g
test0 vg swi-a-s--- 2.00g test 3.19
test0s0 vg swi-a-s--- 2.00g test 22.20
重建快照, 尝试直接使用 -y
恢复, 输出一堆问题, 经过一段时间的等待后修复完成:
$ sudo lvremove -f vg/test0s0 && sudo lvcreate -s vg/test -n test0s0 -L 2g
Logical volume "test0s0" successfully removed
Logical volume "test0s0" created.
$ sudo fsck.ext4 /dev/vg/test0s0 -b 32768 -y
... ...
/dev/vg/test0s0: ***** 文件系统已修改 *****
/dev/vg/test0s0: 13/1310720 files (7.7% non-contiguous), 547718/5242880 blocks
检查发现卷已经修复成 ext4 文件系统, mount 发现文件也恢复了:
$ sudo blkid /dev/vg/test0s0
/dev/vg/test0s0: UUID="a58307c0-79bf-47ff-8457-5a8989695b91" TYPE="ext4"
$ sudo mount /dev/vg/test0s0 /mnt/
$ ls -Alh /mnt/
总用量 1.7G
-rwxr-xr-x 1 root root 1.6G 8月 7 15:20 linuxmint-18-cinnamon-64bit.iso
drwx------ 2 root root 16K 8月 7 15:18 lost+found
-rwxr-xr-x 1 root root 27M 8月 7 15:20 smplayer-portable-16.4.0.0-x64.7z
$ sudo md5sum /mnt/linuxmint-18-cinnamon-64bit.iso /d/software/linuxmint-18-cinnamon-64bit.iso
8e9dba7e5ae538e06a13c7135bf457f2 /mnt/linuxmint-18-cinnamon-64bit.iso
8e9dba7e5ae538e06a13c7135bf457f2 /d/software/linuxmint-18-cinnamon-64bit.iso
可见, 如果只是简单的重新格式化, 数据没有被覆盖, 数据是可以恢复的。
copy 一个 500 多 MiB 的文件到 ntfs 分区, 比较发现 superblock 还是没有被覆盖, 再次尝试恢复, 还是恢复成功, 数据 md5 也对, 猜测 ntfs 文件读写错开了 ext4 的数据区, 数据没有被覆盖(?)。
把 test 卷挂进虚拟机, 再用 ghost 恢复写入一个 700 多 MiB 的镜像,
再检查 superblock, 这回发现前 3 个被破坏了, 第 4 个开始是好的。
第 4 个是从 229376.0 * 4 / 1024 = 896MiB
开始的位置。
$ sudo md5sum a-* b-* | sort -k 2.5n -k 2.3,2.3
ace0dfaed93aad5d3ab63b131627ed03 a-32768
7964e860bad427d7ccf3fa9548ae4665 b-32768
b49e0d5a6012cc9f1fd6a1338e7e5b2b a-98304
168a5dca0c1859f2f8d9b6131b61ba2d b-98304
7081e4935c35304fa421742806555d7e a-163840
aef55fe67f3f75bf2e7cbfd77c56424a b-163840
8776e844b77dc2af5eb959fcab9f32b7 a-229376
8776e844b77dc2af5eb959fcab9f32b7 b-229376
被破坏的 superblock 应该无法恢复了, 果然尝试从前 3 个 superblock 恢复都直接报错:
$ sudo fsck.ext4 /dev/vg/test0s0 -b 32768 -y
e2fsck 1.42.13 (17-May-2015)
fsck.ext4: Bad magic number in super-block 当尝试打开 /dev/vg/test0s0 时
The 超级块 could not be read or does not describe a valid ext2/ext3/ext4
文件系统. If the 设备 is valid and it really contains an ext2/ext3/ext4
文件系统 (and not swap or ufs or something else), then the 超级块
is corrupt, and you might try running e2fsck with an alternate 超级块:
e2fsck -b 8193 <设备>
or
e2fsck -b 32768 <设备>
尝试从第 4 个 superblock 恢复, 恢复过程中磁盘 IO 较高, 整个系统很卡, 经过较长时间的等待恢复完成。 根据之前的经验, 输出日志刷太多, 直接重定向到文件。
$ sudo fsck.ext4 /dev/vg/test0s0 -b 229376 -y > a.log
e2fsck 1.42.13 (17-May-2015)
e2fsck: 已中止
$ head a.log
超级块包含无效的 ext3 日志(inode 8).
清除? 是
*** ext3 journal has been deleted - filesystem is now ext2 only ***
Resize inode not valid. 重建? 是
fsck.ext4: Illegal doubly indirect block found while reading bad blocks inode
This doesn't bode well, but we'll try to go on...
第一步: 检查inode,块,和大小
$ tail a.log
Error storing 目录 块 information (inode=22818, 块=0, num=76668584): Memory allocation failed
/dev/vg/test0s0: ***** 文件系统已修改 *****
重建日志? 是
Creating journal (32768 blocks): 完成.
*** journal has been re-created - filesystem is now ext3 again ***
/dev/vg/test0s0: ***** 文件系统已修改 *****
看控制台输出说 "e2fsck: 已中止", 看日志又是已恢复成 ext3。 blkid 和 parted 看似乎文件系统还是 ntfs。
$ sudo blkid /dev/vg/test0s0
/dev/vg/test0s0: LABEL="test" UUID="BA98DDC598DD7FF5" TYPE="ntfs" PTUUID="00000001" PTTYPE="dos"
hanyong@han2015:~/tmp$ sudo parted /dev/vg/test0s0
GNU Parted 3.2
使用 /dev/dm-16
欢迎使用 GNU Parted! 输入 'help'可获得命令列表.
(parted) print free
Model: Linux 设备映射器 (snapshot) (dm)
磁盘 /dev/dm-16: 21.5GB
Sector size (logical/physical): 512B/4096B
分区表:loop
Disk Flags:
数字 开始: End 大小 文件系统 标志
1 0.00B 21.5GB 21.5GB ntfs
(parted) unit s
(parted) print free
Model: Linux 设备映射器 (snapshot) (dm)
磁盘 /dev/dm-16: 41943040s
Sector size (logical/physical): 512B/4096B
分区表:loop
Disk Flags:
数字 开始: End 大小 文件系统 标志
1 0s 41943039s 41943040s ntfs
(parted) quit
检查前 3 个 superblock 也更新了:
hanyong@han2015:~/tmp$ sudo md5sum a-* b-* | sort -k 2.5n -k 2.3,2.3
ace0dfaed93aad5d3ab63b131627ed03 a-32768
eb4542a7c70c809ea5379e1e1fd8884e b-32768
b49e0d5a6012cc9f1fd6a1338e7e5b2b a-98304
168a5dca0c1859f2f8d9b6131b61ba2d b-98304
7081e4935c35304fa421742806555d7e a-163840
aef55fe67f3f75bf2e7cbfd77c56424a b-163840
... ...
92d9362366e7d7b0dfeccda1aca54378 a-2654208
620f0b67a91f7f74151bc5be745b7110 b-2654208
... ...
注意前 3 个 superblock 因为是破坏后重建, 所以跟之前不一样, 并不奇怪。 其他的是倒数第 2 个 superblock 也变的不一样了, 恢复前是一样的。
mount 卷发现自动识别为 fuseblk
, 与指定 -t ntfs
相同, 数据没有恢复:
$ sudo mount /dev/vg/test0s0 /mnt
$ mount
... ...
/dev/mapper/vg-test0s0 on /mnt type fuseblk (rw,relatime,user_id=0,group_id=0,allow_other,blksize=4096)
$ ls /mnt/
System Volume Information
$ sudo umount /mnt
指定 -t ext3
挂载则报错, 指定 ext2/3/4
均报错:
$ sudo mount -t ext3 /dev/vg/test0s0 /mnt
mount: wrong fs type, bad option, bad superblock on /dev/mapper/vg-test0s0,
missing codepage or helper program, or other error
In some cases useful info is found in syslog - try
dmesg | tail or so.
再次重试从第 4 个 superblock 起恢复, 同样卡了会儿, 等了会儿。日志信息有点不一样。
$ sudo fsck.ext4 /dev/vg/test0s0 -b 229376 -y > a.log
e2fsck 1.42.13 (17-May-2015)
e2fsck: 已中止
$ head a.log
Backing up 日志 inode 块 information.
/dev/vg/test0s0 was not cleanly unmounted, 强制检查.
Resize inode not valid. 重建? 是
第一步: 检查inode,块,和大小
根inode不是一个目录. 清除? 是
Special (设备/socket/fifo) inode 66 has non-zero size. 处理? 是
$ tail a.log
Inode 32663 中包含非法块. 清除? 是
非法的 块 #2 (18223814) in inode 32663. 已清除.
非法的 块 #4 (18223797) in inode 32663. 已清除.
非法的 块 #89132278 (1346801131) in inode 32663. 已清除.
Error storing 目录 块 information (inode=32663, 块=0, num=79846000): Memory allocation failed
/dev/vg/test0s0: ***** 文件系统已修改 *****
/dev/vg/test0s0: ***** 文件系统已修改 *****
检查第一个 superblock 32768 再次变化, 其他没有变:
$ sudo md5sum a-* b-* | sort -k 2.5n -k 2.3,2.3
ace0dfaed93aad5d3ab63b131627ed03 a-32768
08b91135db037987f1a29c7e8e1fc1fb b-32768
再用 blkid 和 parted 看还是 ntfs 格式, 但尝试 mount 挂载时用 ntfs 或 ext2/3/4 都报错。
再测试发现即使对一个正常的分区, 指定 -b
从一个靠后的 superblock 检查, 也会每次检查都有问题。
随后即使再指定第一个 backup superblock -b 32768
也会反复检查都有问题。
如果不指定 -b
, 第一次检查修复后, 后面再检查就没问题了。
检查 ok 后 mount 挂载看数据是 ok 的。
尝试不加 -b
检查 test0s0, 报 "Bad magic number in super-block", 超级块无效,
尝试指定第 1/2/3 个 backup superblock, 依然报错。
重试从第 4 个 superblock 恢复, 这次不重定向日志, 注意到最后有一个报错:
$ sudo fsck.ext4 -v /dev/vg/test0s0 -b 229376 -y
... ...
非法的 块 #89132397 (3968068747) in inode 39480. 已清除.
Error storing 目录 块 information (inode=39480, 块=0, num=79842416): Memory allocation failed
/dev/vg/test0s0: ***** 文件系统已修改 *****
e2fsck: 已中止
/dev/vg/test0s0: ***** 文件系统已修改 *****
"Memory allocation failed", 这个报错之前也有, 但是没注意到。
应该是这个错误导致 fsck.ext4
未执行完就终止了。
为什么会内存分配失败, 是内存不够吗, 重试一次恢复并 top 看了一下,
fsck.ext4
果然会占用大量内存, 这时系统也几乎卡死:
top - 21:03:19 up 8:26, 6 users, load average: 7.19, 4.61, 2.71
Tasks: 320 total, 1 running, 318 sleeping, 0 stopped, 1 zombie
%Cpu(s): 1.5 us, 1.7 sy, 0.0 ni, 84.1 id, 12.7 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16379800 total, 199912 free, 16016944 used, 162944 buff/cache
KiB Swap: 16777212 total, 7278440 free, 9498772 used. 80712 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
19567 root 20 0 23.724g 0.014t 2416 D 5.6 94.1 0:32.86 fsck.ext4
16491 hanyong 20 0 1504920 91816 19660 D 1.3 0.6 5:02.74 chromium-browse
5108 hanyong 20 0 1676952 41972 4668 D 0.0 0.3 5:41.30 chromium-browse
10309 hanyong 20 0 1285248 32756 4668 D 0.3 0.2 1:28.47 chromium-browse
可看到 swap 也用了大半, 因为系统几乎卡死, 不知道 swap 有没有用完。
不知道系统卡是不是因为 swap 用多了。
swapoff -a
禁用掉 swap 再次尝试恢复, 这次果然系统没有卡了, 磁盘 IO 也不高了,
并且 fsck.ext4 竟然也执行成功了。
$ sudo fsck.ext4 -v /dev/vg/test0s0 -b 229376 -y
... ...
/dev/vg/test0s0: ***** 文件系统已修改 *****
3809 inodes used (0.29%, out of 1310720)
95 non-contiguous files (2.5%)
209 non-contiguous directories (5.5%)
# of inodes with ind/dind/tind blocks: 31601/24021/31515
Extent depth histogram: 1/1
127120 blocks used (2.42%, out of 5242880)
0 bad blocks
2 large files
120 regular files
219 directories
831 character device files
690 block device files
1232 fifos
4294966859 links
0 symbolic links (0 fast symbolic links)
731 sockets
------------
4294967078 files
看来 fsck.ext4
并不是 "内存杀手", 都是 swap 害了系统。
随后看 blkid 未识别到文件系统, mount -t ext4
挂载磁盘成功, 但查看数据都丢失了。
$ sudo blkid /dev/vg/test0s0
$ sudo mount -t ext4 /dev/vg/test0s0 /mnt/
hanyong@han2015:~/tmp$ ls /mnt
lost+found
hanyong@han2015:~/tmp$ df -h
文件系统 容量 已用 可用 已用% 挂载点
... ...
/dev/mapper/vg-test0s0 20G 48M 19G 1% /mnt
重建快照重试恢复, 这次系统没有卡死的症状, 但 fsck.ext4
再次报内存分配失败终止了。
fsck.ext4
还是需要大量内存(?)。
google 了一下 e2fsck 确实需要很多内存:
http://serverfault.com/questions/9218/running-out-of-memory-running-fsck-on-large-filesystems 。
可以配置 /etc/e2fsck.conf
使用磁盘文件, 但这样执行会非常慢。没办法只能配置。
看了下 man fsck.ext4
和 man e2fsck.conf
,
scratch_files
包含 directory
, dirinfo
, icount
3 个配置。
后两个是元信息, 应该不用落盘, 尝试将这两个设置为 false
。
第 2 个物理卷创建一个 500GiB 的卷, 挂载到 /fsck
,
/etc/e2fsck.conf
添加如下配置:
[scratch_files]
directory=/fsck
dirinfo=false
icount=false
重建 test0s0 快照再重试恢复, e2fsck 依然内存不足后终止, 注意到:
Error storing 目录 块 information (inode=11456, 块=0, num=22414330): Memory allocation failed
修改后两个设置为 true, 或者删掉这俩设置, 还是一样占用很多内存, 然后内存不足终止。
google "e2fsck scratch_files not work", 找到一篇帖子: e2fsck - out of memory despite [scratch_files] set in /etc/e2fsck.conf . 提到 e2fsck 可能确实需要很多内存, 并且 e2fsck 会区分对待 swap, 给 swap 不要 (不会充分利用 swap), 只要真实内存。 一个技巧就是在宿主机上分配很大的 swap, 然后创建一个 vm 占用 swap 空间作为内存, vm 内运行的 e2fsck 就会以为系统有很大的真实内存了。 不过这样将会变得非常非常慢, 但至少能执行了。
这时在想另一个问题, ext4 的文件系统是不是应该时分散在分区各处的, 而不是集中在分区头部(?)。 创建一个 10G 的卷写入一些文件测试, 发现即使将头部 1G 多的内容抹零, 只丢失了顶层目录数, 二级目录树及部分文件仍然能够恢复, 并且恢复很快, 也不会占很多内存。 猜想只有破坏的错误数据才会导致恢复困难及占用大量内存, 直接抹零反而恢复简单。 但直接抹零会破坏抹零区域的所有数据, 而错误恢复会尝试恢复未被破坏的数据, 在只夹杂部分坏数据时可能有用。 ghost 恢复应该将分区头部的数据全部破坏了, 并不能恢复有效数据, 可能还会误导 e2fsck (占用大量内存进行碎片分析并可能产生错误结果), 不如将分区被完全破坏的头部抹零, 再用 e2fsck 恢复。
重建快照 test0s0, 将其前 750 多 MiB 抹零, 然后执行恢复:
sudo lvremove -f vg/test0s0 && sudo lvcreate -s vg/test -n test0s0 -L 2g
sudo dd if=/dev/zero of=/dev/vg/test0s0 bs=8M count=$(( 750 / 8 + 1 ))
sudo fsck.ext4 -v /dev/vg/test0s0 -b 229376 -y > a.log
由于发现有点慢, 直接禁用掉了 scratch_files
配置,
fsck 较快完成, 并且内存占用都很少, 可惜的是文件内容并没有恢复成功, 可能是有效索引都被覆盖了(?)。
$ sudo blkid /dev/vg/test0s0
/dev/vg/test0s0: UUID="a58307c0-79bf-47ff-8457-5a8989695b91" TYPE="ext4"
$ sudo mount /dev/vg/test0s0 /mnt
$ df -h /mnt
文件系统 容量 已用 可用 已用% 挂载点
/dev/mapper/vg-test0s0 20G 44M 19G 1% /mnt
$ sudo find /mnt
/mnt
/mnt/lost+found
尝试只抹掉前 128MiB 内容, 恢复时依然只占用少量内存, 文件依然没有恢复成功。
推测 e2fsck 应该只会检查利用 superblock 和前 128MiB 保存的关键元数据。
为什么测试 128MiB ? 这是第一个 backup superblock 32768.0 * 4 / 1024 = 128MiB
的位置。
恢复 repo 数据
回顾 testdisk 找到的最靠前 superblock 是 819200 在 3200MiB 位置。
而查看 repo0s0 被覆盖了 9576MiB 空间:
$ sudo mount /dev/vg/repo0s0 /mnt
hanyong@han2015:~$ df -m /mnt
文件系统 1M-块 已用 可用 已用% 挂载点
/dev/mapper/vg-repo0s0 512000 9576 502425 2% /mnt
相对可靠的 superblock 应该是 2654208 在 10368MiB 位置。
repo0s0 将前 128MiB 抹零, 尝试从最靠前 superblock 819200 恢复。 其中出现大量报错, 经过漫长的等待 (29分钟) 后恢复完成。 恢复期间 top 观察 CPU 占用最多 100%, 内存占用不多。 mount 检查恢复了 327GiB 数据。
$ sudo umount /mnt
$ sudo mount /dev/vg/repo0s0 /mnt
$ df -h /mnt
文件系统 容量 已用 可用 已用% 挂载点
/dev/mapper/vg-repo0s0 493G 327G 141G 70% /mnt
$ ls /mnt
lost+found
恢复的文件在 lost+found
文件夹下, 由于丢失了顶层目录,
看到的都是类似 #29097985
这样的文件名, 但二级目录起文件名恢复 OK。
发现 lost+found
下存在大量各种文件, ls
命令直接狂刷屏, 还有大量报错。
$ sudo su -l
# cd /mnt/lost+found/
# find -maxdepth 2 -name 'Bolt.rmvb'
find: ‘./#4328233’: Permission denied
... ...
./#29097985/Bolt.rmvb
可以通过检查文件夹内容确定原顶层文件夹名字,
如通过查找 Bolt.rmvb
可知 #29097985
是 film
文件夹。
尝试播放该文件, 发现可以播放, 恢复成功。
新建快照 repo0s1 抹零, 从可靠 superblock 2654208 恢复。
sudo lvcreate -s vg/repo -n repo0s1 -L 100g
sudo dd if=/dev/zero of=/dev/vg/repo0s1 bs=8M count=$(( 128 / 8 ))
time sudo fsck.ext4 -v /dev/vg/repo0s1 -b 2654208 -y > a.log
依然有大量报错, 31 分钟后恢复完成, 查看恢复后的分区 repo0s1 似乎与之前 repo0s0 是一样的。 恢复数据大小一样, 顶层文件个数一样, film 等文件夹恢复后的编号都一样。
另外发现恢复数据只占用极少的快照空间:
$ sudo lvs -a
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
... ...
repo vg owi-a-s--- 500.00g
repo0s0 vg swi-aos--- 100.00g repo 2.83
repo0s1 vg swi-aos--- 100.00g repo 1.14
怀疑 lost+found
下的大量垃圾数据是恢复被 ghost 复写的垃圾数据产生,
再建一个快照, 抹零前 9576MiB (df -m 看到的 ntfs 分区占用空间, 应为 ghost 恢复后的头部) 再尝试恢复。
机械磁盘较慢, 抹零花了 3 分钟 (写快照速度下降一倍?)。
抹零后最靠前 (也是较可靠) 的 superblock 是 2654208。
结果 10 分钟后内存不够报错。
$ sudo lvcreate -s vg/repo -n repo0s2 -L 20g
Logical volume "repo0s2" created.
$ time sudo dd if=/dev/zero of=/dev/vg/repo0s2 bs=8M count=$(( 9576 / 8 ))
记录了1197+0 的读入
记录了1197+0 的写出
10041163776 bytes (10 GB, 9.4 GiB) copied, 217.848 s, 46.1 MB/s
real 3m37.860s
user 0m0.004s
sys 0m14.400s
$ time sudo fsck.ext4 -v /dev/vg/repo0s2 -b 2654208 -y > a.log
e2fsck 1.42.13 (17-May-2015)
e2fsck: 已中止
real 10m21.034s
user 0m54.888s
sys 1m29.352s
增加到抹零 10GiB, 或可靠superblock 2654208 之前的 10368MiB, 还是内存不足报错。 看来抹零也不能瞎抹, 要有技巧或者看运气的。
repo0s2 抹零 128MiB 从 testdisk 识别到的最后一个 superblock 71663616 恢复, 依然恢复成功。 而且看数据大小和文件夹内容结构都跟之前恢复的结构一样。 由此可推测 ext4 的索引分散在整个文件系统, 从任意一个有效 superblock 都可以恢复文件系统。
重建 repo0s3 抹零 128MiB 从可靠 superblock 2654208 恢复, 比较两个恢复快照的 superblock:
sudo bash -ec 'i0=0 ; for i in 32768 98304 163840 229376 294912 819200 884736 1605632 2654208 4096000 7962624 11239424 20480000 23887872 71663616 78675968 102400000 ; do dd if=/dev/vg/repo0s3 bs=4096 skip=$(( i - $i0 )) count=1 of=a-$i ; done'
sudo bash -ec 'i0=0 ; for i in 32768 98304 163840 229376 294912 819200 884736 1605632 2654208 4096000 7962624 11239424 20480000 23887872 71663616 78675968 102400000 ; do dd if=/dev/vg/repo0s2 bs=4096 skip=$(( i - $i0 )) count=1 of=b-$i ; done'
sudo md5sum a-* b-* | sort -k 2.5n -k 2.3,2.3
发现虽然不同 superblock 都能恢复文件系统, 恢复之后的 superblock 列表却是不一样的。 为保险起见, 决定使用从最靠前可靠 superblock 恢复的快照 repo0s3 找回数据, 其他快照可以删除。 而从实际恢复数据看 (文件和文件夹结构), 从哪个 superblock 恢复的结果似乎都是一样的。
找回数据
受垃圾数据的影响, 恢复文件里有大量无用的垃圾文件。 repo 卷顶层只有几个重要的文件夹, 除了通过能记住名字的文件进行定位, 还可以尝试从这堆垃圾文件中只把正常文件夹过滤出来。
mkdir ../tmp
# 只保留执行 ls 不报错的文件夹. 还有很多文件夹 ls 不报错但是 mv 失败, 也一同忽略.
find -maxdepth 1 -name '.' -o \( -type d -exec bash -c 'ls {} >/dev/null 2>/dev/null' \; -exec mv {} ../tmp/ \; \)
# 删除空文件夹
find -maxdepth 1 -empty -exec rm -rf {} \;
过滤之后剩下的文件夹已经不多了, 再 ls 过一遍相关文件夹内容, 就能将各个文件夹都识别出来了:
root@han2015:/mnt/tmp# ls
#11272193 #131083 #131087 #131091 #17563649 #17566454 #22937601 #23724033 #24248322 #25165825 #30146561
#131081 #131085 #131089 #131093 #17566420 #22806529 #23068673 #24248321 #24903681 #27525121 #4528911
root@han2015:/mnt/tmp# ls *
... ...