标题: 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"之类 的挣扎。