如何恢复 Linux 上删除的文件-原理及普通文件的恢复(1)
- UID
- 1066743
|
如何恢复 Linux 上删除的文件-原理及普通文件的恢复(1)
对于很多 Linux 的用户来说,可能有一个问题一直都非常头疼:对于那些不小心删除的数据来说,怎样才能恢复出来呢?大家知道,在 Windows 系统上,回收站中保存了最近使用资源管理器时删除的文件。即便是对于那些在命令行中删除的文件来说,也有很多工具(例如recover4all,FinalData Recovery)可以把这些已经删除的文件恢复出来。在Linux 下这一切是否可能呢?
实际上,为了方便用户的使用,现在 Linux 上流行的桌面管理工具(例如gnome和KDE)中都已经集成了回收站的功能。其基本思想是在桌面管理工具中捕获对文件的删除操作,将要删除的文件移动到用户根目录下的 .Trash 文件夹中,但却并不真正删除该文件。当然,像在 Windows 上一样,如果用户在删除文件的同时,按下了 Shift 键并确认删除该文件,那么这个文件就不会被移动到 .Trash 文件夹中,也就无从恢复了。此时,习惯了使用 Windows 上各种恢复工具的人就会顿足捶胸,抱怨 Linux 上工具的缺乏了。但是请稍等一下,难道按照这种方式删除的文件就真的无从恢复了么?或者换一个角度来看,使用 rm 命令删除的文件是否还有办法能够恢复出来呢?
背景知识在开始真正进行实践之前,让我们首先来了解一下在 Linux 系统中,文件是如何进行存储和定位的,这对于理解如何恢复文件来说非常重要。我们知道,数据最终以数据块的形式保存在磁盘上,而操作系统是通过文件系统来管理这些数据的。ext2/ext3 是 Linux 上应用最为广泛的文件系统,本文将以 ext2 文件系统为例展开介绍。
我们知道,在操作系统中,文件系统是采用一种层次化的形式表示的,通常可以表示成一棵倒置的树。所有的文件和子目录都是通过查找其父目录项来定位的,目录项中通过匹配文件名可以找到对应的索引节点号(inode),通过查找索引节点表(inode table)就可以找到文件在磁盘上的位置,整个过程如图1所示。
图 1. 文件数据定位过程对于 ext2 类型的文件系统来说,目录项是使用一个名为 ext2_dir_entry_2 的结构来表示的,该结构定义如下所示:
清单 1. ext2_dir_entry_2 结构定义1
2
3
4
5
6
7
| struct ext2_dir_entry_2 {
__le32 inode; /* 索引节点号 */
__le16 rec_len; /* 目录项的长度 */
__u8 name_len; /* 文件名长度 */
__u8 file_type; /* 文件类型 */
char name[EXT2_NAME_LEN]; /* 文件名 */
};
|
在 Unix/Linux 系统中,目录只是一种特殊的文件。目录和文件是通过 file_type 域来区分的,该值为 1 则表示是普通文件,该值为 2 则表示是目录。
对于每个 ext2 分区来说,其在物理磁盘上的布局如图 2 所示:
图 2. ext2 分区的布局从图 2 中可以看到,对于 ext2 文件系统来说,磁盘被划分成一个个大小相同的数据块,每个块的大小可以是1024、2048 或 4096 个字节。其中,第一个块称为引导块,一般保留做引导扇区使用,因此 ext2 文件系统一般都是从第二个块开始的。剩余的块被划分为一个个的块组,ext2 文件系统会试图尽量将相同文件的数据块都保存在同一个块组中,并且尽量保证文件在磁盘上的连续性,从而提高文件读写时的性能。
至于一个分区中到底有多少个块组,这取决于两个因素:
最终的计算公式如下:
分区中的块组数=分区大小/(块大小*8)
这是由于在每个块组中使用了一个数据块位图来标识数据块是否空闲,因此每个块组中最多可以有(块大小*8)个块;该值除上分区大小就是分区中总的块组数。
每个块组都包含以下内容:
- 超级块。存放文件系统超级块的一个拷贝。
- 组描述符。该块组的组描述符。
- 数据块位图。标识相应的数据块是否空闲。
- 索引节点位图。标识相应的索引节点是否空闲。
- 索引节点表。存放所有索引节点的数据。
- 数据块。该块组中用来保存实际数据的数据块。
在每个块组中都保存了超级块的一个拷贝,默认情况下,只有第一个块组中的超级块结构才会被系统内核使用;其他块组中的超级块可以在 e2fsck 之类的程序对磁盘上的文件系统进行一致性检查使用。在 ext2 文件系统中,超级块的结构会通过一个名为 ext2_super_block 的结构进行引用。该结构的一些重要域如下所示:
清单 2. ext2_super_block 结构定义1
2
3
4
5
6
7
8
9
10
11
12
| 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; /* 每个块组中的索引节点个数 */
...
}
|
每个块组都有自己的组描述符,在 ext2 文件系统中是通过一个名为 ext2_group_desc的结构进行引用的。该结构的定义如下:
清单 3. ext2_group_desc 结构定义1
2
3
4
5
6
7
8
9
10
11
12
13
14
| /*
* 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];
};
|
数据块位图和索引节点位图分别占用一个块的大小,其每一位描述了对应数据块或索引节点是否空闲,如果该位为0,则表示空闲;如果该位为1,则表示已经使用。
索引节点表存放在一系列连续的数据块中,每个数据块中可以包括若干个索引节点。每个索引节点在 ext2 文件系统中都通过一个名为 ext2_inode 的结构进行引用,该结构大小固定为 128 个字节,其中一些重要的域如下所示:
清单 4. ext2_inode 结构定义1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| /*
* 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];/* 指向数据块的指针 */
...
};
|
第一个索引节点所在的块号保存在该块组描述符的 bg_inode_table 域中。请注意 i_block 域,其中就包含了保存数据的数据块的位置。有关如何对数据块进行寻址,请参看后文“数据块寻址方式”一节的内容。
需要知道的是,在普通的删除文件操作中,操作系统并不会逐一清空保存该文件的数据块的内容,而只会释放该文件所占用的索引节点和数据块,方法是将索引节点位图和数据块位图中的相应标识位设置为空闲状态。因此,如果我们可以找到文件对应的索引节点,由此查到相应的数据块,就可能从磁盘上将已经删除的文件恢复出来。
幸运的是,这一切都是可能的!本文将通过几个实验来了解一下如何从磁盘上恢复删除的文件。 |
|
|
|
|
|