标题: Debian中ext4magic 0.3.2的BUG修正
创建: 2019-08-06 18:20 更新: 链接: https://scz.617.cn/unix/201908061820.txt
ext4magic是一款针对ext3、ext4的文件恢复工具,本文只测ext4,未测ext3,二者 有区别。文中源码对应x86版本,x64版本应该类似。
Debian中ext4magic 0.3.2某些命令会触发SIGSEGV。
$ mount -o remount,ro /dev/sdb1 /mnt/1
或
$ umount /dev/sdb1
$ aptitude install ext4magic $ ext4magic -V ext4magic version : 0.3.2 libext2fs version : 1.45.3 CPU is little endian. Expert Mode is activ
$ ext4magic /dev/sdb1 -l Filesystem in use: /dev/sdb1
Using internal Journal at Inode 8 Inode 2 is allocated Segmentation fault
$ ext4magic /dev/sdb1 -f bin -T -x Filesystem in use: /dev/sdb1
Using internal Journal at Inode 8 Segmentation fault
上述两条命令最终触发SIGSEGV的地方是同一处:
local_block_iterate3 () { / * block.c:702 / local_ext2fs_extent_free( handle ); }
从调用栈回溯看,该函数最终调用free()释放内存,堆破坏后触发SIGSEGV。检查 local_block_iterate3(),发现Debian修正其他BUG时引入新BUG:
local_block_iterate3 () { / * block.c:617 * * FIXME : Debian Bug #802089 (temporary work around) * * ctx.errcode = local_ext2fs_extent_open( fs, inode, &handle ); / ctx.errcode = ext2fs_extent_open2( fs, 0, &inode, &handle ); ... / * block.c:702 / local_ext2fs_extent_free( handle ); }
从源码中的注释看,为了修正"Debian Bug #802089",将原来的 local_ext2fs_extent_open()换成ext2fs_extent_open2(),但与之配对的 local_ext2fs_extent_free()并未同步换成ext2fs_extent_free()。这类似于用new 分配内存,用free()释放内存,必然导致堆破坏。
参看:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802089
解决办法就是将"block.c:702"处的local_ext2fs_extent_free()改成 ext2fs_extent_free()。
与之类似,另一处触发SIGSEGV的地方是"inode.c:213":
local_dump_extents () { / * inode.c:115 * * FIXME : Debian Bug #802089 (temporary work around) * * errcode = local_ext2fs_extent_open( current_fs, inode, &handle ); / errcode = ext2fs_extent_open2( current_fs, 0, inode, &handle ); ... / * inode.c:213 */ local_ext2fs_extent_free( handle ); }
BUG起因与"block.c:702"完全一致。解决办法就是将"inode.c:213"处的 local_ext2fs_extent_free()改成ext2fs_extent_free()。
$ ext4magic /dev/sdb1 -f / -l -a 1564732393 -b 1564733473
只能看到符号链接,看不到被删除的文件。这个BUG的根源在于,对于被删除文件,函 数get_undel_inode()返回NULL。函数get_last_undel_inode()有类似BUG。
/ * lookup_local.c:615 * * inode是in/out型参数,get_dir3()会填写它。对于被删除文件,inode被清零。 / d_list = get_dir3( inode, dir->dir_inode, lp->inode_nr, dir->pathname, lp->filename, t_after, t_before, flag ); if ( d_list ) { recursion = 1; if ( flag & LOST_DIR_SEARCH ) recursion = check_find_dir( des_dir, lp->inode_nr, dir->pathname, lp->filename ); if ( recursion ) { lookup_local(des_dir, d_list, t_after, t_before, flag); } } else { switch ( flag & REC_FILTER ) { ... case LIST_STATUS : / * lookup_local.c:641 * * 对于被删除文件,allocated被赋0,导致"-l"输出不包含它们 / allocated = check_file_recover( inode ); if ( allocated ) printf ( "%5u%% %c%s%s%s%c\n", allocated,c, dir->pathname, ( ( strlen( dir->pathname ) > 0 ) && strcmp( dir->pathname, "/" ) ) ? "/" : "", lp->filename, c );
/ * recover.c:587 / int check_file_recover ( struct ext2_inode *inode ) { int retval = -1; struct alloc_stat stat;
/*
* recover.c:591
*
* no type flag
*
* 对于被删除文件,inode全零,流程在此返回0,导致"-l"输出不包含它们
*/
if ( !( inode->i_mode & LINUX_S_IFMT ) )
return 0;
/ * lookup_local.c:504 / struct dir_list_head_t * get_dir3 ( struct ext2_inode d_inode, ext2_ino_t path_ino, ext2_ino_t ino, char path, char name, __u32 t_after, __u32 t_before, int options ) { int retval; int flags; struct ext2_inode inode; struct ring_buf i_list = NULL; r_item item = NULL;
...
/*
* lookup_local.c:517
*
* d_inode在此被清零
*/
if ( d_inode )
memset( d_inode, 0 , current_fs->super->s_inode_size );
i_list = ( struct ring_buf * )get_j_inode_list( current_fs->super, ino );
if ( !i_list )
return NULL;
...
/*
* lookup_local.c:525
*
* 对于被删除文件,item被赋NULL,导致后面的memcpy()不会发生
*/
item = get_undel_inode( i_list, t_after, t_before );
if ( item && item->inode )
{
inode = ( struct ext2_inode * )item->inode;
if ( d_inode )
memcpy( d_inode, inode, current_fs->super->s_inode_size );
/ * inode.c:599 * * 对于被删除文件,该函数返回NULL / r_item * get_last_undel_inode ( struct ring_buf *buf )
/ * inode.c:631 * * 对于被删除文件,该函数返回NULL / r_item * get_undel_inode ( struct ring_buf *buf, __u32 after, __u32 before )
修改inode.c中get_undel_inode()、get_last_undel_inode()结尾的"return NULL;" 为"return item;",可以解决"-l"的BUG。
即使这些都修正了,Debian中ext4magic 0.3.2还有其他BUG。
extundelete是另一个工具,比ext4magic老点,本来我都不想用它了,挣扎中发现它 的"--restore-inode"挺好用。
$ aptitude install extundelete $ extundelete -V extundelete version 0.2.4 libext2fs version 1.45.3 Processor is little endian.
建议,对于ext4,先用ext4magic看被删除文件的inode,再用extundelete恢复。
上面说的是定向恢复,不考虑"ext4magic -m"、"extundelete --restore-all"之类 的挣扎。