diff --git a/tools/fs/ext4_recover/CMakeLists.txt b/tools/fs/ext4_recover/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b382b0f01d6eba1312a331164046a4ea6df75681 --- /dev/null +++ b/tools/fs/ext4_recover/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 2.8) +project(ext4_recover) + +add_executable(ext4recover src/ext4recover.c) + +find_library(EXT2FS ext2fs) +find_library(COM_ERR com_err) +target_link_libraries(ext4recover ${EXT2FS}) +target_link_libraries(ext4recover ${COM_ERR}) \ No newline at end of file diff --git a/tools/fs/ext4_recover/Readme.md b/tools/fs/ext4_recover/Readme.md new file mode 100644 index 0000000000000000000000000000000000000000..6d44d6ec63390cf2dd70bd73aec62679ca44aac2 --- /dev/null +++ b/tools/fs/ext4_recover/Readme.md @@ -0,0 +1,135 @@ +# 项目介绍 +ext4文件系统误删除文件恢复工具 + +## 支持的平台 + +Linux ext4 + +# 快速上手 Getting Started + +## 环境要求 +Linux + +## 前提条件 + + +## 操作步骤 +### 使用方法 +``` +ext4recover /dev/sdx +# 假设/dev/sdx是要恢复数据的设备,执行上述命令后,会将能恢复的已删除文件恢复到RECOVER子目录下。 +``` +### 编译方法 +``` +yum install -y e2fsprogs-devel libcom_err-devel +cd src +make +``` + +## 常见问题 +#### 原理: +ext4在删除包含多级extent的文件时,会调用ext4_ext_rm_idx 删除下一级, 上一级本身只是eh_entries减一, +但是block位置没修改,同时inode外部的extent块信息清空后没有落盘。所以包含多级extent的文件删除后可以利用这些信息恢复。 +只包含一级extent时,只调用ext4_ext_rm_leaf,会修改对应index的eh_entries, 并且每一个entry的block位置被置0, 长度置0, +所以只包含一级extent的文件无法恢复. + +如果文件不存在碎片,单文件大于496M就会使用多级extent, 能恢复。 +当存在碎片的情况,文件就算很小也可能占用4个以上extent, 因此这些高度碎片化的小文件也能恢复 + +背景信息:ext4_ext_rm_idx删除的外部extent数据没落盘其实算是一个bug, 只不过当前内核社区还没修复, +brookxu同学提了补丁:https://lkml.org/lkml/2020/3/5/1248 + +本工具原始版本是zorrozou所写,curuwang从e2fsprogs代码中剥离并补充原理说明。 + +为什么说是bug呢? 因为实际上内核代码里面已经将叶子extent清空了( +rm后,直接dd读底层的extent,发现已清0, 但是dd使用iflag=direct绕过pagecache从块设备发现信息有残留, +证明只是把页面写了没有标记未脏,所以没落盘) + +文件被删除后, inode里面的信息发生了如下变化: +1. 文件大小,文件链接数变为0 +2. 文件dtime,ctime,atime,mtime被设置为删除时间 +3. 上述的extent信息变化 +4. 其他... + + +## 行为准则 +代码层面需严格按照开源治理的要求。 + +### 一句话介绍 + +根据腾讯代码规范,评估开源项目和协同Oteam项目的代码规范度,提升代码可读性,降低技术交流成本。 + +### html + +1. 编码:所有编码均采用xhtml,标签必须闭合,属性值用双引号包括,编码统一为utf-8。 + +2. 语义化:语义化html,正确使用标签,充分利用无兼容性问题的html自身标签。 + +3. 文件命名:命名以中文命名,依实际模块命名,如同一模块以_&title&_来组合命名,以方便添加功能时查找对应页面。 + +4. 文件头部head的内容 + - title,需要添加标题 + - 编码charset=UTF-8 + - 缓存: + - Content=’-1’表示立即过期。 + - 添加description、keywords内容 + - Robots content部分有四个指令选项:index、noindex、follow、nofollow,用‘,’分隔,如: + - 在head标签内引入css文件,有助于页面渲染 + - 引入样式文件或JavaScript文件时, 须略去默认类型声明. + - 页脚引入javascript文件 + - 连接地址标签:书写链接地址时,避免重定向,如href=”http://www.example.com/”,需在地址后面加‘/’ + - 尽可能减少div嵌套,如:根据重要性使用h1-6标签,段落使用p,列表使用ul,内联元素中不可嵌套块级元素,为含有描述性表单元素(input,tetarea)添加label + +5. 图片 + - 能以背景形式呈现的图片,尽量写入css样式中 + - 区分作为内容的图片和作为背景的图片,作为背景的图片采用Css sprite技术,放在一张大图里 + - 重要图片必须加上alt属性,给重要的元素和截断的元素加上title + +6. 注释:给区块代码及重要功能加上注释,方便后台添加功能 +7. 转义字符:特殊符号使用转义字符 +8. 页面架构时考虑扩展性 + +### CSS + +1. 编码统一为utf-8 + +2. Class与id的使用: + - Id:具有唯一性,是父级的,用于标识页面上的特定元素,如header/footer/wrapper/left/right之类 + - Class:可以重复使用,是子级的,可用于页面上任意多个元素 + - 命名:以小写英文字母、数字、下划线组合命名,避免使用中文拼音,尽量使用简易的单词组合,避免使用拼音,采用驼峰命名法和划线命名法,提高可读性,如:dropMenu、sub_nav_menu、drop-menu等。为JavaScript预留钩子的命名, 以 js_ 起始, 比如:js_hide, js_show + +3. 书写代码前,考虑样式重复利用率,充分利用html自身属性及样式继承原理减少代码量,代码建议单行书写,利于后期管理 + +4. 图片: + - 命名:小写英文字母、数字、_ 的组合,使用有意义的名称或英文简写,最好不要使用汉语拼音,区分大写字。 + - 使用sprite技术, 减小http请求,sprite按模块制作 +5. 书写顺序:保证同类型属性写在一起,一般遵循布局定位属性–>自身属性–>文本属性–>其他属性的书写格式 + - 书写顺序规则 + - 定位属性(比如:display, position, float, clear, visibility, table-layout等) + - 自身属性(比如:width, height, margin, padding, border等) + - 文本属性(比如:font, line-height, text-align, text-indent, vertical-align等) + - 其他属性(比如:color, background, opacity, cursor,content, list-style, quotes等) + - 缩进:统一使用tab进行缩进 + +6. 样式表中中文字体名,最好转成unicode码,以避免编码错误时乱码。 +7. 减少影响性能的属性,如:position,float +8. 为大区块样式添加注释,小区块适量注释。 + +### Javascript部分 + +1. 代码规范须通过腾讯云ESLint的设定集合的规范 +2. 代码安全须通过啄木鸟(定制规则) +codecc(扫描)+邮件通知+工蜂Git(修复)的检查 +3. 圈复杂度须通过CodeCC的工具的检查,每个函数圈复杂度需小于等于20 + + + +## 如何加入 +联系curuwang申请权限即可 + +## 团队介绍 +云服务器技术支持团队,主要对接CVM/CBS/CFS/云API/黑石等计算产品的专业技术支持工作。 +以系统技术为支撑,对内跟进最终产品问题促进产品改进,对外为提供专业技术服务解决客户问题。 + +同时,团队还拥有大量与客户打交道经验丰富的自营、腾云悦智的同事。 +他们深扎与客户,了解客户的需求、行业的特点、不同的视角 +给予团队更多元的思考方向与良好的服务口碑。 \ No newline at end of file diff --git a/tools/fs/ext4_recover/src/Makefile b/tools/fs/ext4_recover/src/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..cbdca72996a2b95f96aac4b414a494c881254aba --- /dev/null +++ b/tools/fs/ext4_recover/src/Makefile @@ -0,0 +1,10 @@ +all: ext4recover bgrep_demo + +ext4recover: ext4recover.c + $(CC) -Werror -o $@ $< -lcom_err -lext2fs + +bgrep_%: bgrep_%.c + $(CC) -Werror -o $@ $< + +clean: + rm -f ext4recover bgrep_demo bgrep_non_zero diff --git a/tools/fs/ext4_recover/src/bgrep.c b/tools/fs/ext4_recover/src/bgrep.c new file mode 100644 index 0000000000000000000000000000000000000000..e59457187591c8d3144590a58a3f1dd919fdad31 --- /dev/null +++ b/tools/fs/ext4_recover/src/bgrep.c @@ -0,0 +1,51 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#define BUFLEN 4096 +int main(int argc, char **argv){ + int needle_len; + unsigned char *needle; + char buf[BUFLEN]; + char * f; + + if(argc < 4){ + printf("Usage: %s file_path bytes_len bytes_str\n", argv[0]); + exit(1); + } + setlinebuf(stdout); + + f = argv[1]; + needle_len = atoi(argv[2]); + needle = argv[3]; + + int fd = open(f, O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + printf("checking match bytes with file %s\n", f); + printf("bytes len:%d\n", needle_len); + printf("bytes in hex: "); + int i; + for(i=0; i +#include +#include +#include +#include +#include +#include + +#define BLOCK_SIZE 4096 +#define MAX_VALID_BLOCKS 131072 +#define RECOVER_DIR "./RECOVER" + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "Usage: %s file\n", argv[0]); + exit(1); + } + printf("creating recover directory '%s'\n", RECOVER_DIR); + if (mkdir(RECOVER_DIR, 0700) < 0) { + perror("failed to create recover dir:"); + return 1; + } + // fdx + char *match_fdx_start = "\x3f\xd7\x6c\x17\x1dLucene50StoredFieldsHighIndex"; + int match_fdx_len = 34; + // fdt + char *match_fdt1_start = "\x3f\xd7\x6c\x17\x1cLucene50StoredFieldsFastData"; + int match_fdt1_len = 33; + char *match_fdt2_start = "\x3f\xd7\x6c\x17\x1cLucene50StoredFieldsHighData"; + int match_fdt2_len = 33; + + char *match_fnm_start = "\x3f\xd7\x6c\x17\x12Lucene60FieldInfos"; + int match_fnm_len = 23; + char *match_si_start = "\x3f\xd7\x6c\x17\x13Lucene70SegmentInfo"; + int match_si_len = 24; + char *match_end = "\xc0\x28\x93\xe8\x00\x00\x00\x00"; + char match_end_len = 8; + char *f = argv[1]; + printf("recovering from %s\n", f); + + int fd = open(f, O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + size_t nread, blk_number; + char buf[BLOCK_SIZE]; + char wfilename[100]; + int wfd = 0; + char *p; + char *pend; + int started = 0; + size_t nblocks = 0; + while (nread = read(fd, buf, BLOCK_SIZE)) { + p = buf; + if (!started) { + if (((p = memmem(buf, nread, match_fdt1_start, match_fdt1_len)) == buf) || + ((p = memmem(buf, nread, match_fdt2_start, match_fdt2_len)) == buf)) { + sprintf(wfilename, "%s/%lu.fdt", RECOVER_DIR, blk_number); + } + /* + else if( (p=memmem(buf, nread, match_fdx_start, match_fdx_len)) == buf){ + sprintf(wfilename, "./recover/%lu.fdx", blk_number); + }else if( (p=memmem(buf, nread, match_fnm_start, match_fnm_len)) == buf){ + sprintf(wfilename, "./recover/%lu.fnm", blk_number); + }else if( (p=memmem(buf, nread, match_si_start, match_si_len)) == buf){ + sprintf(wfilename, "./recover/%lu.si", blk_number); + } + */ + + if (p == buf) { + started = 1; + nblocks = 0; + printf("found match file begin at block:%-10lu\n", blk_number); + printf("dump file to %s... begin\n", wfilename); + fflush(stdout); + wfd = open(wfilename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (wfd < 0) { + perror("open"); + return 1; + } + } + } + + if (started) { + pend = memmem(buf, nread, match_end, match_end_len); + // found end str + if (pend != NULL) { + pend = pend + match_end_len + 8; + write(wfd, buf, pend - buf); + printf("found match end at block:%-10lu\n", blk_number); + close(wfd); + started = 0; + } else if (nblocks > MAX_VALID_BLOCKS) { + write(wfd, buf, nread); + printf("reach MAX_VALID_BLOCKS at block:%-10lu\n", blk_number); + close(wfd); + started = 0; + } else { + write(wfd, buf, nread); + nblocks += 1; + } + } + blk_number += 1; + } + close(fd); +} + diff --git a/tools/fs/ext4_recover/src/bgrep_es_cfs_cfe.c b/tools/fs/ext4_recover/src/bgrep_es_cfs_cfe.c new file mode 100644 index 0000000000000000000000000000000000000000..0b440338c4fe0947a3387022f97f7d4a111132f1 --- /dev/null +++ b/tools/fs/ext4_recover/src/bgrep_es_cfs_cfe.c @@ -0,0 +1,125 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK_SIZE 4096 +#define MAX_VALID_BLOCKS 131072 +#define RECOVER_DIR "./RECOVER" + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "Usage: %s file\n", argv[0]); + exit(1); + } + printf("creating recover directory '%s'\n", RECOVER_DIR); + if (mkdir(RECOVER_DIR, 0700) < 0) { + perror("failed to create recover dir:"); + return 1; + } + // fdx + char *match_fdx_start = "\x3f\xd7\x6c\x17\x1dLucene85FieldsIndexMeta"; + int match_fdx_len = 28; + // fdt + char *match_fdt1_start = "\x3f\xd7\x6c\x17\x1cLucene87StoredFieldsFastData"; + int match_fdt1_len = 33; + char *match_fdt2_start = "\x3f\xd7\x6c\x17\x1cLucene87StoredFieldsHighData"; + int match_fdt2_len = 33; + char *match_fdt3_start = "\x3f\xd7\x6c\x17\x1cLucene87StoredFieldsZstandard"; + int match_fdt3_len = 34; + // cfs + char *match_cfs_start = "\x3f\xd7\x6c\x17\x14Lucene50CompoundData"; + int match_cfs_len = 25; + + // cfe + char *match_cfe_start = "\x3f\xd7\x6c\x17\x17Lucene50CompoundEntries"; + int match_cfe_len = 28; + + char *match_fnm_start = "\x3f\xd7\x6c\x17\x12Lucene60FieldInfos"; + int match_fnm_len = 23; + char *match_si_start = "\x3f\xd7\x6c\x17\x13Lucene86SegmentInfo"; + int match_si_len = 24; + char *match_end = "\xc0\x28\x93\xe8\x00\x00\x00\x00"; + char match_end_len = 8; + char *f = argv[1]; + printf("recovering from %s\n", f); + + int fd = open(f, O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + size_t nread, blk_number; + char buf[BLOCK_SIZE]; + char wfilename[100]; + int wfd = 0; + char *p; + char *pend; + int started = 0; + size_t nblocks = 0; + while (nread = read(fd, buf, BLOCK_SIZE)) { + p = buf; + + if(!started){ + // Check for CFS format + if ((p = memmem(buf, nread, match_cfs_start, match_cfs_len)) == buf) { + sprintf(wfilename, "%s/%lu.cfs", RECOVER_DIR, blk_number); + } + // Check for CFE format + else if ((p = memmem(buf, nread, match_cfe_start, match_cfe_len)) == buf) { + sprintf(wfilename, "%s/%lu.cfe", RECOVER_DIR, blk_number); + } + // Check for other formats like fdt, fdx, fnm, si, etc. (you can uncomment and modify this part) + /* + else if (!started && ((p = memmem(buf, nread, match_fdt1_start, match_fdt1_len)) == buf)) { + sprintf(wfilename, "%s/%lu.fdt", RECOVER_DIR, blk_number); + } + else if (!started && ((p = memmem(buf, nread, match_fdx_start, match_fdx_len)) == buf)) { + sprintf(wfilename, "%s/%lu.fdx", RECOVER_DIR, blk_number); + } + // Add more format checks here... + */ + + if (p == buf) { + started = 1; + nblocks = 0; + printf("found match file begin at block:%-10lu\n", blk_number); + printf("dump file to %s... begin\n", wfilename); + fflush(stdout); + wfd = open(wfilename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (wfd < 0) { + perror("open"); + return 1; + } + } + } + + if (started) { + pend = memmem(buf, nread, match_end, match_end_len); + // found end str + if (pend != NULL) { + pend = pend + match_end_len + 8; + write(wfd, buf, pend - buf); + printf("found match end at block:%-10lu\n", blk_number); + close(wfd); + started = 0; + } else if (nblocks > MAX_VALID_BLOCKS) { + write(wfd, buf, nread); + printf("reach MAX_VALID_BLOCKS at block:%-10lu\n", blk_number); + close(wfd); + started = 0; + } else { + write(wfd, buf, nread); + nblocks += 1; + } + } + blk_number += 1; + } + close(fd); +} + diff --git a/tools/fs/ext4_recover/src/bgrep_es_lucene87.c b/tools/fs/ext4_recover/src/bgrep_es_lucene87.c new file mode 100644 index 0000000000000000000000000000000000000000..b86a055cff23f589d0242372a760763ce9b792f4 --- /dev/null +++ b/tools/fs/ext4_recover/src/bgrep_es_lucene87.c @@ -0,0 +1,134 @@ +/* + * Copyright 2020 Tencent Inc. All rights reserved. + * Author: curuwang@tencent.com +*/ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK_SIZE 4096 +#define MAX_VALID_BLOCKS 131072 +#define RECOVER_DIR "./RECOVER" + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "Usage: %s file\n", argv[0]); + exit(1); + } + printf("creating recover directory '%s'\n", RECOVER_DIR); + if (mkdir(RECOVER_DIR, 0700) < 0) { + perror("failed to create recover dir:"); + return 1; + } + // fdx + char *match_fdx_start = "\x3f\xd7\x6c\x17\x1dLucene85FieldsIndexMeta"; + int match_fdx_len = 28; + // fdt + char *match_fdt1_start = "\x3f\xd7\x6c\x17\x1cLucene87StoredFieldsFastData"; + int match_fdt1_len = 33; + char *match_fdt2_start = "\x3f\xd7\x6c\x17\x1cLucene87StoredFieldsHighData"; + int match_fdt2_len = 33; + char *match_fdt3_start = "\x3f\xd7\x6c\x17\x1cLucene87StoredFieldsZstandard"; + int match_fdt3_len = 34; + // cfs + char *match_cfs_start = "\x3f\xd7\x6c\x17\x14Lucene50CompoundData"; + int match_cfs_len = 25; + // cfe + char *match_cfe_start = "\x3f\xd7\x6c\x17\x17Lucene50CompoundEntries"; + int match_cfe_len = 28; + // fnm + char *match_fnm_start = "\x3f\xd7\x6c\x17\x12Lucene60FieldInfos"; + int match_fnm_len = 23; + // si + char *match_si_start = "\x3f\xd7\x6c\x17\x13Lucene86SegmentInfo"; + int match_si_len = 24; + + char *match_end = "\xc0\x28\x93\xe8\x00\x00\x00\x00"; + char match_end_len = 8; + char *f = argv[1]; + printf("recovering from %s\n", f); + + int fd = open(f, O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + size_t nread, blk_number=0; + char *buf; + char wfilename[100]; + int wfd = 0; + char *p; + char *pend; + int started = 0; + size_t nblocks = 0; + buf = malloc(BLOCK_SIZE); + while (nread = read(fd, buf, BLOCK_SIZE)) { + p = buf; + if (!started) { + if(((p = memmem(buf, nread, match_fdt1_start, match_fdt1_len)) != NULL) || + ((p = memmem(buf, nread, match_fdt2_start, match_fdt2_len)) != NULL) || + ((p = memmem(buf, nread, match_fdt3_start, match_fdt3_len)) != NULL)){ + sprintf(wfilename, "%s/%lu.fdt", RECOVER_DIR, blk_number); + } + /* + else if ((p = memmem(buf, nread, match_cfs_start, match_cfs_len)) == buf) { + sprintf(wfilename, "%s/%lu.cfs", RECOVER_DIR, blk_number); + } + else if ((p = memmem(buf, nread, match_cfe_start, match_cfe_len)) == buf) { + sprintf(wfilename, "%s/%lu.cfe", RECOVER_DIR, blk_number); + } + else if( (p=memmem(buf, nread, match_fdx_start, match_fdx_len)) == buf){ + sprintf(wfilename, "%s/%lu.fdx", RECOVER_DIR, blk_number); + }else if( (p=memmem(buf, nread, match_fnm_start, match_fnm_len)) == buf){ + sprintf(wfilename, "%s/%lu.fnm", RECOVER_DIR, blk_number); + }else if( (p=memmem(buf, nread, match_si_start, match_si_len)) == buf){ + sprintf(wfilename, "%s/%lu.si", RECOVER_DIR, blk_number); + } + */ + + if (p != NULL) { + started = 1; + nread -= p - buf; //buf will start at p + buf = p; + nblocks = 0; + printf("found match file begin at block:%-10lu\n", blk_number); + printf("dump file to %s... begin\n", wfilename); + fflush(stdout); + wfd = open(wfilename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (wfd < 0) { + perror("open"); + return 1; + } + } + } + + if (started) { + pend = memmem(buf, nread, match_end, match_end_len); + // found end str + if (pend != NULL) { + pend = pend + match_end_len + 8; + write(wfd, buf, pend - buf); + printf("found match end at block:%-10lu\n", blk_number); + close(wfd); + started = 0; + } else if (nblocks > MAX_VALID_BLOCKS) { + write(wfd, buf, nread); + printf("reach MAX_VALID_BLOCKS at block:%-10lu\n", blk_number); + close(wfd); + started = 0; + } else { + write(wfd, buf, nread); + nblocks += 1; + } + } + blk_number += 1; + } + close(fd); +} + diff --git a/tools/fs/ext4_recover/src/bgrep_foundationdb.c b/tools/fs/ext4_recover/src/bgrep_foundationdb.c new file mode 100644 index 0000000000000000000000000000000000000000..157076cfde40cbc655875c4b87f8d8f27cad3c3e --- /dev/null +++ b/tools/fs/ext4_recover/src/bgrep_foundationdb.c @@ -0,0 +1,134 @@ +/* + * Copyright 2020 Tencent Inc. All rights reserved. + * Author: curuwang@tencent.com +*/ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK_SIZE 4096 +#define MAX_VALID_BLOCKS 131072 +#define RECOVER_DIR "./RECOVER" + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "Usage: %s [max_file_size_in_bytes]\n", argv[0]); + exit(1); + } + + setlinebuf(stdout); + + int max_file_size = 0; + if (argc == 3 ){ + max_file_size = atoi(argv[2]); + printf("max file size: %d\n", max_file_size); + } + + printf("creating recover directory '%s'\n", RECOVER_DIR); + if (mkdir(RECOVER_DIR, 0700) < 0) { + perror("failed to create recover dir:"); + return 1; + } + // .sqlite + char *m1_start = "FoundationDB100\0"; + char *m1_suffix = ".sqlite"; + int m1_len = 16; + // processId + char *m2_start = "\x01\x00\x01\x70\xa5\x00\xdb\x0f"; + char *m2_suffix = ".processId"; + int m2_len = 8; + //.sqlite-wal + char *m3_start = "\x37\x7f\x06\x82\x00\x2d\xe2\x18\x00\x00\x10"; + char *m3_suffix = ".sqlite-wal"; + int m3_len = 11; + //.db: reeal sqlite v3 + char *m4_start = "SQLite format 3\0"; + char *m4_suffix = ".sqlite.db"; + int m4_len = 16; + + char *f = argv[1]; + printf("recovering from %s\n", f); + + int fd = open(f, O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + size_t nread, blk_number=0; + char buf[BLOCK_SIZE]; + char wfilename[100]; + int wfd = 0; + char *p; + char *pend; + int started = 0; + size_t nblocks = 0; + + uint64_t file_size = 0; + while (nread = read(fd, buf, BLOCK_SIZE)) { + if (!started) { + if ( (p = memmem(buf, nread, m1_start, m1_len)) == buf) { + sprintf(wfilename, "%s/%lu%s", RECOVER_DIR, blk_number, m1_suffix); + file_size = max_file_size; + } + else if ( (p = memmem(buf, nread, m2_start, m2_len)) == buf) { + sprintf(wfilename, "%s/%lu%s", RECOVER_DIR, blk_number, m2_suffix); + file_size = max_file_size; + } + else if ( (p = memmem(buf, nread, m3_start, m3_len)) == buf) { + sprintf(wfilename, "%s/%lu%s", RECOVER_DIR, blk_number, m3_suffix); + file_size = max_file_size; + } + else if ( (p = memmem(buf, nread, m4_start, m4_len)) == buf) { + sprintf(wfilename, "%s/%lu%s", RECOVER_DIR, blk_number, m4_suffix); + uint16_t page_size; + uint32_t page_count; + page_size = __bswap_16(*(uint16_t*)(p+16)); + page_count = __bswap_32(*(uint32_t*)(p+28)); + file_size = (uint64_t)page_count * page_size; + printf("page size %u, page count:%u, file size:%u\n", page_size, page_count, file_size); + if( max_file_size > 0 && file_size > max_file_size){ + file_size = max_file_size; + printf("truncate file to max_file_size:%d\n", max_file_size); + } + } + + if (p == buf) { + started = 1; + nblocks = 0; + printf("found match file begin at block:%-10lu\n", blk_number); + printf("dump file to %s... begin\n", wfilename); + fflush(stdout); + wfd = open(wfilename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (wfd < 0) { + perror("open"); + return 1; + } + } + } + + if (started) { + if((nblocks+1)*BLOCK_SIZE >= file_size){ + size_t to_write = file_size - nblocks*BLOCK_SIZE; + write(wfd, buf, to_write); + printf("nwrite: %lu >= %lu: to_wirte:%lu\n", (nblocks+1)*BLOCK_SIZE, file_size, to_write); + printf("file end at block:%-10lu\n", blk_number); + close(wfd); + started = 0; + } else { + write(wfd, buf, nread); + nblocks += 1; + } + } + blk_number += 1; + } + close(fd); +} + diff --git a/tools/fs/ext4_recover/src/bgrep_non_zero.c b/tools/fs/ext4_recover/src/bgrep_non_zero.c new file mode 100644 index 0000000000000000000000000000000000000000..2c7e7db94f73315e0cf80a5ed2501313fdcad055 --- /dev/null +++ b/tools/fs/ext4_recover/src/bgrep_non_zero.c @@ -0,0 +1,63 @@ +/* + * Copyright 2020 Tencent Inc. All rights reserved. + * Author: curuwang@tencent.com +*/ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK_SIZE 16*1024*1024 +#define MAX_VALID_BLOCKS 131072 +#define RECOVER_DIR "./RECOVER" + +int main(int argc, char **argv) { + if (argc < 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + + setlinebuf(stdout); + + char *f = argv[1]; + size_t block_size = atoi(argv[2]); + printf("checking %s with chunk: %d bytes\n", f, block_size); + + char *zero_block = malloc(block_size); + char *buf = malloc(block_size); + memset(zero_block, '\0', block_size); + + int fd = open(f, O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + + size_t nread, blk_num=0; + ssize_t blk_start= -1; + while (nread = read(fd, buf, block_size)) { + if(memcmp(buf, zero_block, block_size) != 0){ + blk_start = blk_start > -1 ? blk_start : blk_num; + }else{ + if(blk_start > -1){ + printf("found non zero %d bytes block at: %-llu -> %-llu\n", block_size, blk_start, blk_num); + blk_start = -1; + } + } + blk_num += 1; + } + + if(blk_start > -1){ + printf("found non zero %d bytes block at: %-llu -> %-llu\n", block_size, blk_start, blk_num); + } + + close(fd); +} + diff --git a/tools/fs/ext4_recover/src/ext4recover.c b/tools/fs/ext4_recover/src/ext4recover.c new file mode 100644 index 0000000000000000000000000000000000000000..26819a7c91af4e99bd9a802bde69e9c26d60d616 --- /dev/null +++ b/tools/fs/ext4_recover/src/ext4recover.c @@ -0,0 +1,392 @@ +/* + * History: + * 2020-03-01 - Creation by zorrozou + * 2020-03-08 - beta 0.1 + * 2020-08-05 - beta 0.2 split from e2fsprogs code by curuwang + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +#include + +#define RECOVER_DIR "./RECOVER" +#define VERSION "0.2b" + +static const char *program_name = "ext4recover"; +static char *device_name = NULL; +static int flag = 0; +static __u32 icount; +static struct ext3_extent_header *eh; +static struct ext3_extent_idx *ei; +static struct ext3_extent *ee; +static ssize_t blocksize; +static int recover_fd, device_fd; + +struct extent_path { + char *buf; + int entries; + int max_entries; + int left; + int visit_num; + int flags; + blk64_t end_blk; + void *curr; +}; + +struct ext2_extent_handle { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode *inode; + struct ext2_inode inodebuf; + int type; + int level; + int max_depth; + int max_paths; + struct extent_path *path; +}; + +static int is_inode_extent_clear(struct ext2_inode *inode) { + errcode_t retval; + + eh = (struct ext3_extent_header *) inode->i_block; + ei = (struct ext3_extent_idx *) eh + 1; + ee = (struct ext3_extent *) eh + 2; + blk64_t blk; + blk = ext2fs_le32_to_cpu(ei->ei_leaf) + + ((__u64) ext2fs_le16_to_cpu(ei->ei_leaf_hi) << 32); + if (ei->ei_leaf > 1 && LINUX_S_ISREG(inode->i_mode) && inode->i_links_count == 0) { + fprintf(stdout, "inode: %u extent_block: %llu\n", icount, blk); + // not clear + return 0; + } + + return 1; +} + +static int recover_block_to_file(int devfd, int inofd, __le32 block, __le16 len, __u64 start) { + off_t offset_dev, offset_ino, offset; + ssize_t size, ret; + char buf[blocksize]; + int i; + + offset_dev = lseek(devfd, start * blocksize, SEEK_SET); + if (offset_dev < 0) { + perror("lseek(devfd)"); + return 0; + } + + offset_ino = lseek(inofd, block * blocksize, SEEK_SET); + if (offset_ino < 0) { + perror("lseek(inofd)"); + return 0; + } + + for (i = 0; i < len; i++) { + offset = 0; + size = 0; + while (offset < blocksize) { + size = read(devfd, buf + offset, blocksize - offset); + if (size < 0) { + lseek(devfd, offset_dev + blocksize, SEEK_SET); + perror("read(dev_fd)"); + continue; + } + offset += size; + } + //printf("before write offset: %llu\n", offset); + //printf("before write size: %llu\n", size); + while (offset != 0) { + ret = write(inofd, buf + (blocksize - offset), offset); + if (ret < 0) { + lseek(inofd, offset_ino + blocksize, SEEK_SET); + perror("write(inofd)"); + continue; + } + offset -= ret; + //printf("ret: %llu\n", ret); + } + //printf("after write offset: %llu\n", offset); + //printf("after write size: %llu\n", size); + } + + offset_dev = lseek(devfd, 0, SEEK_SET); + if (offset_dev < 0) { + perror("lseek(devfd)"); + return 0; + } + + offset_ino = lseek(inofd, 0, SEEK_SET); + if (offset_ino < 0) { + perror("lseek(inofd)"); + return 0; + } + return 1; +} + +static int dump_dir_extent(struct ext3_extent_header *eh) { + struct ext3_extent *ee; + int i; + __le32 ee_block; + __le16 ee_len; + __u64 ee_start; + char *buf; + __u32 headbuflen = 4; + int retval; + +/* + retval = ext2fs_get_mem(headbuflen, &buf); + if (retval) + return; +*/ + ee = EXT_FIRST_EXTENT(eh); +//printf("eh->eh_entries: %d\n", eh->eh_entries); +//printf("eh->eh_max: %d\n", eh->eh_max); + if (ext2fs_le16_to_cpu(eh->eh_entries) > 340) { + return 1; + } else if (ext2fs_le16_to_cpu(eh->eh_magic) != EXT3_EXT_MAGIC) { + return 1; + } else if (ext2fs_le16_to_cpu(eh->eh_max) != 340) { + return 1; + } + for (i = 1; i < eh->eh_entries + 1; i++) { + ee_block = ext2fs_le32_to_cpu(ee->ee_block); + ee_len = ext2fs_le32_to_cpu(ee->ee_len); + ee_start = (((__u64) ext2fs_le16_to_cpu(ee->ee_start_hi) << 32) + + (__u64) ext2fs_le32_to_cpu(ee->ee_start)); + printf("%u %u %u %llu\n", icount, ee_block, ee_len, ee_start); + fflush(stdout); + + retval = recover_block_to_file(device_fd, recover_fd, ee_block, ee_len, ee_start); + if (!retval) { + fprintf(stderr, "recover_block_to_file()\n"); + return 0; + } + + ee++; + } + return 1; +} + +static int extent_tree_travel(ext2_extent_handle_t handle, struct ext3_extent_header *eh) { + struct ext3_extent_header *next; + struct ext3_extent_idx *ei; + int i, retval; + char *buf; + blk64_t blk; + + //printf("Enter extent_tree_travel + if (eh->eh_depth == 0) { + //printf("dump_dir_extent\n"); + retval = dump_dir_extent(eh); + if (!retval) { + fprintf(stderr, "dump_dir_extent()\n"); + return retval; + } + } else if (eh->eh_depth <= 4) { + flag = 1; + //printf("eh->eh_depth < 4\n"); + for (i = 1; i < eh->eh_entries + 1; i++) { + retval = ext2fs_get_mem(blocksize, &buf); + if (retval) + return; + + memset(buf, 0, blocksize); + ei = EXT_FIRST_INDEX(eh) + i - 1; + blk = ext2fs_le32_to_cpu(ei->ei_leaf) + + ((__u64) ext2fs_le16_to_cpu(ei->ei_leaf_hi) << 32); + retval = io_channel_read_blk64(handle->fs->io, + blk, 1, buf); + if (retval) + return retval; + next = (struct ext3_extent_header *) buf; + //printf("Recursive\n"); + retval = extent_tree_travel(handle, next); + if (!retval) { + fprintf(stderr, "extent_tree_travel()\n"); + } + ext2fs_free_mem(&buf); + //printf("Recursive end\n"); + } + } else { + /* xxxxxxxxxxxxxxx */ + return 1; + } + return 1; +} + +static int prase_ino_extent(ext2_extent_handle_t handle) { + int i, retval; + struct ext3_extent_idx *ix; + struct ext3_extent_header *next; + char *buf; + blk64_t blk; + struct ext3_extent_header *eh; + + eh = (struct ext3_extent_header *) handle->inode->i_block; + +// printf("aaaaaaaaaaaaaa\n"); + retval = ext2fs_get_mem(blocksize, &buf); + if (retval) + return 0; +// printf("i:%d\n", i); +// printf("ix->ei_leaf: %d\n", ix->ei_leaf); +// printf("next->eh_max: %d\n", next->eh_max); +// printf("next->eh_entries: %d\n", next->eh_entries); + memset(buf, 0, blocksize); + for (i = 1; i <= 4; i++) { + ix = EXT_FIRST_INDEX(eh) + i - 1; + + + blk = ext2fs_le32_to_cpu(ix->ei_leaf) + + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); + retval = io_channel_read_blk64(handle->fs->io, + blk, 1, buf); + if (retval) + return retval; + + next = (struct ext3_extent_header *) buf; + //printf("bbbbbbbbbbbbbb\n"); + retval = extent_tree_travel(handle, next); + if (!retval) { + fprintf(stderr, "extent_tree_travel()"); + return retval; + } + //printf("cccccccccccccccccc\n"); + /* for last extent */ + if (next->eh_entries < 340) { +// printf("dddddddddddddddddddd\n"); + break; + } + }; + ext2fs_free_mem(&buf); + return 1; +} + +static int is_on_device(const char *path, const char *dev) { + struct stat stat1, stat2; + int ret; + ret = stat(path, &stat1); + if (ret < 0) { + perror("stat:"); + return -1; + } + ret = stat(dev, &stat2); + if (ret < 0) { + perror("stat:"); + return -1; + } + if (((stat2.st_mode & S_IFMT) == S_IFBLK) && (stat1.st_dev == stat2.st_rdev)) { + return 1; + } + return 0; +} + +int main(int argc, char **argv) { + errcode_t retval; + blk64_t use_superblock = 0; + ext2_filsys fs; + int use_blocksize = 0; + int flags; + __u32 imax; + struct ext2_inode inode; + ext2_extent_handle_t handle; + char filename[BUFSIZ]; + + if (argc != 2) { + fprintf(stderr, "Usage: %s /dev/xxx\n", argv[0]); + fprintf(stderr, "Recover deleted files using remaining extent info\n"); + fprintf(stderr, "version: %s\n\n", VERSION); + fprintf(stderr, "eg:\n\t%s /dev/vdb1\n", argv[0]); + exit(1); + } + + device_name = argv[1]; + flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES | + EXT2_FLAG_64BITS; + retval = ext2fs_open(device_name, flags, use_superblock, + use_blocksize, unix_io_manager, &fs); + + if (retval) { + com_err(program_name, retval, "while trying to open %s", + device_name); + printf("%s", "Couldn't find valid filesystem superblock.\n"); + exit(retval);; + } + fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; + + blocksize = fs->blocksize; + + device_fd = open(argv[1], O_RDONLY); + if (device_fd < 0) { + perror("open(device)"); + exit(1); + } + + retval = mkdir(RECOVER_DIR, 0750); + if (retval < 0 && errno != EEXIST) { + perror("mkdir()"); + exit(1); + } + if (is_on_device(RECOVER_DIR, device_name) == 1) { + fprintf(stderr, "DANGER: recover dir '%s' is on target device '%s', aborted!\n", + RECOVER_DIR, device_name); + exit(1); + } + imax = fs->super->s_inodes_count; + for (icount = 3; icount < imax + 1; icount++) { + flag = 0; + + // printf("%u\n", icount); + retval = ext2fs_read_inode(fs, icount, &inode); + if (retval) { + com_err(program_name, retval, "%s", + "while reading journal inode"); + continue; + } + if (is_inode_extent_clear(&inode)) { + continue; + } + + snprintf(filename, BUFSIZ, "%s/%d_file", RECOVER_DIR, icount); + + recover_fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE, 0640); + if (recover_fd < 0) { + perror("open(inode)"); + continue; + } + + retval = ext2fs_extent_open(fs, icount, &handle); + if (retval) + return; + + retval = prase_ino_extent(handle); + if (!retval) { + fprintf(stderr, "Recover error!\n"); + close(recover_fd); + close(device_fd); + exit(1); + } + if (flag) { + fflush(stdout); + } + + close(recover_fd); + } + + close(device_fd); + + fprintf(stderr, "Recover success!\n"); + + exit(0); +} + diff --git a/tools/fs/xfs_recover/Makefile b/tools/fs/xfs_recover/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c9927991a8e911a7d97fd62199dd1214330b9141 --- /dev/null +++ b/tools/fs/xfs_recover/Makefile @@ -0,0 +1,12 @@ +XFSPROGS_VERSION = 5.9.0 +XFSPROGS_DIR = xfsprogs-$(XFSPROGS_VERSION) + +default: $(XFSPROGS_DIR) + cp recover/* $(XFSPROGS_DIR)/db + make -C $(XFSPROGS_DIR) + cp $(XFSPROGS_DIR)/db/xfsrecover recover + +$(XFSPROGS_DIR): + curl -fsSL https://kernel.org/pub/linux/utils/fs/xfs/xfsprogs/xfsprogs-$(XFSPROGS_VERSION).tar.xz | tar -xJf - + cd ${XFSPROGS_DIR} && ./configure + diff --git a/tools/fs/xfs_recover/README.md b/tools/fs/xfs_recover/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7a8fef71f1cb6df682c8d6938fb2f574ae1a6c46 --- /dev/null +++ b/tools/fs/xfs_recover/README.md @@ -0,0 +1,33 @@ +# Xfs文件系统误删除文件恢复工具使用 + +这是一个xfs误删除的恢复工具,使用方法: + +编译: +``` +make +``` + +如果make失败的话,需安装以下rpm包再make: +centos: +``` +yum install libtool +yum install libuuid libuuid-devel +yum install libblkid-devel +``` +ubuntu: +``` +apt get glibtoolize +apt install uuid-dev +apt install libblkid-dev +``` + +make成功后,在 recover目录下会编译生成 xfsrecover 可执行文件。 + +之后 cd 到你想恢复数据的目标目录,比如此处目标目录为 /data1 , xfs_recover目录为 /data1/xfs_recover ,执行: + +cd /data1 + +./xfs_recover/db/xfsrecover /dev/sdb1 # /dev/sdb1 为你想恢复数据的文件系统。 + +执行完毕后会在 /data1 下产生 RECOVER 目录,其中的文件就是恢复出来的数据。 + diff --git a/tools/fs/xfs_recover/recover/Makefile b/tools/fs/xfs_recover/recover/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..af7dbe1ad7f41ac00b18e0e79a5e84045667bdb9 --- /dev/null +++ b/tools/fs/xfs_recover/recover/Makefile @@ -0,0 +1,27 @@ +# + +TOPDIR = .. +include $(TOPDIR)/include/builddefs + +LTCOMMAND = xfsrecover + +HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \ + btblock.h bmroot.h check.h command.h crc.h debug.h \ + dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \ + flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \ + io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \ + sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h \ + fuzz.h +CFILES = $(HFILES:.h=.c) btdump.c btheight.c convert.c info.c + +LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBFROG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD) +LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) $(LIBFROG) +LLDFLAGS += -static-libtool-libs + +default: depend $(LTCOMMAND) + +include $(BUILDRULES) + +install-dev: + +-include .dep diff --git a/tools/fs/xfs_recover/recover/init.c b/tools/fs/xfs_recover/recover/init.c new file mode 100644 index 0000000000000000000000000000000000000000..0a827c4812455d08ca233c13f1fbbddaf05c2538 --- /dev/null +++ b/tools/fs/xfs_recover/recover/init.c @@ -0,0 +1,742 @@ +/* + * Copyright (c) Tencent, Inc. + * All Rights Reserved. + * Create add Edit by zorrozou 2020/03/30. + * update by curuwang 2022/01/18 + * add recover dir by jindazhong 2022/04/12 + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE +#define _FILE_OFFSET_BITS 64 + +#include "libxfs.h" +#include "libxlog.h" +#include "init.h" +#include "bmap.h" +#define RECOVER_DIR "./RECOVER" +#define FILE_DIR_HASH_SIZE 10000 +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +// path /a/b/c/d max size +#define FILE_PATH_MAX 1024 + +int mflag = 0; // Output only inode/filename and file storage location, do not recover file +int nflag = 0; // Do not recover filename +int dflag = 0; // Output debug logs + +char *fsdevice; +static int recover_fd, device_fd; +int blkbb; +int exitcode; +int expert_mode; +static int force; +static struct xfs_mount xmount; +struct xfs_mount *mp; +static struct xlog xlog; +xfs_agnumber_t cur_agno = NULLAGNUMBER; +xfs_dinode_t *dinode; +struct xfs_sb *sbp; + +struct inode_file_list file_list; +struct hlist_head file_dir_htable[FILE_DIR_HASH_SIZE]; + + +typedef struct inode_file_list{ + __u64 inumber; + struct list_head list; +} inode_file_list; + +typedef struct file_dir_hash_node { + __u64 inumber; + uint8_t file_type; + uint8_t file_name_len; + char* file_name; + __u64 parent_inumber; + struct hlist_node list; +} file_dir_hash_node; + +static int recover_block_to_file(int devfd, int inofd, __u64 block, __u64 len, __u64 start) +{ + off_t offset_dev, offset_ino, offset; + ssize_t size, ret; + char buf[sbp->sb_blocksize]; + int i; + + if ( mflag ){ + return 0; + } + + offset_dev = lseek(devfd, start * sbp->sb_blocksize, SEEK_SET); + if (offset_dev < 0) { + perror("lseek(devfd)"); + return 0; + } + + offset_ino = lseek(inofd, block * sbp->sb_blocksize, SEEK_SET); + if (offset_ino < 0) { + perror("lseek(inofd)"); + return 0; + } + + for (i = 0; i < len; i++) { + offset = 0; + size = 0; + while (offset < sbp->sb_blocksize) { + size = read(devfd, buf + offset, sbp->sb_blocksize - offset); + if (size < 0) { + lseek(devfd, offset_dev + sbp->sb_blocksize, SEEK_SET); + perror("read(dev_fd)"); + continue; + } + offset += size; + } + //printf("before write offset: %llu\n", offset); + //printf("before write size: %llu\n", size); + while (offset != 0) { + ret = write(inofd, buf + (sbp->sb_blocksize - offset), offset); + if (ret < 0) { + lseek(inofd, offset_ino + sbp->sb_blocksize, SEEK_SET); + perror("write(inofd)"); + continue; + } + offset -= ret; + //printf("ret: %llu\n", ret); + } + //printf("after write offset: %llu\n", offset); + //printf("after write size: %llu\n", size); + } + + offset_dev = lseek(devfd, 0, SEEK_SET); + if (offset_dev < 0) { + perror("lseek(devfd)"); + return 0; + } + + offset_ino = lseek(inofd, 0, SEEK_SET); + if (offset_ino < 0) { + perror("lseek(inofd)"); + return 0; + } + return 1; +} + +static char *xfs_get_block(xfs_filblks_t blknum, uint32_t bsize) +{ + char *buf; + ssize_t size; + int fd; + off_t offset; + + fd = libxfs_device_to_fd(mp->m_ddev_targp->dev); + + buf = malloc(bsize); + if (buf == NULL) { + fprintf(stderr, "xfs_get_block: malloc() error!\n"); + return NULL; + } + + offset = lseek(fd, blknum * bsize, SEEK_SET); + if (offset < 0) { + perror("xfs_get_block: lseek()"); + return NULL; + } + + size = read(fd, buf, bsize); + if (size < 0) { + perror("xfs_get_block: read()"); + return NULL; + } + + return buf; +} + + +static int is_inode_used(xfs_dinode_t *dinode) +{ + //if (dinode->di_atime.t_sec == 0 || + // dinode->di_atime.t_nsec == 0) { + // dinode->di_nlink > 0) { + if (dinode->di_size != 0){ + return 1; + } + return 0; +} + +static int btree_block_travel(struct xfs_btree_block *block) +{ + int count, state, num, retval; + char *buf; + xfs_bmbt_rec_t *rec; + xfs_fileoff_t startoff; + xfs_fsblock_t startblock; + xfs_filblks_t blknum; + xfs_filblks_t blockcount; + xfs_bmbt_ptr_t *pp; + + if (be32_to_cpu(block->bb_magic) != XFS_BMAP_CRC_MAGIC) { + printf("block->bb_magic: %x, block not a bmap!\n", be32_to_cpu(block->bb_magic)); + return 0; + } + + if (be16_to_cpu(block->bb_level) == 0) { + /* leaf */ + num = be16_to_cpu(block->bb_numrecs); + printf("block->bb_numrecs: %d\n", num); + /* + buf = block; + for (i=1; i <= 4096;i++) { + printf("%.2x ", buf[i-1]); + if (i % 16 == 0) { + printf("\n"); + } + } + */ + rec = (xfs_bmbt_rec_t * )((struct xfs_btree_block *) block + 1); + for (count = 0; count < num; count++) { + convert_extent(rec + count, &startoff, &startblock, &blockcount, &state); + + /* + printf("rec l0: %16llx\t", (rec+count)->l0); + printf("rec l1: %16llx\n", (rec+count)->l1); + printf("addr rec: %p\n", rec+count); + */ + + printf("%lu %lu %lu\n", startoff, startblock, blockcount); + retval = recover_block_to_file(device_fd, recover_fd, startoff, blockcount, startblock); + if (!retval) { + fprintf(stderr, "recover_block_to_file(btree_block_travel)\n"); + return 0; + } + } + } else if (be16_to_cpu(block->bb_level) > 0 && + be16_to_cpu(block->bb_level) <= 5) { + /* middle */ + pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[0]); + for (count = 0; count < be16_to_cpu(block->bb_numrecs); count++) { + blknum = cpu_to_be64(*pp + count); + printf("blknum: %lu\n", blknum); + buf = xfs_get_block(blknum, sbp->sb_blocksize); + if (buf == NULL) { + return 0; + } + btree_block_travel((struct xfs_btree_block *) buf); + free(buf); + } + } else { + /* do nothing! */ + } + + return 1; +} + + +static int inode_extent_tree_travel(xfs_bmdr_block_t *rblock, int iflag) +{ + int state, fsize, i, retval; + char *buf; + xfs_fileoff_t startoff; + xfs_fsblock_t startblock; + xfs_filblks_t blockcount, blknum; + xfs_bmbt_rec_t *rec; + xfs_bmbt_ptr_t *pp; + xfs_buf_t *bp; + xfs_agnumber_t agno; + + + if (rblock == NULL) { + return 0; + } + + if (cpu_to_be16(rblock->bb_level) == 0) { + /* extent fmt or leaf */ + if (iflag == 1) { + /* extent */ + rec = (xfs_bmbt_rec_t *) rblock; + } else { + /* leaf */ + rec = (xfs_bmbt_rec_t * )(rblock + 1); + } + convert_extent(rec, &startoff, &startblock, &blockcount, &state); + while (1) { + convert_extent(rec, &startoff, &startblock, &blockcount, &state); + if (blockcount == 0) { + break; + } + + agno = XFS_FSB_TO_AGNO(mp, startblock); + startblock = XFS_FSB_TO_DADDR(mp, startblock) >> mp->m_blkbb_log; + printf("startoff:%-10lu\tstartblock:%-10lu\tblockcount:%-10lu\tag:%u\n", + startoff, startblock, blockcount, agno); + retval = recover_block_to_file(device_fd, recover_fd, startoff, blockcount, startblock); + if (!retval) { + fprintf(stderr, "recover_block_to_file(inode_extent_tree_travel)\n"); + return 0; + } + rec++; + } + } else { + /* btree fmt: root */ + if (cpu_to_be16(rblock->bb_level) > 0 && cpu_to_be16(rblock->bb_numrecs) < 10) { + /* root */ + bp = malloc(sizeof(xfs_buf_t)); + fsize = XFS_DFORK_SIZE(dinode, mp, XFS_DATA_FORK); + if (fsize > sbp->sb_inodesize) { + return 0; + } + printf("root inode fsize: %u\n", fsize); + printf("bb_numrecs: %u\n", cpu_to_be16(rblock->bb_numrecs)); + pp = XFS_BMDR_PTR_ADDR(rblock, 1, libxfs_bmdr_maxrecs(fsize, 0)); + for (i = 0; i < cpu_to_be16(rblock->bb_numrecs); i++) { + blknum = cpu_to_be64(*pp + i); + printf("blknum: %lu\n", blknum); + buf = xfs_get_block(blknum, sbp->sb_blocksize); + if (buf == NULL) { + fprintf(stderr, "inode_extent_tree_travel: btree_block_travel() error!\n"); + return 0; + } + btree_block_travel((struct xfs_btree_block *) buf); + /* + libxfs_readbufr(mp->m_ddev_targp, blknum, bp, sbp->sb_blocksize, 0); + inode_extent_tree_travel(bp->b_addr, 0); + */ + free(buf); + } + free(bp); + } else { + /* do nothing! */ + } + } + + return 1; +} + +static inline int hlist_empty(const struct hlist_head *h){ + return !h->first; +} + +static struct file_dir_hash_node* search_file_dir_htable(__u64 inumber){ + struct file_dir_hash_node *data_node = NULL; + struct hlist_node *hlist; + __u64 key; + + key = inumber % FILE_DIR_HASH_SIZE; + if(hlist_empty(&file_dir_htable[key])) + return NULL; + else{ + hlist_for_each_entry(data_node, hlist, &file_dir_htable[key], list){ + if(data_node->inumber == inumber){ + return data_node; + } + + } + } + return NULL; +} + +static int insert_file_dir_htable(__u64 inumber, char *name, uint8_t namelen, uint8_t dir_ftype, __u64 parent_inumber){ + __u64 key; + struct file_dir_hash_node *data_node; + data_node = malloc(sizeof(struct file_dir_hash_node)); + if(!data_node){ + perror("malloc error struct file_dir_hash_node "); + return -1; + } + + data_node->inumber = inumber; + data_node->file_name = malloc(namelen+1); + memcpy(data_node->file_name, name, namelen); + data_node->file_name[namelen] = '\0'; + data_node->file_name_len = namelen; + data_node->file_type = dir_ftype; + data_node->parent_inumber = parent_inumber; + INIT_HLIST_NODE(&data_node->list); + + key = data_node->inumber % FILE_DIR_HASH_SIZE; + hlist_add_head(&data_node->list, &file_dir_htable[key]); + return 0; +} + +static int dir_travel(char * buf, uint16_t isize, uint16_t ipblock){ + struct xfs_dir2_data_hdr *block; + xfs_dir2_block_tail_t *btp = NULL; + xfs_dir2_leaf_entry_t *lep = NULL; + int i,j; + struct xfs_dir2_data_hdr *data; + xfs_dir2_data_entry_t *dep; + char *ptr; + char *endptr; + xfs_ino_t lino; + struct xfs_dir2_sf_hdr *sf; + xfs_dir2_sf_entry_t *sfe; + uint8_t dir_ftype; + __u64 parent_inumber = 0; + + data = (struct xfs_dir2_data_hdr*) buf; + block = (struct xfs_dir2_data_hdr*) buf; + //recover block dir + if (be32_to_cpu(block->magic) == XFS_DIR2_BLOCK_MAGIC || + be32_to_cpu(data->magic) == XFS_DIR2_DATA_MAGIC || + be32_to_cpu(block->magic) == XFS_DIR3_BLOCK_MAGIC || + be32_to_cpu(data->magic) == XFS_DIR3_DATA_MAGIC) { + + ptr = (char *)data + mp->m_dir_geo->data_entry_offset; + if (be32_to_cpu(block->magic) == XFS_DIR2_BLOCK_MAGIC || + be32_to_cpu(block->magic) == XFS_DIR3_BLOCK_MAGIC) { + btp = xfs_dir2_block_tail_p(mp->m_dir_geo, block); + lep = xfs_dir2_block_leaf_p(btp); + endptr = (char *)lep; + } else{ + endptr = (char *)data + mp->m_dir_geo->blksize; + } + while (ptr < endptr) { + dep = (xfs_dir2_data_entry_t *)ptr; + dir_ftype = xfs_dir2_data_get_ftype(mp, dep); + if ( dep->namelen != 0 ) { + if (parent_inumber == 0 && dep->name[0] == '.' && dep->namelen == 1){ + parent_inumber = be64_to_cpu(dep->inumber); + ptr += libxfs_dir2_data_entsize(mp, dep->namelen); + continue; + } + if (dep->name[0] == '.' && dep->name[1] == '.' && dep->namelen == 2){ + ptr += libxfs_dir2_data_entsize(mp, dep->namelen); + continue; + } + if ( dflag ){ + printf("name:%s, namelen:%d,inode:%u,filetype:%u, parent_inumber:%u\n", dep->name, dep->namelen, be64_to_cpu(dep->inumber), dir_ftype, parent_inumber); + } + insert_file_dir_htable(be64_to_cpu(dep->inumber), (char*)dep->name, dep->namelen, dir_ftype, parent_inumber); + } + ptr += libxfs_dir2_data_entsize(mp, dep->namelen); + } + } + + //recover inode dir + dinode = (xfs_dinode_t *) buf; + for (i = 0; i < ipblock; i++) { + dinode = (xfs_dinode_t * ) & buf[isize * i]; + if (cpu_to_be16(dinode->di_magic) != XFS_DINODE_MAGIC) { + continue; + } + //if (dinode->di_format == XFS_DINODE_FMT_LOCAL){ + if ( 1==1 ){ + //if (be64_to_cpu(dinode->di_ino) == 67){ + sf = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dinode); + sfe = xfs_dir2_sf_firstentry(sf); + //sf->count rewrite 0? + for (j = sf->count - 1; j >= -256; j--) { + lino = libxfs_dir2_sf_get_ino(mp, sf, sfe); + dir_ftype = libxfs_dir2_sf_get_ftype(mp, sfe); + if ( dir_ftype == 0 || lino == 0 ){ + break; + } + if ( dflag ){ + printf("file:%s, inode:%u,dir_ftype:%u,parent_inode:%u\n",sfe->name, lino, dir_ftype, be64_to_cpu(dinode->di_ino)); + } + insert_file_dir_htable(lino, (char*)sfe->name, sfe->namelen, dir_ftype, be64_to_cpu(dinode->di_ino)); + sfe = libxfs_dir2_sf_nextentry(mp, sf, sfe); + } + } + } + return 0; +} + +static int _mkdir(const char *dir) { + char tmp[256]; + char *p = NULL; + size_t len; + snprintf(tmp, sizeof(tmp),"%s",dir); + len = strlen(tmp); + if(tmp[len - 1] == '/'){ + tmp[len - 1] = 0; + } + for(p = tmp + 1; *p; p++){ + if(*p == '/') { + *p = 0; + mkdir(tmp, 0777); + *p = '/'; + } + } + return mkdir(tmp, 0777); +} + +static int mv_or_cp_file(__u64 inumber, char *file_path, char *file_name){ + int retval; + char recover_file_path[FILE_PATH_MAX+10]; + char recover_file[FILE_PATH_MAX+256]; + char inode_file[128]; + snprintf(inode_file, 128, "%s/%llu_file", RECOVER_DIR, inumber); + snprintf(recover_file_path, FILE_PATH_MAX+10, "%s/%s", RECOVER_DIR, file_path); + snprintf(recover_file, FILE_PATH_MAX+256, "%s/%s", recover_file_path, file_name); + if ( strlen(file_name) == 0 ){ + return -1; + } + if (access(recover_file_path, 0) != 0){ + retval = _mkdir(recover_file_path); + if (retval < 0 && errno != EEXIST) { + perror("mkdir() recover_file_path"); + } + } + //mv + if (access(inode_file, 0) != 0){ + perror("inode_file is not exist"); + return -1; + } + + //printf("inode_file:%s, recover_file:%s\n",inode_file, recover_file); + if (rename(inode_file, recover_file) != 0){ + perror("rename error"); + return -1; + } + + //cp todo + return 0; +} + +static void recover_dir(){ + struct inode_file_list *file_list_node; + struct file_dir_hash_node *data_node,*tmp_node; + char *file_path, *file_path_tmp; + file_path = malloc(FILE_PATH_MAX); + file_path_tmp = malloc(FILE_PATH_MAX); + list_for_each_entry(file_list_node, &file_list.list, list){ + file_path = memset(file_path, '\0', FILE_PATH_MAX); + file_path_tmp = memset(file_path_tmp, '\0', FILE_PATH_MAX); + data_node = search_file_dir_htable(file_list_node->inumber); + if (!data_node){ + continue; + } + if (data_node->file_type == XFS_DIR3_FT_DIR){ + continue; + } + //printf("inode :%u, file:%s\n", file_list_node->inumber,data_node->file_name); + tmp_node = data_node; + while ( tmp_node->parent_inumber != sbp->sb_rootino && tmp_node->parent_inumber != 0 ){ + tmp_node = search_file_dir_htable(tmp_node->parent_inumber); + if (!tmp_node){ + break; + } + if (tmp_node->file_type !=XFS_DIR3_FT_DIR){ + break; + } + snprintf(file_path_tmp, FILE_PATH_MAX, "%s/%s",tmp_node->file_name, file_path); + memcpy(file_path, file_path_tmp, strlen(file_path_tmp)); + } + if ( dflag ){ + printf("inode:%llu, path:%s, file_name:%s\n",data_node->inumber, file_path,data_node->file_name); + } + mv_or_cp_file(data_node->inumber, file_path, data_node->file_name); + } +} + +static int disk_traverse(char *device, uint32_t bsize, uint16_t isize, uint16_t ipblock) +{ + int i, fd; + char buf[bsize]; + ssize_t size, offset,inum; + xfs_bmdr_block_t *rblock; + struct inode_file_list *file_list_node; + char filename[BUFSIZ]; + + fd = open(device, O_RDONLY | O_LARGEFILE); + if (fd < 0) { + perror("open(device)"); + return 0; + } + + offset = -1; + inum = -1; + while ((size = read(fd, buf, bsize)) == bsize) { + if ( !nflag ){ + dir_travel(buf, isize, ipblock); + } + //continue; + offset++; + dinode = (xfs_dinode_t *) buf; + /*if (cpu_to_be16(dinode->di_magic) != XFS_DINODE_MAGIC) { + continue; + }*/ + for (i = 0; i < ipblock; i++) { + inum++; + dinode = (xfs_dinode_t * ) & buf[isize * i]; + if (cpu_to_be16(dinode->di_magic) != XFS_DINODE_MAGIC) { + continue; + } + if (dinode->di_atime.t_sec == 0 || inum == sbp->sb_rootino ){ + printf("invalid v4 inode: %llu\n",inum ); + continue; + } + //取消注释, 部分文件被删除后,inode.size 还是不为0 + /*if (is_inode_used(dinode)) { + continue; + }*/ + //printf("magic: %d\n", dinode->di_magic); + if ( dflag ){ + printf("inode: %llu\n", be64_to_cpu(dinode->di_ino)); + printf("v4 inode: %llu\n",inum ); + printf("inode offset: %lu:%d\n", offset, i); + } + + //snprintf(filename, BUFSIZ, "%s/%lu_%d_file", RECOVER_DIR, offset, i); + // snprintf(filename, BUFSIZ, "%s/%llu_file", RECOVER_DIR, be64_to_cpu(dinode->di_ino)); + snprintf(filename, BUFSIZ, "%s/%llu_file", RECOVER_DIR, inum); + if ( !nflag ){ + file_list_node = (struct inode_file_list *)malloc(sizeof(struct inode_file_list)); + //file_list_node->inumber = be64_to_cpu(dinode->di_ino); + file_list_node->inumber = inum; + list_add(&(file_list_node->list), &(file_list.list)); + } + + recover_fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE, 0644); + if (recover_fd < 0) { + perror("open(inode)"); + continue; + } + rblock = (xfs_bmdr_block_t *) XFS_DFORK_PTR(dinode, XFS_DATA_FORK); + //printf("rblock addr - inode addr: %lu\n", (uint64_t)rblock - (uint64_t)dinode); + inode_extent_tree_travel(rblock, 1); + close(recover_fd); + } + } + + if (size != bsize && size != 0) { + fprintf(stderr, "read(): read device error!\n"); + close(fd); + return 0; + } + + close(fd); + return 1; +} + +int main(int argc, char **argv) +{ + struct xfs_buf *bp; + unsigned int agcount; + int retval,i; + int opt; + + setlinebuf(stdout); + + progname = basename(argv[0]); + + while ((opt = getopt(argc, argv, "mnd")) != -1) { + switch (opt) { + case 'm': + mflag = 1; + break; + case 'n': + nflag = 1; + break; + case 'd': + dflag = 1; + break; + default: /* '?' */ + fprintf(stderr, "Usage: %s [-m] [-n] [-d] device\n-m: Output only inode/filename and file storage location, do not recover file\n-n: Do not recover filename\n-d: Output debug logs\n",progname); + exit(EXIT_FAILURE); + } + } + + if (optind >= argc) { + fprintf(stderr, "Expected argument after options\n"); + exit(EXIT_FAILURE); + } + + fsdevice = argv[optind]; + + if (!x.disfile) + x.volname = fsdevice; + else + x.dname = fsdevice; + + x.bcache_flags = CACHE_MISCOMPARE_PURGE; + x.isreadonly = LIBXFS_ISREADONLY; + + if (!libxfs_init(&x)) { + fputs(_("\nfatal error -- couldn't initialize XFS library\n"), + stderr); + exit(1); + } + /* + * Read the superblock, but don't validate it - we are a diagnostic + * tool and so need to be able to mount busted filesystems. + */ + memset(&xmount, 0, sizeof(struct xfs_mount)); + libxfs_buftarg_init(&xmount, x.ddev, x.logdev, x.rtdev); + retval = -libxfs_buf_read_uncached(xmount.m_ddev_targp, XFS_SB_DADDR, + 1 << (XFS_MAX_SECTORSIZE_LOG - BBSHIFT), 0, &bp, NULL); + if (retval) { + fprintf(stderr, _("%s: %s is invalid (cannot read first 512 " + "bytes)\n"), progname, fsdevice); + exit(1); + } + + /* copy SB from buffer to in-core, converting architecture as we go */ + libxfs_sb_from_disk(&xmount.m_sb, bp->b_addr); + + sbp = &xmount.m_sb; + if (sbp->sb_magicnum != XFS_SB_MAGIC) { + fprintf(stderr, _("%s: %s is not a valid XFS filesystem (unexpected SB magic number 0x%08x)\n"), + progname, fsdevice, sbp->sb_magicnum); + if (!force) { + fprintf(stderr, _("Use -F to force a read attempt.\n")); + exit(EXIT_FAILURE); + } + } + + agcount = sbp->sb_agcount; + mp = libxfs_mount(&xmount, sbp, x.ddev, x.logdev, x.rtdev, + LIBXFS_MOUNT_DEBUGGER); + if (!mp) { + fprintf(stderr, + _("%s: device %s unusable (not an XFS filesystem?)\n"), + progname, fsdevice); + exit(1); + } + mp->m_log = &xlog; + + + /* + * xfs_check needs corrected incore superblock values + */ + if (sbp->sb_rootino != NULLFSINO && + xfs_sb_version_haslazysbcount(&mp->m_sb)) { + int error = -libxfs_initialize_perag_data(mp, sbp->sb_agcount); + if (error) { + fprintf(stderr, + _("%s: cannot init perag data (%d). Continuing anyway.\n"), + progname, error); + } + } + + printf("agcount: %u\n", agcount); + printf("isize: %u\n", sbp->sb_inodesize); + printf("bsize: %u\n", sbp->sb_blocksize); + printf("rootinode: %lu\n", sbp->sb_rootino); + printf("inopblock: %u\n", sbp->sb_inopblock); + printf("sb_icount: %lu\n", sbp->sb_icount); + device_fd = open(fsdevice, O_RDONLY); + if (device_fd < 0) { + perror("open(device)"); + exit(1); + } + + retval = mkdir(RECOVER_DIR, 0777); + if (retval < 0 && errno != EEXIST) { + perror("mkdir()"); + exit(1); + } + + INIT_LIST_HEAD(&file_list.list); + for(i = 0; i < FILE_DIR_HASH_SIZE; i++){ + INIT_HLIST_HEAD(&file_dir_htable[i]); + } + + disk_traverse(fsdevice, sbp->sb_blocksize, sbp->sb_inodesize, sbp->sb_inopblock); + + if ( !nflag ){ + recover_dir(); + } + + close(device_fd); + exit(0); +}