linux恢复磁盘上删除的文件的方法,【转】如何恢复 Linux 上删除的文件(5)

ext3 文件系统中恢复删除文件的方法 1:正文匹配

我们知道,磁盘以及磁盘上的某个分区在系统中都以设备文件的形式存在,我们可以将这些设备文件当作普通文件一样来读取数据。不过,既然已经无法通过文件名来定位文件的数据块位置,现在要想恢复文件,就必须了解文件的数据,并通过正文匹配进行检索了。自然,grep

就是这样一个理想的工具:

清单8. 使用 grep 搜索磁盘上的文件

# ./creatfile.sh 35 testfile.35K

# rm -f testfile.35K

# cd ..

# umount test

# grep -A 1 -B 1 -a -n " 10:0" /dev/sdb7 > sdb7.testfile.35K

grep: memory exhausted

# cat sdb7.testfile.35K

545- 9:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

546:10:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

547- 11:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

在清单 8 中的例子中,我们首先使用本系列文章第 1 部分中提供的脚本创建了一个测试文件,在删除该文件后,通过利用 grep

以“ 10:0”作为关键字对设备文件进行正文匹配,我们顺利地找到了测试文件中的第 10 行数据。需要注意的是,grep

命令中我们使用了几个参数,-a 参数表明将设备文件当作文本文件进行处理,-B 和 –A

参数分别说明同时打印匹配项的前/后几行的数据。同一关键字可能会在很多文件中多次出现,因此如何从中挑选出所需的数据就完全取决于对数据的熟悉程度了。

利用 grep 进行正文匹配也存在一个问题,由于打开的设备文件非常大,grep

会产生内存不足的问题,因此无法完成整个设备的正文匹配工作。解决这个问题的方法是使用 strings。strings

命令可以将文件中所有可打印字符全部打印出来,因此对于正文匹配的目的来说,它可以很好地实现文本数据的提取工作。

清单9. 使用 strings 提取设备文件中的可打印数据

# time strings /dev/sdb7 > sdb7.strings

real 12m42.386s

user 10m44.140s

sys 1m42.950s

# grep " 10:0" sdb7.strings

10:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,

清单 9 中的例子使用 strings 将 /dev/sdb7

中的可打印字符提取出来,并重定向到一个文本文件中,此后就可以使用任何文本编辑工具或正文匹配工具从这个文件中寻找自己的数据了。不过扫描磁盘需要的时间可能会比较长,在上面这个例子中,扫描

20GB 的分区大概需要 13 分钟。

a4c26d1e5885305701be709a3d33442f.png

a4c26d1e5885305701be709a3d33442f.png

ext3 文件系统中恢复删除文件的方法 2:提前备份元数据

利用 grep 或 strings

对磁盘数据进行正文匹配,这种方法有一定的局限性:它只是对文本文件的恢复比较有用,这还要依赖于用户对数据的熟悉程度;而对于二进制文件来说,除非有其他备份,否则要通过这种正文匹配的方式来恢复文件,几乎是不可能完成的任务。然而,如果没有其他机制的辅助,在

ext3

文件系统中,这却几乎是唯一可以尝试用来恢复数据的方法了。究其原因是因为,这种方法仅仅是一种亡羊补牢的做法,而所需要的一些关键数据已经不存在了,因此恢复文件就变得愈发困难了。不过,索引节点中的这些关键信息在删除文件之前肯定是存在的。

受本系列文章介绍过的 debugfs 和 libundel 的启发,我们可以对 libundel

进行扩充,使其在删除文件的同时,除了记录文件名、磁盘设备、索引节点信息之外,把文件大小、数据块位置等重要信息也同时记录下来,这样就可以根据日志文件中的元数据信息完美地恢复文件了。

为了实现以上想法,我们需要对 libundel 的实现进行很大的调整,下面介绍所使用的几个关键的函数。

清单10. get_bmap 函数的实现

static unsigned long get_bmap(int fd, unsigned long block)

{

int ret;

unsigned int b;

b = block;

ret = ioctl(fd, FIBMAP, &b);

if (ret < 0)

{

if (errno == EPERM)

{

if (f)

{

{

fprintf(f, "No permission to use FIBMAP ioctl.\n");

fflush(f);

}

}

return 0;

}

}

return b;

}

get_bmap 函数是整个改进的基础,其作用是返回磁盘上保存指定文件的某块数据使用的数据块的位置,这是通过 ioctl 的

FIBMAP 命令来实现的。ioctl 提供了一种在用户空间与内核进行交互的便捷机制,在内核中会通过调用

f_op->ioctl() 进行处理。对于 FIBMAP 命令来说,会由 VFS 调用

f_op->bmap() 获取指定数据块在磁盘上的块号,并将结果保存到第 3

个参数指向的地址中。

清单11. get_blocks 函数的实现

int get_blocks(const char *filename)

{

#ifdef HAVE_FSTAT64

struct stat64 fileinfo;

#else

struct stat fileinfo;

#endif

int bs;

long fd;

unsigned long block, last_block = 0, first_cblock = 0, numblocks, i;

long bpib;

char cblock_list[256];

char pwd[PATH_MAX];

if (NULL != filename)

{

if (__lxstat(3, filename, &fileinfo))

fileinfo.st_ino = 0;

if (!realpath(filename, pwd))

pwd[0] = '\0';

}

#ifdef HAVE_OPEN64

fd = open64(filename, O_RDONLY);

#else

fd = open(filename, O_RDONLY);

#endif

if (fd < 0) {

fprintf(stderr, "cannot open the file of %s\n", filename);

return -1;

}

if (ioctl(fd, FIGETBSZ, &bs) < 0) {

perror("FIGETBSZ");

close(fd);

return -1;

}

bpib = bs / 4;

numblocks = (fileinfo.st_size + (bs-1)) / bs;

sprintf(block_list, "%ld,%ld::%ld::%ld::%ld::",

(long) (fileinfo.st_dev & 0xff00) / 256,

(long) fileinfo.st_dev & 0xff,

(long) fileinfo.st_ino,

(long) fileinfo.st_size, (long)bs);

for (i=0; i < numblocks; i++) {

block = get_bmap(fd, i);

if (last_block == 0) {

first_cblock = block;

}

if (last_block && (block != last_block +1) ) {

sprintf(cblock_list, "(%ld-%ld):%ld-%ld,",

i-(last_block-first_cblock)-1, i-1, first_cblock, last_block);

strcat(block_list, cblock_list);

first_cblock = block;

}

if (i == numblocks - 1 ) {

if (last_block == 0) {

sprintf(cblock_list, "(%ld-%ld):%ld-%ld", i, i, first_cblock, block);

}

else {

sprintf(cblock_list, "(%ld-%ld):%ld-%ld",

i-(last_block-first_cblock)-1, i, first_cblock, block);

}

strcat(block_list, cblock_list);

}

last_block = block;

}

sprintf(cblock_list, "::%s", pwd[0] ? pwd : filename);

strcat(block_list, cblock_list);

close(fd);

return 0;

}

get_blocks

函数的作用是遍历文件包含的每个数据块,获取它们在磁盘上保存的数据块位置。为了保证日志文件中的数据尽量精简,数据块位置会按照本身的连续情况被划分成一个个的连续块域,每个都记录为下面的形式:(文件中的起始块号-文件中的结束块号):磁盘上的起始数据块号-磁盘上的结束数据块号。

自然,get_blocks 函数应该是在用户调用删除文件的系统调用时被调用的,这些系统调用的实现也应该进行修改。清单 12

给出了 remove 库函数修改后的例子。

清单12. remove 库函数的实现

int remove(const char *pathname)

{

int err;

int (*removep)(char *) = dlsym(RTLD_NEXT, "remove");

err = get_blocks(pathname);

if (err < 0)

{

fprintf(stderr, "error while reading blocks from %s\n", pathname);

}

err = (*removep)((char *) pathname);

if (err) return err;

if (f)

{

fprintf(f, "%s\n", block_list);

fflush(f);

}

return err;

}

现在,记录元数据信息的日志文件 /var/e2undel/e2undel 如清单 13 所示:

清单13. 修改后的 /var/e2undel/e2undel 样例文件

8,23::48865::13690::4096::(0-3):106496-106499::/tmp/test/apprentice.c

8,23::48867::19665::4096::(0-4):106528-106532::/tmp/test/ascmagic.c

8,23::48872::1036::4096::(0-0):106545-106545::/tmp/test/compactlog.c

8,23::48875::31272::4096::(0-7):106596-106603::/tmp/test/e2undel.c

8,23::48878::1077::4096::(0-0):106616-106616::/tmp/test/file.c

8,23::48880::4462::4096::(0-1):106618-106619::/tmp/test/find_del.c

8,23::48885::2141::4096::(0-0):106628-106628::/tmp/test/is_tar.c

8,23::48887::6540::4096::(0-1):106631-106632::/tmp/test/libundel.c

8,23::48890::8983::4096::(0-2):106637-106639::/tmp/test/log.c

8,23::48897::13117::4096::(0-3):106663-106666::/tmp/test/softmagic.c

8,23::48866::10485760::4096::(0-11):108544-108555,(12-1035):108557-109580,\

(1036-2059):109583-110606,(2060-2559):110608-111107::/tmp/test/testfile.10M

8,23::48866::7169::4096::(1-1):129024-129024::/tmp/test/hole

8,23::48865::21505::4096::(1-1):129025-129025,(5-5):129026-129026::/tmp/test/hole2

文件名前所增加的 3 项分别为文件大小、块大小和数据块列表。

当然,为了能够利用 /var/e2undel/e2undel 中记录的信息恢复文件,e2undel

的对应实现也必须相应地进行修改。详细实现请参看本文下载部分给出的补丁,在此不再详述。修改后的 libundel

的用法与原来完全相同,详细介绍请参看本系列文章的第 3 部分。

a4c26d1e5885305701be709a3d33442f.png

a4c26d1e5885305701be709a3d33442f.png

ext3 文件系统中恢复删除文件的方法 3:修改 ext3 实现

利用 libundel 方法的确可以完美地恢复删除文件,但是这毕竟是一种治标不治本的方法,就像是本系列文章第 3

部分中所介绍的一样,这要依赖于环境变量 LD_PRELOAD

的设置。如果用户在删除文件之前,并没有正确设置这个环境变量,那么对应的删除操作就不会记录到日志文件

/var/e2undel/e2undel 中,因此也就无从恢复文件了。

还记得在本系列文章的第 2 部分中我们是如何通过修改 Linux 内核来支持大文件的删除的 吗?采用同样的思路,我们也可以修改

ext3 的实现,使其支持文件的恢复,这样本系列前面文章中介绍的工具就都可以在 ext3 文件系统上正常使用了。

总结一下,在删除文件时,内核中执行的操作至少包括:

在块位图中将该文件所占用的数据块标识为可用状态。

在索引节点位图中将该文件所占用的索引节点标识为可用状态。

将该文件索引节点中的硬链接数目设置为 0。

清空间接索引节点中的数据.

清空 i_block 数组中各个成员中的数据。

将索引节点中的文件大小(i_size)和占用块数(i_blocks)设置为 0。

将该文件索引节点中的删除时间设置为当前时间。

将父目录项中该文件对应项中的索引节点号设置为 0,并扩展前一项,使其包含该项所占用的空间。

其中步骤 5 和 6 只适用于 ext3 文件系统,在 ext2 文件系统中并不需要执行。在 ext3

文件系统的实现中,它们分别是由 fs/ext3/inode.c 中的 ext3_delete_inode 和

ext3_truncate 函数实现的:

清单14. ext3_delete_inode 函数实现

182 void ext3_delete_inode (struct inode * inode)

183 {

184 handle_t *handle;

185

186 truncate_inode_pages(&inode->i_data, 0);

187

188 if (is_bad_inode(inode))

189 goto no_delete;

190

191 handle = start_transaction(inode);

192 if (IS_ERR(handle)) {

193

198 ext3_orphan_del(NULL, inode);

199 goto no_delete;

200 }

201

202 if (IS_SYNC(inode))

203 handle->h_sync = 1;

204 inode->i_size = 0;

205 if (inode->i_blocks)

206 ext3_truncate(inode);

207

215 ext3_orphan_del(handle, inode);

216 EXT3_I(inode)->i_dtime = get_seconds();

217

218

225 if (ext3_mark_inode_dirty(handle, inode))

226

227 clear_inode(inode);

228 else

229 ext3_free_inode(handle, inode);

230 ext3_journal_stop(handle);

231 return;

232 no_delete:

233 clear_inode(inode);

234 }

清单15. ext3_truncate 函数实现

2219 void ext3_truncate(struct inode *inode)

2220 {

2221 handle_t *handle;

2222 struct ext3_inode_info *ei = EXT3_I(inode);

2223 __le32 *i_data = ei->i_data;

2224 int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);

2225 struct address_space *mapping = inode->i_mapping;

2226 int offsets[4];

2227 Indirect chain[4];

2228 Indirect *partial;

2229 __le32 nr = 0;

2230 int n;

2231 long last_block;

2232 unsigned blocksize = inode->i_sb->s_blocksize;

2233 struct page *page;

2234

...

2247 if ((inode->i_size & (blocksize - 1)) == 0) {

2248

2249 page = NULL;

2250 } else {

2251 page = grab_cache_page(mapping,

2252 inode->i_size >> PAGE_CACHE_SHIFT);

2253 if (!page)

2254 return;

2255 }

2256

2257 handle = start_transaction(inode);

2258 if (IS_ERR(handle)) {

2259 if (page) {

2260 clear_highpage(page);

2261 flush_dcache_page(page);

2262 unlock_page(page);

2263 page_cache_release(page);

2264 }

2265 return;

2266 }

2267

2268 last_block = (inode->i_size + blocksize-1)

2269 >> EXT3_BLOCK_SIZE_BITS(inode->i_sb);

2270

2271 if (page)

2272 ext3_block_truncate_page(handle, page, mapping, inode->i_size);

2273

2274 n = ext3_block_to_path(inode, last_block, offsets, NULL);

2275 if (n == 0)

2276 goto out_stop;

...

2287 if (ext3_orphan_add(handle, inode))

2288 goto out_stop;

2289

...

2297 ei->i_disksize = inode->i_size;

...

2303 mutex_lock(&ei->truncate_mutex);

2304

2305 if (n == 1) {

2306 ext3_free_data(handle, inode, NULL, i_data+offsets[0],

2307 i_data + EXT3_NDIR_BLOCKS);

2308 goto do_indirects;

2309 }

2310

2311 partial = ext3_find_shared(inode, n, offsets, chain, &nr);

2312

2313 if (nr) {

2314 if (partial == chain) {

2315

2316 ext3_free_branches(handle, inode, NULL,

2317 &nr, &nr+1, (chain+n-1) - partial);

2318 *partial->p = 0;

2319

2323 } else {

2324

2325 BUFFER_TRACE(partial->bh, "get_write_access");

2326 ext3_free_branches(handle, inode, partial->bh,

2327 partial->p,

2328 partial->p+1, (chain+n-1) - partial);

2329 }

2330 }

2331

2332 while (partial > chain) {

2333 ext3_free_branches(handle, inode, partial->bh, partial->p + 1,

2334 (__le32*)partial->bh->b_data+addr_per_block,

2335 (chain+n-1) - partial);

2336 BUFFER_TRACE(partial->bh, "call brelse");

2337 brelse (partial->bh);

2338 partial--;

2339 }

2340 do_indirects:

2341

2342 switch (offsets[0]) {

2343 default:

2344 nr = i_data[EXT3_IND_BLOCK];

2345 if (nr) {

2346 ext3_free_branches(handle, inode, NULL, &nr, &nr+1, 1);

2347 i_data[EXT3_IND_BLOCK] = 0;

2348 }

2349 case EXT3_IND_BLOCK:

2350 nr = i_data[EXT3_DIND_BLOCK];

2351 if (nr) {

2352 ext3_free_branches(handle, inode, NULL, &nr, &nr+1, 2);

2353 i_data[EXT3_DIND_BLOCK] = 0;

2354 }

2355 case EXT3_DIND_BLOCK:

2356 nr = i_data[EXT3_TIND_BLOCK];

2357 if (nr) {

2358 ext3_free_branches(handle, inode, NULL, &nr, &nr+1, 3);

2359 i_data[EXT3_TIND_BLOCK] = 0;

2360 }

2361 case EXT3_TIND_BLOCK:

2362 ;

2363 }

2364

2365 ext3_discard_reservation(inode);

2366

2367 mutex_unlock(&ei->truncate_mutex);

2368 inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;

2369 ext3_mark_inode_dirty(handle, inode);

2370

2371

2375 if (IS_SYNC(inode))

2376 handle->h_sync = 1;

2377 out_stop:

2378

2385 if (inode->i_nlink)

2386 ext3_orphan_del(handle, inode);

2387

2388 ext3_journal_stop(handle);

2389 }

清单 14 和 15 列出的 ext3_delete_inode 和 ext3_truncate

函数实现中,使用黑体标出了与前面提到的问题相关的部分代码。本文下载部分给出的针对 ext3

文件系统的补丁中,包括了对这些问题的一些修改。清单 16 给出了使用这个补丁之后在 ext3 文件系统中删除文件的一个例子。

清单16. 利用 debugfs 工具查看删除文件的信息

# ./creatfile.sh 90 testfile.90K

# ls -li

total 116

12 -rwxr-xr-x 1 root root 1407 2008-01-23 07:25 creatfile.sh

11 drwx------ 2 root root 16384 2008-01-23 07:23 lost+found

13 -rw-r--r-- 1 root root 92160 2008-01-23 07:25 testfile.90K

# rm -f testfile.90K

# cd ..

# umount /tmp/test

# debugfs /dev/sda3

debugfs 1.40.2 (12-Jul-2007)

debugfs: lsdel

Inode Owner Mode Size Blocks Time deleted

0 deleted inodes found.

debugfs: stat <13>

Inode: 13 Type: regular Mode: 0644 Flags: 0x0 Generation: 3438957668

User: 0 Group: 0 Size: 92160

File ACL: 0 Directory ACL: 0

Links: 1 Blockcount: 192

Fragment: Address: 0 Number: 0 Size: 0

ctime: 0x47967b69 -- Wed Jan 23 07:25:29 2008

atime: 0x47967b68 -- Wed Jan 23 07:25:28 2008

mtime: 0x47967b69 -- Wed Jan 23 07:25:29 2008

BLOCKS:

(0-11):22528-22539, (IND):22540, (12-22):22541-22551

TOTAL: 24

debugfs:

在清单 16 中,我们首先创建了一个大小为 90KB 的测试文件,删除该文件并使用 debugfs

查看对应的设备文件时,stat <13>

显示的信息说明数据块的位置信息都仍然保存在了索引节点中,不过 lsdel

命令并未找到这个索引节点。因此下载部分中给出的补丁尚不完善,感兴趣的读者可以自行开发出更加完善的补丁。

a4c26d1e5885305701be709a3d33442f.png

a4c26d1e5885305701be709a3d33442f.png

小结

本文首先介绍了 ext3 文件系统作为一种日志文件系统的一些特性,然后针对这些特性介绍了 3

种恢复删除文件的方法:正文匹配、利用 libundel 和修改内核中 ext3 文件系统的实现,并给出了对 libundel 和

ext3 文件系统实现的补丁程序。应用本文介绍的方法,读者可以最大程度地恢复 ext3

文件系统中删除的文件。本系列的后续文章将继续探讨在其他文件系统上如何恢复已删除的文件,并着手设计更加通用的文件备份方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值