如何恢复 Linux 上删除的文件
2014-04-07 01:51
561 查看
由于现在的linux文件系统大多是etx3的,一不小心删除后是无法恢复的(至少我不知道),而rm是个很危险的操作!鉴于此我写了这俩小脚本,希望GGJJ们指点! 1,用root修改rm的权限: #chmod o-x /bin/rm 2,在用户主目录下创建个"垃圾箱" $mkdir ~/.temp 删除文件脚本:cat erase #!/bin/ksh (($#==0)) && { echo "No paraments!";exit 1; } for i in $* do mv -f $i ~/.temp/$(find $(pwd) -maxdepth 1 -name $i|tr "/" "=") done 复制代码恢复文件脚本:cat unerase #!/bin/ksh (($#==0))&&{ echo "No paraments!";exit 1; } cd ~/.temp list=$(for i in $*;do ls ~/.temp|grep "\<$i\>";done) for j in $list do file=$(echo $j|tr "=" "/") mv $j ${file%/*}/${file##*/} done 复制代码 |
图 1. 文件数据定位过程
对于 ext2 类型的文件系统来说,目录项是使用一个名为 ext2_dir_entry_2 的结构来表示的,该结构定义如下所示:
清单 1. ext2_dir_entry_2 结构定义
struct ext2_dir_entry_2 { __le32 inode; /* 索引节点号 */ __le16 rec_len; /* 目录项的长度 */ __u8 name_len; /* 文件名长度 */ __u8 file_type; /* 文件类型 */ char name[EXT2_NAME_LEN]; /* 文件名 */}; |
图 2. ext2 分区的布局
从图 2 中可以看到,对于 ext2 文件系统来说,磁盘被划分成一个个大小相同的数据块,每个块的大小可以是1024、2048 或 4096 个字节。其中,第一个块称为引导块,一般保留做引导扇区使用,因此 ext2 文件系统一般都是从第二个块开始的。剩余的块被划分为一个个的块组,ext2 文件系统会试图尽量将相同文件的数据块都保存在同一个块组中,并且尽量保证文件在磁盘上的连续性,从而提高文件读写时的性能。至于一个分区中到底有多少个块组,这取决于两个因素:分区大小。
块大小。
最终的计算公式如下:分区中的块组数=分区大小/(块大小*8)这是由于在每个块组中使用了一个数据块位图来标识数据块是否空闲,因此每个块组中最多可以有(块大小*8)个块;该值除上分区大小就是分区中总的块组数。每个块组都包含以下内容:超级块。存放文件系统超级块的一个拷贝。
组描述符。该块组的组描述符。
数据块位图。标识相应的数据块是否空闲。
索引节点位图。标识相应的索引节点是否空闲。
索引节点表。存放所有索引节点的数据。
数据块。该块组中用来保存实际数据的数据块。
在每个块组中都保存了超级块的一个拷贝,默认情况下,只有第一个块组中的超级块结构才会被系统内核使用;其他块组中的超级块可以在 e2fsck 之类的程序对磁盘上的文件系统进行一致性检查使用。在 ext2 文件系统中,超级块的结构会通过一个名为 ext2_super_block 的结构进行引用。该结构的一些重要域如下所示:
清单 2. ext2_super_block 结构定义
struct ext2_super_block { __le32 s_inodes_count; /* 索引节点总数 */ __le32 s_blocks_count; /* 块数,即文件系统以块为单位的大小 */ __le32 s_r_blocks_count; /* 系统预留的块数 */ __le32 s_free_blocks_count; /* 空闲块数 */ __le32 s_free_inodes_count; /* 空闲索引节点数 */ __le32 s_first_data_block; /* 第一个可用数据块的块号 */ __le32 s_log_block_size; /* 块大小 */ __le32 s_blocks_per_group; /* 每个块组中的块数 */ __le32 s_inodes_per_group; /* 每个块组中的索引节点个数 */ ...} |
清单 3. ext2_group_desc 结构定义
/* * Structure of a blocks group descriptor */struct ext2_group_desc{ __le32 bg_block_bitmap; /* 数据块位图的块号 */ __le32 bg_inode_bitmap; /* 索引节点位图的块号 */ __le32 bg_inode_table; /* 第一个索引节点表的块号 */ __le16 bg_free_blocks_count; /* 该组中空闲块数 */ __le16 bg_free_inodes_count; /* 该组中空闲索引节点数 */ __le16 bg_used_dirs_count; /* 该组中的目录项 */ __le16 bg_pad; __le32 bg_reserved[3];}; |
清单 4. ext2_inode 结构定义
/* * Structure of an inode on the disk */struct ext2_inode { __le16 i_mode; /* 文件模式 */ __le16 i_uid; /* 文件所有者的 uid */ __le32 i_size; /* 以字节为单位的文件长度 */ __le32 i_atime; /* 最后一次访问该文件的时间 */ __le32 i_ctime; /* 索引节点最后改变的时间 */ __le32 i_mtime; /* 文件内容最后改变的时间 */ __le32 i_dtime; /* 文件删除的时间 */ __le16 i_gid; /* 文件所有者的 gid */ __le16 i_links_count; /* 硬链接数 */ __le32 i_blocks; /* 文件的数据块数 */ ... __le32 i_block[EXT2_N_BLOCKS];/* 指向数据块的指针 */ ...}; |
图 3. 数据块寻址方式
对于 i_block 的前 12 个元素(i_block[0]到i_block[11])来说,其中存放的就是实际的数据块号,即对应于文件的 0 到 11 块。这种方式称为直接寻址。
对于第13个元素(i_block[12])来说,其中存放的是另外一个数据块的逻辑块号;这个块中并不存放真正的数据,而是存放真正保存数据的数据块的块号。即 i_block[12] 指向一个二级数组,其每个元素都是对应数据块的逻辑块号。由于每个块号需要使用 4 个字节表示,因此这种寻址方式可以访问的对应文件的块号范围为 12 到 (块大小/4)+11。这种寻址方式称为间接寻址。
对于第14个元素(i_block[13])来说,其中存放也是另外一个数据块的逻辑块号。与间接寻址方式不同的是,i_block[13] 所指向的是一个数据块的逻辑块号的二级数组,而这个二级数组的每个元素又都指向一个三级数组,三级数组的每个元素都是对应数据块的逻辑块号。这种寻址方式称为二次间接寻址,对应文件块号的寻址范围为 (块大小/4)+12 到 (块大小/4)2+(块大小/4)+11。
对于第15个元素(i_block[14])来说,则利用了三级间接索引,其第四级数组中存放的才是逻辑块号对应的文件块号,其寻址范围从 (块大小/4)2+(块大小/4)+12 到 (块大小/4)3+ (块大小/4)2+(块大小/4)+11。
ext2 文件系统可以支持1024、2048和4096字节三种大小的块,对应的寻址能力如下表所示:
表 1. 各种数据块对应的文件寻址范围
块大小 | 直接寻址 | 间接寻址 | 二次间接寻址 | 三次间接寻址 |
1024 | 12KB | 268KB | 64.26MB | 16.06GB |
2048 | 24KB | 1.02MB | 513.02MB | 265.5GB |
4096 | 48KB | 4.04MB | 4GB | ~ 4TB |
清单 5. 新建磁盘分区
# fdisk /dev/sdb << ENDn+20GpwqEND |
清单 6. 在新分区上创建 ext2 文件系统
# mke2fs /dev/sdb6 |
清单 7. 挂载创建的 ext2 文件系统
# mkdir /tmp/test# mount /dev/sdb6 /tmp/test |
清单 8. 使用 dumpe2fs 熟悉这个文件系统的参数
# dumpe2fs /dev/sdb6 dumpe2fs 1.39 (29-May-2006)Filesystem volume name: <none>Last mounted on: <not available>Filesystem UUID: d8b10aa9-c065-4aa5-ab6f-96a9bcda52ceFilesystem magic number: 0xEF53Filesystem revision #: 1 (dynamic)Filesystem features: ext_attr resize_inode dir_index filetype sparse_super large_fileDefault mount options: (none)Filesystem state: not cleanErrors behavior: ContinueFilesystem OS type: LinuxInode count: 2443200Block count: 4885760Reserved block count: 244288Free blocks: 4797829Free inodes: 2443189First block: 0Block size: 4096Fragment size: 4096Reserved GDT blocks: 1022Blocks per group: 32768Fragments per group: 32768Inodes per group: 16288Inode blocks per group: 509Filesystem created: Mon Oct 29 20:04:16 2007Last mount time: Mon Oct 29 20:06:52 2007Last write time: Mon Oct 29 20:08:31 2007Mount count: 1Maximum mount count: 39Last checked: Mon Oct 29 20:04:16 2007Check interval: 15552000 (6 months)Next check after: Sat Apr 26 20:04:16 2008Reserved blocks uid: 0 (user root)Reserved blocks gid: 0 (group root)First inode: 11Inode size: 128Default directory hash: teaDirectory Hash Seed: d1432419-2def-4762-954a-1a26fef9d5e8Group 0: (Blocks 0-32767) Primary superblock at 0, Group descriptors at 1-2 Reserved GDT blocks at 3-1024 Block bitmap at 1025 (+1025), Inode bitmap at 1026 (+1026) Inode table at 1027-1535 (+1027) 31224 free blocks, 16276 free inodes, 2 directories Free blocks: 1543-22535, 22537-32767 Free inodes: 12, 14-16288...Group 149: (Blocks 4882432-4885759) Block bitmap at 4882432 (+0), Inode bitmap at 4882433 (+1) Inode table at 4882434-4882942 (+2) 2817 free blocks, 16288 free inodes, 0 directories Free blocks: 4882943-4885759 Free inodes: 2426913-2443200 |
清单 9. createfile.sh 脚本的用法
# ./createfile.sh [size in KB] [filename] |
清单 10. 准备测试文件
# cd /tmp/test#./createfile.sh 35 testfile.35K#./createfile.sh 10240 testfile.10M# cp testfile.35K testfile.35K.orig# cp testfile.10M testfile.10M.orig |
清单11. 查看文件的索引节点号
# ls -li | sort11 drwx------ 2 root root 16384 Oct 29 20:08 lost+found12 -rwxr-xr-x 1 root root 1406 Oct 29 20:09 createfile.sh13 -rw-r--r-- 1 root root 35840 Oct 29 20:09 testfile.35K14 -rw-r--r-- 1 root root 10485760 Oct 29 20:10 testfile.10M15 -rw-r--r-- 1 root root 35840 Oct 29 20:10 testfile.35K.orig16 -rw-r--r-- 1 root root 10485760 Oct 29 20:11 testfile.10M.orig |
清单12. 查看索引节点 <13> 的详细信息
# echo "stat <13>" | debugfs /dev/sdb6debugfs 1.39 (29-May-2006)Inode: 13 Type: regular Mode: 0644 Flags: 0x0 Generation: 2957086759User: 0 Group: 0 Size: 35840File ACL: 0 Directory ACL: 0Links: 1 Blockcount: 72Fragment: Address: 0 Number: 0 Size: 0ctime: 0x47268467 -- Mon Oct 29 20:09:59 2007atime: 0x4726849d -- Mon Oct 29 20:10:53 2007mtime: 0x47268467 -- Mon Oct 29 20:09:59 2007BLOCKS:(0-8):4096-4104TOTAL: 9 |
清单13. 查看索引节点 <14> 的详细信息
# echo "stat <14>" | debugfs /dev/sdb6debugfs 1.39 (29-May-2006)Inode: 14 Type: regular Mode: 0644 Flags: 0x0 Generation: 2957086760User: 0 Group: 0 Size: 10485760File ACL: 0 Directory ACL: 0Links: 1 Blockcount: 20512Fragment: Address: 0 Number: 0 Size: 0ctime: 0x47268485 -- Mon Oct 29 20:10:29 2007atime: 0x472684a5 -- Mon Oct 29 20:11:01 2007mtime: 0x47268485 -- Mon Oct 29 20:10:29 2007BLOCKS:(0-11):24576-24587, (IND):24588, (12-1035):24589-25612, (DIND):25613, (IND):25614, (1036-2059):25615-26638, (IND):26639, (2060-2559):26640-27139TOTAL: 2564 |
清单14. 删除测试文件
# rm -f testfile.35K testfile.10M |
清单15. 使用 lsdel 命令搜索已删除的文件
# echo "lsdel" | debugfs /dev/sdb6debugfs 1.39 (29-May-2006) Inode Owner Mode Size Blocks Time deleted 13 0 100644 35840 9/9 Mon Oct 29 20:32:05 2007 14 0 100644 10485760 2564/2564 Mon Oct 29 20:32:05 20072 deleted inodes found. |
清单16. 使用 dump 命令恢复已删除的文件
# echo "dump <13> /tmp/recover/testfile.35K.dump" | debugfs /dev/sdb6# echo "dump <14> /tmp/recover/testfile.10M.dump" | debugfs /dev/sdb6 |
清单 17. 使用 dd 命令手工恢复已删除的文件
# dd if=/dev/sdb6 of=/tmp/recover/testfile.10M.dd.part1 bs=4096 count=12 skip=24576# dd if=/dev/sdb6 of=/tmp/recover/testfile.10M.dd.part2 bs=4096 count=1024 skip=24589# dd if=/dev/sdb6 of=/tmp/recover/testfile.10M.dd.part2 bs=4096 count=1024 skip=25615# dd if=/dev/sdb6 of=/tmp/recover/testfile.10M.dd.part4 bs=4096 count=500 skip=26640# cat /tmp/recover/testfile.10M.dd.part[1-4] > /tmp/recover/ testfile.10M.dd |
清单 18. 使用 diff 命令对恢复文件和原文件进行比较
# diff /tmp/recover/ testfile.10M.dd /tmp/test/ testfile.10M.orig |
清单 19. 再次查看索引节点 <14> 的详细信息
# echo "stat <14>" | debugfs /dev/sdb6debugfs 1.39 (29-May-2006)Inode: 14 Type: regular Mode: 0644 Flags: 0x0 Generation: 2957086760User: 0 Group: 0 Size: 10485760File ACL: 0 Directory ACL: 0Links: 0 Blockcount: 20512Fragment: Address: 0 Number: 0 Size: 0ctime: 0x47268995 -- Mon Oct 29 20:32:05 2007atime: 0x472684a5 -- Mon Oct 29 20:11:01 2007mtime: 0x47268485 -- Mon Oct 29 20:10:29 2007dtime: 0x47268995 -- Mon Oct 29 20:32:05 2007BLOCKS: (0-11):24576-24587, (IND):24588, (DIND):25613 TOTAL: 14 |
清单 20. 查看间接索引块 24588 中的内容
# dd if=/dev/sdb6 of=block. 24588 bs=4096 count=1 skip=24588# hexdump block. 245880000000 0000 0000 0000 0000 0000 0000 0000 0000*0001000 |
清单 21. 内核中 ext2_free_data 和 ext2_free_branches 函数的实现
814 /**815 * ext2_free_data - free a list of data blocks816 * @inode: inode we are dealing with817 * @p: array of block numbers818 * @q: points immediately past the end of array819 *820 * We are freeing all blocks refered from that array (numbers are821 * stored as little-endian 32-bit) and updating @inode->i_blocks822 * appropriately.823 */824 static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q)825 {826 unsigned long block_to_free = 0, count = 0;827 unsigned long nr;828 829 for ( ; p < q ; p++) {830 nr = le32_to_cpu(*p);831 if (nr) {832 *p = 0;833 /* accumulate blocks to free if they're contiguous */834 if (count == 0)835 goto free_this;836 else if (block_to_free == nr - count)837 count++;838 else {839 mark_inode_dirty(inode);840 ext2_free_blocks (inode, block_to_free, count);841 free_this:842 block_to_free = nr;843 count = 1;844 }845 }846 }847 if (count > 0) {848 mark_inode_dirty(inode);849 ext2_free_blocks (inode, block_to_free, count);850 }851 }852 853 /**854 * ext2_free_branches - free an array of branches855 * @inode: inode we are dealing with856 * @p: array of block numbers857 * @q: pointer immediately past the end of array858 * @depth: depth of the branches to free859 *860 * We are freeing all blocks refered from these branches (numbers are861 * stored as little-endian 32-bit) and updating @inode->i_blocks862 * appropriately.863 */864 static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int depth)865 {866 struct buffer_head * bh;867 unsigned long nr;868 869 if (depth--) {870 int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);871 for ( ; p < q ; p++) {872 nr = le32_to_cpu(*p);873 if (!nr)874 continue;875 *p = 0;876 bh = sb_bread(inode->i_sb, nr);877 /*878 * A read failure? Report error and clear slot879 * (should be rare).880 */ 881 if (!bh) {882 ext2_error(inode->i_sb, "ext2_free_branches",883 "Read failure, inode=%ld, block=%ld",884 inode->i_ino, nr);885 continue;886 }887 ext2_free_branches(inode,888 (__le32*)bh->b_data,889 (__le32*)bh->b_data + addr_per_block,890 depth);891 bforget(bh);892 ext2_free_blocks(inode, nr, 1);893 mark_inode_dirty(inode);894 }895 } else896 ext2_free_data(inode, p, q);897 } |
在本系列文章的第一部分中,我们介绍了 ext2 文件系统中的一些基本概念和重要数据结构,并通过几个实例学习了如何恢复已经删除的文件,最后通过修改 2.6 版本内核中 ext2 文件系统的实现,解决了大文件无法正常恢复的问题。通过第一部分的介绍,我们已经知道如何恢复系统中删除的普通文件了,但是系统中还存在一些特殊的文件,比如我们熟悉的符号链接等。回想一下在本系列文章的第一部分中,目录项是使用一个名为 ext2_dir_entry_2 的结构来表示的,该结构定义如下:
清单1. ext2_dir_entry_2 结构定义
struct ext2_dir_entry_2 { __le32 inode; /* 索引节点号 */ __le16 rec_len; /* 目录项的长度 */ __u8 name_len; /* 文件名长度 */ __u8 file_type; /* 文件类型 */ char name[EXT2_NAME_LEN]; /* 文件名 */}; |
表 1. ext2 文件系统中支持的文件类型
file_type | 宏定义 | 说明 |
1 | EXT2_FT_REG_FILE | 普通文件 |
2 | EXT2_FT_DIR | 目录 |
3 | EXT2_FT_CHRDEV | 字符设备 |
4 | EXT2_FT_BLKDEV | 块设备 |
5 | EXT2_FT_FIFO | 命名管道 |
6 | EXT2_FT_SOCK | socket |
7 | EXT2_FT_SYMLINK | 符号链接 |
清单2. 创建带有文件洞的文件
# echo -n "X" | dd of=/tmp/test/hole bs=1024 seek=7# ls -li /tmp/test/hole15 -rw-r--r-- 1 root root 7169 Nov 26 11:03 /tmp/test/hole# hexdump /tmp/test/hole 0000000 0000 0000 0000 0000 0000 0000 0000 0000*0001c00 0058 0001c01 |
图1. /tmp/test/hole 文件的存储方法
file_hole.jpg现在我们可以使用 debugfs 来查看一下这个文件的详细信息:
清单3. 带有文件洞的文件的 inode 信息
# echo "stat <15>" | debugfs /dev/sdb6debugfs 1.39 (29-May-2006)debugfs: Inode: 15 Type: regular Mode: 0644 Flags: 0x0 Generation: 4118330634User: 0 Group: 0 Size: 7169File ACL: 1544 Directory ACL: 0Links: 1 Blockcount: 16Fragment: Address: 0 Number: 0 Size: 0ctime: 0x474a379c -- Mon Nov 26 11:03:56 2007atime: 0x474a379c -- Mon Nov 26 11:03:56 2007mtime: 0x474a379c -- Mon Nov 26 11:03:56 2007BLOCKS: (1):20480 TOTAL: 1 |
清单4. 使用 dd 命令手工恢复带有文件洞的文件
# dd if=/dev/zero of=/tmp/recover/hole.part1 bs=4096 count=1# dd if=/dev/sdb6 of=/tmp/recover/hole.part2 bs=4096 count=1 skip=20480# cat /tmp/recover/hole.part1 /tmp/recover/hole.part2 > /tmp/recover/hole.full# split -d -b 7169 hole.full hole# mv hole00 hole# diff /tmp/test/hole /tmp/recover/hole |
清单5. 创建 57K 大小的带有文件洞的文件
# echo -n "Y" | dd of=/tmp/test/hole.57K bs=1024 seek=57# ls -li /tmp/test/hole.57K 17 -rw-r--r-- 1 root root 58369 Nov 26 12:53 /tmp/test/hole.57K# hexdump /tmp/test/hole.57K0000000 0000 0000 0000 0000 0000 0000 0000 0000*000e400 0059 000e401 |
清单6. 使用间接寻址方式的带有文件洞的文件的 inode 信息
# echo "stat <17>" | debugfs /dev/sdb6 debugfs 1.39 (29-May-2006)debugfs: Inode: 17 Type: regular Mode: 0644 Flags: 0x0 Generation: 4261347083User: 0 Group: 0 Size: 58369File ACL: 1544 Directory ACL: 0Links: 1 Blockcount: 24Fragment: Address: 0 Number: 0 Size: 0ctime: 0x474a5166 -- Mon Nov 26 12:53:58 2007atime: 0x474a5187 -- Mon Nov 26 12:54:31 2007mtime: 0x474a5166 -- Mon Nov 26 12:53:58 2007BLOCKS:(IND):24576, (14):24577TOTAL: 2 |
清单7. 索引数据块中存储的数据
# dd if=/dev/sdb6 of=/tmp/recover/block.24576 bs=4096 count=1 skip=24576# hexdump block.245760000000 0000 0000 0000 0000 6001 0000 0000 00000000010 0000 0000 0000 0000 0000 0000 0000 0000*0001000 |
清单8. 手工恢复带有文件洞的大文件
# dd if=/dev/zero of=/tmp/recover/hole.57K.part1 bs=4096 count=14 # dd if=/dev/sdb6 of=/tmp/recover/hole.57K.part2 bs=4096 count=1 skip=24577 # cat /tmp/recover/hole.57K.part1 /tmp/recover/hole.57K.part2 \> /tmp/recover/hole.57K.full # split -d -b 58369 hole.57K.full hole.57K # mv hole.57K00 hole.57K # diff /tmp/test/hole.57K /tmp/recover/hole.57K |
清单9. 用来测试的文件系统的信息
# debugfs /dev/sdb6debugfs 1.39 (29-May-2006)debugfs: ls -l 2 40755 (2) 0 0 4096 28-Nov-2007 16:57 . 2 40755 (2) 0 0 4096 28-Nov-2007 16:57 .. 11 40700 (2) 0 0 16384 28-Nov-2007 16:52 lost+found 12 100755 (1) 0 0 1406 28-Nov-2007 16:53 createfile.sh 13 100644 (1) 0 0 35840 28-Nov-2007 16:53 testfile.35K 14 100644 (1) 0 0 10485760 28-Nov-2007 16:54 testfile.10M 32577 40755 (2) 0 0 4096 28-Nov-2007 16:56 dir1 15 100644 (1) 0 0 35840 28-Nov-2007 16:56 testfile.35K.orig 16 100644 (1) 0 0 10485760 28-Nov-2007 16:57 testfile.10M.origdebugfs: ls -l dir1 32577 40755 (2) 0 0 4096 28-Nov-2007 16:56 . 2 40755 (2) 0 0 4096 28-Nov-2007 16:57 .. 32578 100755 (1) 0 0 1406 28-Nov-2007 16:55 createfile.sh 32579 40755 (2) 0 0 4096 28-Nov-2007 16:55 subdir11 48865 40755 (2) 0 0 4096 28-Nov-2007 16:55 subdir12 32580 100644 (1) 0 0 35840 28-Nov-2007 16:56 testfile.35K 32581 100644 (1) 0 0 10485760 28-Nov-2007 16:56 testfile.10M |
清单10. 根目录(索引节点 <2>)子目录 dir1的 inode 信息
debugfs: stat <2>Inode: 2 Type: directory Mode: 0755 Flags: 0x0 Generation: 0User: 0 Group: 0 Size: 4096File ACL: 0 Directory ACL: 0Links: 4 Blockcount: 8Fragment: Address: 0 Number: 0 Size: 0ctime: 0x474d2d63 -- Wed Nov 28 16:57:07 2007atime: 0x474d3203 -- Wed Nov 28 17:16:51 2007mtime: 0x474d2d63 -- Wed Nov 28 16:57:07 2007BLOCKS:(0):1536TOTAL: 1debugfs: stat <32577>Inode: 32577 Type: directory Mode: 0755 Flags: 0x0 Generation: 1695264350User: 0 Group: 0 Size: 4096File ACL: 1542 Directory ACL: 0Links: 4 Blockcount: 16Fragment: Address: 0 Number: 0 Size: 0ctime: 0x474d2d2a -- Wed Nov 28 16:56:10 2007atime: 0x474d3203 -- Wed Nov 28 17:16:51 2007mtime: 0x474d2d2a -- Wed Nov 28 16:56:10 2007BLOCKS:(0):88064TOTAL: 1 |
清单11. 备份根目录和子目录 dir1 的数据块
# dd if=/dev/sdb6 of=/tmp/recover/block.1536.orig bs=4096 count=1 skip=1536# dd if=/dev/sdb6 of=/tmp/recover/block.88064.orig bs=4096 count=1 skip=88064 |
清单12. read_dir_entry.c 源代码
#include <stdio.h>#include <stdlib.h>#include <ext2fs/ext2_fs.h>struct ext2_dir_entry_part { __u32 inode; /* Inode number */ __u16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type;} dep;void usage(){ printf("read_dir_entry [dir entry filename] [dir entry size]\n");}int main(int argc, char **argv){ struct ext2_dir_entry_2 de; char *filename = NULL; FILE *fp = NULL; int rtn = 0; int length = 0; int de_size = 0; if (argc < 3) { printf("Too few parameters!\n"); usage(); exit(1); } filename = argv[1]; de_size = atoi(argv[2]); fp = fopen(filename, "r"); if (!fp) { printf("cannot open file: %s\n", filename); exit(1); } printf(" offset | inode number | rec_len | name_len | file_type | name\n"); printf("========================================\n"); while ( rtn = fread(&dep, sizeof(struct ext2_dir_entry_part), 1, fp) ) { if (dep.rec_len <= 0) { fclose(fp); exit(0); } fseek(fp, 0 - sizeof(struct ext2_dir_entry_part), SEEK_CUR); fread(&de, ((int)(dep.name_len + 3)/4)*4 + sizeof(struct ext2_dir_entry_part), 1, fp); de.name[de.name_len] = '\0'; printf("%6d: %12d%12d%12d%12d %s\n", length, de.inode, de.rec_len, de.name_len, de.file_type, de.name); length += dep.rec_len; if (length >= de_size - sizeof(struct ext2_dir_entry_part)) { fclose(fp); exit(0); } } fclose(fp); } |
清单13. 编译 read_dir_entry.c
# gcc –o read_dir_entry read_dir_entry.c |
清单14. 分析原始目录项中的数据
# ./read_dir_entry block.1536.orig 4096 offset | inode number | rec_len | name_len | file_type | name================================================================= 0: 2 12 1 2 . 12: 2 12 2 2 .. 24: 11 20 10 2 lost+found 44: 12 24 13 1 createfile.sh 68: 13 20 12 1 testfile.35K 88: 14 20 12 1 testfile.10M 108: 32577 12 4 2 dir1 120: 15 28 17 1 testfile.35K.orig 148: 16 3948 17 1 testfile.10M.orig# ./read_dir_entry block.88064.orig 4096 offset | inode number | rec_len | name_len | file_type | name================================================================= 0: 32577 12 1 2 . 12: 2 12 2 2 .. 24: 32578 24 13 1 createfile.sh 48: 32579 16 8 2 subdir11 64: 48865 16 8 2 subdir12 80: 32580 20 12 1 testfile.35K 100: 32581 3996 12 1 testfile.10M |
清单15. 删除目录并重新备份目录项数据
# rm –rf /tmp/test/dir1# cd /# umount /tmp/test# dd if=/dev/sdb6 of=/tmp/recover/block.1536.deleted bs=4096 count=1 skip=1536# dd if=/dev/sdb6 of=/tmp/recover/block.88064. deleted bs=4096 count=1 skip=88064 |
清单16. 分析新目录项中的数据
# ./read_dir_entry block.1536.deleted 4096 offset | inode number | rec_len | name_len | file_type | name================================================================= 0: 2 12 1 2 . 12: 2 12 2 2 .. 24: 11 20 10 2 lost+found 44: 12 24 13 1 createfile.sh 68: 13 20 12 1 testfile.35K 88: 14 32 12 1 testfile.10M 108: 0 12 4 2 dir1 120: 15 28 17 1 testfile.35K.orig 148: 16 3948 17 1 testfile.10M.orig# ./read_dir_entry block.88064.deleted 4096 offset | inode number | rec_len | name_len | file_type | name================================================================= 0: 32577 12 1 2 . 12: 2 12 2 2 .. 24: 32578 24 13 1 createfile.sh 48: 32579 16 8 2 subdir11 64: 48865 16 8 2 subdir12 80: 32580 20 12 1 testfile.35K 100: 32581 3996 12 1 testfile.10M |
清单17. 删除子目录后索引节点信息的变化
# debugfs /dev/sdb6debugfs 1.39 (29-May-2006)debugfs: stat <2>Inode: 2 Type: directory Mode: 0755 Flags: 0x0 Generation: 0User: 0 Group: 0 Size: 4096File ACL: 0 Directory ACL: 0Links: 3 Blockcount: 8Fragment: Address: 0 Number: 0 Size: 0ctime: 0x474d3387 -- Wed Nov 28 17:23:19 2007atime: 0x474d33c2 -- Wed Nov 28 17:24:18 2007mtime: 0x474d3387 -- Wed Nov 28 17:23:19 2007BLOCKS:(0):1536TOTAL: 1debugfs: stat <32577>Inode: 32577 Type: directory Mode: 0755 Flags: 0x0 Generation: 1695264350User: 0 Group: 0 Size: 0File ACL: 1542 Directory ACL: 0Links: 0 Blockcount: 16Fragment: Address: 0 Number: 0 Size: 0ctime: 0x474d3387 -- Wed Nov 28 17:23:19 2007atime: 0x474d3387 -- Wed Nov 28 17:23:19 2007mtime: 0x474d3387 -- Wed Nov 28 17:23:19 2007dtime: 0x474d3387 -- Wed Nov 28 17:23:19 2007BLOCKS:(0):88064TOTAL: 1 |
设置 dtime 时间,并更新其他时间字段。
由于目录只有在为空时才会被删除,因此其 Size 值会被设置为 0,Links 字段也被设置为 0。
通过了解数据块和索引节点的相应变化可以为恢复目录提供一个清晰的思路,其具体步骤如下:确定删除目录所对应的索引节点号。
按照恢复文件的方法恢复索引节点对应的数据块。
遍历数据块内容,恢复其中包含的文件和子目录。
更新索引节点对应信息。
修改父目录的索引节点信息和数据块中对应目录项的内容。
实际上,步骤3并不是必须的,因为如果这个目录中包含文件或子目录,使用 debugfs 的 lsdel 命令(遍历索引节点表)也可以找到所删除的索引节点记录,采用本文中介绍的方法也可以将其逐一恢复出来。debugfs 的 mi 命令可以用来直接修改索引节点的信息,下面我们就使用这个命令来修改 dir1 这个目录对应的索引节点的信息:
清单18. 使用 debugfs 的 mi 命令直接修改索引节点信息
# debugfs -w /dev/sdb6debugfs 1.39 (29-May-2006)debugfs: lsdel Inode Owner Mode Size Blocks Time deleted 32577 0 40755 0 1/ 1 Wed Nov 28 17:23:19 2007 32578 0 100755 1406 1/ 1 Wed Nov 28 17:23:19 2007 32579 0 40755 0 1/ 1 Wed Nov 28 17:23:19 2007 32580 0 100644 35840 9/ 9 Wed Nov 28 17:23:19 2007 32581 0 100644 10485760 2564/2564 Wed Nov 28 17:23:19 2007 48865 0 40755 0 1/ 1 Wed Nov 28 17:23:19 20076 deleted inodes found.debugfs: mi <32577> Mode [040755] User ID [0] Group ID [0] Size [0] 4096 Creation time [1196241799] Modification time [1196241799] Access time [1196241799] Deletion time [1196241799] 0 Link count [0] 4 Block count [16] File flags [0x0] Generation [0x650bae5e] File acl [1542] Directory acl [0] Fragment address [0] Fragment number [0] Fragment size [0] Direct Block #0 [88064] Direct Block #1 [0] Direct Block #2 [0] Direct Block #3 [0] Direct Block #4 [0] Direct Block #5 [0] Direct Block #6 [0] Direct Block #7 [0] Direct Block #8 [0] Direct Block #9 [0] Direct Block #10 [0] Direct Block #11 [0] Indirect Block [0] Double Indirect Block [0] Triple Indirect Block [0] debugfs: link <32577> dir1debugfs: q |
清单19. 验证恢复结果
# mount /dev/sdb6 /tmp/test# ls -li /tmp/testtotal 20632 12 -rwxr-xr-x 1 root root 1406 Nov 28 16:53 createfile.sh32577 drwxr-xr-x 4 root root 4096 Nov 28 17:23 dir1 11 drwx------ 2 root root 16384 Nov 28 16:52 lost+found 14 -rw-r--r-- 1 root root 10485760 Nov 28 16:54 testfile.10M 16 -rw-r--r-- 1 root root 10485760 Nov 28 16:57 testfile.10M.orig 13 -rw-r--r-- 1 root root 35840 Nov 28 16:53 testfile.35K 15 -rw-r--r-- 1 root root 35840 Nov 28 16:56 testfile.35K.orig# ls -li /tmp/test/dir1total 0??--------- ? ? ? ? ? /tmp/test/dir1/createfile.sh??--------- ? ? ? ? ? /tmp/test/dir1/subdir11??--------- ? ? ? ? ? /tmp/test/dir1/subdir12??--------- ? ? ? ? ? /tmp/test/dir1/testfile.10M??--------- ? ? ? ? ? /tmp/test/dir1/testfile.35K |
清单20. 使用 e2fsck 强制对文件系统进行一致性检查
# e2fsck -f -y /dev/sdb6 > e2fsck.out 2>&1 |
检查目录结构的问题。检查索引节点的父目录,如果不存在,就认为父目录就是根目录。对于目录节点,需要检查是否包含当前目录和父目录项。
检查目录结构的连通性。防止出现按照绝对路径无法访问文件的情况出现,将这些有问题的文件或目录放入 lost+found 目录中。
检查并修复引用计数。统计对索引节点的引用计数值。
检查并修复块组信息,包括块位图、索引节点位图,计算块组中的空闲块数、空闲索引节点数等。
现在重新挂载这个文件系统,会发现所有的文件已经全部恢复出来了。回页首符号链接我们知道,在 ext2 文件系统中,链接可以分为两种:硬链接和符号链接(或称为软链接)。实际上,目录中的每个文件名都对应一个硬链接。硬链接的出现是为了解决使用不同的文件名来引用同一个文件的问题。如果没有硬链接,只能通过给现有文件新建一份拷贝才能通过另外一个名字来引用这个文件,这样做的问题是在文件内容发生变化的情况下,势必会造成引用这些文件的进程所访问到的数据不一致的情况出现。而虽然每个硬链接在文件目录项中都是作为一个单独的项存在的,但是其索引节点号完全相同,这就是说它们引用的是同一个索引节点,因此对应的文件数据也完全相同。下面让我们通过一个例子来验证一下:
清单21.硬链接采用相同的索引节点号
# ln testfile.10M hardlink.10M# ls -litotal 20592 12 -rwxr-xr-x 1 root root 1406 Nov 29 19:19 createfile.sh1205313 drwxr-xr-x 2 root root 4096 Nov 29 19:29 dir1 14 -rw-r--r-- 2 root root 10485760 Nov 29 19:21 hardlink.10M 11 drwx------ 2 root root 16384 Nov 29 19:19 lost+found 14 -rw-r--r-- 2 root root 10485760 Nov 29 19:21 testfile.10M 13 -rw-r--r-- 1 root root 35840 Nov 29 19:20 testfile.35K |
不能建立跨文件系统的硬链接,这是由于每个文件系统中的索引节点号都是单独进行编号的,跨文件系统就会导致索引节点号变得非常混乱。而这在现代 Linux/Unix 操作系统上恰恰是无法接受的,因为每个文件系统中都可能会有很多挂载点来挂载不同的文件系统。
为了解决上面的问题,符号链接就应运而生了。符号链接与硬链接的区别在于它要占用一个单独的索引节点来存储相关数据,但却并不存储链接指向的文件的数据,而是存储链接的路径名:如果这个路径名小于60个字符,就其存储在符号链接索引节点的 i_block 域中;如果超过 60 个字符,就使用一个单独的数据块来存储。下面让我们来看一个例子:
清单22. 符号链接采用不同的索引节点号
# ln -s testfile.10M softlink.10M# ls -litotal 20596 12 -rwxr-xr-x 1 root root 1406 Nov 29 19:19 createfile.sh1205313 drwxr-xr-x 2 root root 4096 Nov 29 19:29 dir1 14 -rw-r--r-- 2 root root 10485760 Nov 29 19:21 hardlink.10M 11 drwx------ 2 root root 16384 Nov 29 19:19 lost+found 15 lrwxrwxrwx 1 root root 12 Nov 29 19:41 softlink.10M -> testfile.10M 14 -rw-r--r-- 2 root root 10485760 Nov 29 19:21 testfile.10M 13 -rw-r--r-- 1 root root 35840 Nov 29 19:20 testfile.35K# echo "stat <15>" | debugfs /dev/sdb6debugfs 1.39 (29-May-2006)debugfs: Inode: 15 Type: symlink Mode: 0777 Flags: 0x0 Generation: 2344716327User: 0 Group: 0 Size: 12File ACL: 1542 Directory ACL: 0Links: 1 Blockcount: 8Fragment: Address: 0 Number: 0 Size: 0ctime: 0x474ea56f -- Thu Nov 29 19:41:35 2007atime: 0x474ea571 -- Thu Nov 29 19:41:37 2007mtime: 0x474ea56f -- Thu Nov 29 19:41:35 2007Fast_link_dest: testfile.10M |
清单23. 长名符号链接
# touch abcdwfghijklmnopqrstuvwxyz0123456789abcdwfghijklmnopqrstuvwxyz0123456789.sh # ln -s abcdwfghijklmnopqrstuvwxyz0123456789abcdwfghijklmnopqrstuvwxyz0123456789.sh \longsoftlink.sh# ls -litotal 20608 16 -rw-r--r-- 1 root root 0 Nov 29 19:52 \ abcdwfghijklmnopqrstuvwxyz0123456789abcdwfghijklmnopqrstuvwxyz0123456789.sh 12 -rwxr-xr-x 1 root root 1406 Nov 29 19:19 createfile.sh1205313 drwxr-xr-x 2 root root 4096 Nov 29 19:29 dir1 14 -rw-r--r-- 2 root root 10485760 Nov 29 19:21 hardlink.10M 17 lrwxrwxrwx 1 root root 75 Nov 29 19:53 longsoftlink.sh -> \ abcdwfghijklmnopqrstuvwxyz0123456789abcdwfghijklmnopqrstuvwxyz0123456789.sh 11 drwx------ 2 root root 16384 Nov 29 19:19 lost+found 15 lrwxrwxrwx 1 root root 12 Nov 29 19:41 softlink.10M -> testfile.10M 14 -rw-r--r-- 2 root root 10485760 Nov 29 19:21 testfile.10M 13 -rw-r--r-- 1 root root 35840 Nov 29 19:20 testfile.35K# echo "stat <17>" | debugfs /dev/sdb6debugfs 1.39 (29-May-2006)debugfs: Inode: 17 Type: symlink Mode: 0777 Flags: 0x0 Generation: 744523175User: 0 Group: 0 Size: 75File ACL: 1542 Directory ACL: 0Links: 1 Blockcount: 16Fragment: Address: 0 Number: 0 Size: 0ctime: 0x474ea824 -- Thu Nov 29 19:53:08 2007atime: 0x474ea826 -- Thu Nov 29 19:53:10 2007mtime: 0x474ea824 -- Thu Nov 29 19:53:08 2007BLOCKS: (0):6144 TOTAL: 1 |
清单24. 恢复长名符号链接
# dd if=/dev/sdb6 of=longsoftlink.6144 bs=4096 count=1 skip=6144# xxd longsoftlink.6144 | more0000000: 6162 6364 7766 6768 696a 6b6c 6d6e 6f70 abcdwfghijklmnop0000010: 7172 7374 7576 7778 797a 3031 3233 3435 qrstuvwxyz0123450000020: 3637 3839 6162 6364 7766 6768 696a 6b6c 6789abcdwfghijkl0000030: 6d6e 6f70 7172 7374 7576 7778 797a 3031 mnopqrstuvwxyz010000040: 3233 3435 3637 3839 2e73 6800 0000 0000 23456789.sh.....0000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ |
回页首下载
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
样例代码 | read_dir_entry.c | 2KB | HTTP |
相关文章推荐
- linux下如何恢复rm命令删除的文件
- 如何恢复 Linux 上删除的文件,第 2 部分(特殊文件的恢复)
- Linux不小心删除日志文件syslog如何恢复
- 如何恢复 Linux 上删除的文件,第 7 部分
- 如何恢复 Linux 上删除的文件:ext2
- 如何恢复 Linux 上删除的文件
- linux下如何恢复rm命令删除的文件
- 如何恢复误删除的Linux文件
- 如何恢复 Linux 上删除的文件,第 1 部分(原理及普通文件的恢复)
- 如何恢复误删除的Linux文件
- 如何恢复 Linux 上删除的文件 第一部分 转自IBM
- 如何恢复误删除的Linux文件
- 如何在 Linux 中恢复一个删除了的文件
- linux下,如何恢复ext文件系统下删除的文件
- 如何在 Linux 中恢复一个删除了的文件
- 如何恢复 Linux 上删除的文件(2)
- 如何恢复Linux上删除的文件
- 在linux系统下如何恢复误删除文件
- 如何恢复 Linux 上删除的文件
- 如何在 Linux 中恢复一个删除了的文件