Message ID | 20240508005704.765813-1-drosen@google.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | [f2fs-dev,1/2] dump.f2fs: Add ability to dump folders | expand |
On Tue, May 7, 2024 at 6:26 PM Daniel Rosenberg via Linux-f2fs-devel <linux-f2fs-devel@lists.sourceforge.net> wrote: > > This adds the ability to dump folders as well as files. Folders are > dumped recursively. Additionally, dumped files/folders may be directed > to a folder specified by -o [path] instead of ./lost_found. The -r flag > will dump the entire fs from the root inode. -f or -y will skip the > prompt before dumping, and -P will preserve the mode/owner info for the > created file/folder. > > Signed-off-by: Daniel Rosenberg <drosen@google.com> > --- > fsck/dump.c | 164 +++++++++++++++++++++++++++++++++++++----------- > fsck/fsck.c | 4 +- > fsck/fsck.h | 4 +- > fsck/main.c | 24 ++++++- > man/dump.f2fs.8 | 17 ++++- > 5 files changed, 171 insertions(+), 42 deletions(-) > > diff --git a/fsck/dump.c b/fsck/dump.c > index b2e990b..f60f6f9 100644 > --- a/fsck/dump.c > +++ b/fsck/dump.c > @@ -247,7 +247,26 @@ out: > printf("\n"); > } > > -static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) > +static void dump_folder_contents(struct f2fs_sb_info *sbi, u8 *bitmap, > + struct f2fs_dir_entry *dentry, > + __u8 (*filenames)[F2FS_SLOT_LEN], int max) > +{ > + int i; > + int name_len; > + > + for (i = 0; i < max; i++) { > + if (test_bit_le(i, bitmap) == 0) > + continue; > + name_len = le16_to_cpu(dentry[i].name_len); > + if (name_len == 1 && filenames[i][0] == '.') > + continue; > + if (name_len == 2 && filenames[i][0] == '.' && filenames[i][1] == '.') > + continue; > + dump_node(sbi, le32_to_cpu(dentry[i].ino), 1, NULL, 0, 1); > + } > +} > + > +static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr, bool is_folder) > { > char buf[F2FS_BLKSIZE]; > > @@ -288,12 +307,19 @@ static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) > ASSERT(ret >= 0); > } > > - /* write blkaddr */ > - dev_write_dump(buf, offset, F2FS_BLKSIZE); > + if (is_folder) { > + struct f2fs_dentry_block *d = (struct f2fs_dentry_block *) buf; > + > + dump_folder_contents(sbi, d->dentry_bitmap, F2FS_DENTRY_BLOCK_DENTRIES(d), > + F2FS_DENTRY_BLOCK_FILENAMES(d), NR_DENTRY_IN_BLOCK); > + } else { > + /* write blkaddr */ > + dev_write_dump(buf, offset, F2FS_BLKSIZE); > + } > } > > static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, > - u32 nid, u32 addr_per_block, u64 *ofs) > + u32 nid, u32 addr_per_block, u64 *ofs, int is_dir) > { > struct node_info ni; > struct f2fs_node *node_blk; > @@ -330,20 +356,20 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, > switch (ntype) { > case TYPE_DIRECT_NODE: > dump_data_blk(sbi, *ofs * F2FS_BLKSIZE, > - le32_to_cpu(node_blk->dn.addr[i])); > + le32_to_cpu(node_blk->dn.addr[i]), is_dir); > (*ofs)++; > break; > case TYPE_INDIRECT_NODE: > dump_node_blk(sbi, TYPE_DIRECT_NODE, > le32_to_cpu(node_blk->in.nid[i]), > addr_per_block, > - ofs); > + ofs, is_dir); > break; > case TYPE_DOUBLE_INDIRECT_NODE: > dump_node_blk(sbi, TYPE_INDIRECT_NODE, > le32_to_cpu(node_blk->in.nid[i]), > addr_per_block, > - ofs); > + ofs, is_dir); > break; > } > } > @@ -435,8 +461,9 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, > u32 i = 0; > u64 ofs = 0; > u32 addr_per_block; > + bool is_dir = S_ISDIR(le16_to_cpu(node_blk->i.i_mode)); > > - if((node_blk->i.i_inline & F2FS_INLINE_DATA)) { > + if ((node_blk->i.i_inline & F2FS_INLINE_DATA)) { > DBG(3, "ino[0x%x] has inline data!\n", nid); > /* recover from inline data */ > dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET, > @@ -444,13 +471,25 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, > return -1; > } > > + if ((node_blk->i.i_inline & F2FS_INLINE_DENTRY)) { > + void *inline_dentry = inline_data_addr(node_blk); > + struct f2fs_dentry_ptr d; > + > + make_dentry_ptr(&d, node_blk, inline_dentry, 2); > + > + DBG(3, "ino[0x%x] has inline dentries!\n", nid); > + /* recover from inline dentry */ > + dump_folder_contents(sbi, d.bitmap, d.dentry, d.filename, d.max); > + return -1; > + } > + > c.show_file_map_max_offset = f2fs_max_file_offset(&node_blk->i); > addr_per_block = ADDRS_PER_BLOCK(&node_blk->i); > > /* check data blocks in inode */ > for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++) > dump_data_blk(sbi, ofs * F2FS_BLKSIZE, le32_to_cpu( > - node_blk->i.i_addr[get_extra_isize(node_blk) + i])); > + node_blk->i.i_addr[get_extra_isize(node_blk) + i]), is_dir); > > /* check node blocks in inode */ > for (i = 0; i < 5; i++) { > @@ -458,17 +497,20 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, > dump_node_blk(sbi, TYPE_DIRECT_NODE, > le32_to_cpu(F2FS_INODE_I_NID(&node_blk->i, i)), > addr_per_block, > - &ofs); > + &ofs, > + is_dir); > else if (i == 2 || i == 3) > dump_node_blk(sbi, TYPE_INDIRECT_NODE, > le32_to_cpu(F2FS_INODE_I_NID(&node_blk->i, i)), > addr_per_block, > - &ofs); > + &ofs, > + is_dir); > else if (i == 4) > dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE, > le32_to_cpu(F2FS_INODE_I_NID(&node_blk->i, i)), > addr_per_block, > - &ofs); > + &ofs, > + is_dir); > else > ASSERT(0); > } > @@ -479,8 +521,44 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, > return 0; > } > > -static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, > - struct f2fs_node *node_blk, int force) > +static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, > + struct f2fs_node *node_blk, char *path) > +{ > + struct f2fs_inode *inode = &node_blk->i; > + int ret; > + > + c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666); > + ASSERT(c.dump_fd >= 0); > + > + /* dump file's data */ > + dump_inode_blk(sbi, ni->ino, node_blk); > + > + /* adjust file size */ > + ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size)); > + ASSERT(ret >= 0); > + > + close(c.dump_fd); > +} > + > +static void dump_folder(struct f2fs_sb_info *sbi, struct node_info *ni, > + struct f2fs_node *node_blk, char *path, int is_root) > +{ > + if (!is_root) { > + if (mkdir(path, 0777) < 0 && errno != EEXIST) { > + MSG(0, "Failed to create directory %s\n", path); > + return; > + } > + ASSERT(chdir(path) == 0); > + } > + /* dump folder data */ > + dump_inode_blk(sbi, ni->ino, node_blk); > + if (!is_root) > + ASSERT(chdir("..") == 0); > +} > + > +static int dump_filesystem(struct f2fs_sb_info *sbi, struct node_info *ni, > + struct f2fs_node *node_blk, int force, char *base_path, > + bool is_base, bool allow_folder) > { > struct f2fs_inode *inode = &node_blk->i; > u32 imode = le16_to_cpu(inode->i_mode); > @@ -489,6 +567,7 @@ static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, > char path[1024] = {0}; > char ans[255] = {0}; > int is_encrypted = file_is_encrypt(inode); > + int is_root = sbi->root_ino_num == ni->nid; > int ret; > > if (is_encrypted) { > @@ -496,11 +575,15 @@ static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, > return -1; > } > > - if ((!S_ISREG(imode) && !S_ISLNK(imode)) || > - namelen == 0 || namelen > F2FS_NAME_LEN) { > - MSG(force, "Not a regular file or wrong name info\n\n"); > + if ((!S_ISREG(imode) && !S_ISLNK(imode) && !(S_ISDIR(imode) && allow_folder))) { > + MSG(force, "Not a regular file\n\n"); MSG(force, "Not a valid file type\n\n") ? > return -1; > } > + if (!is_root && (namelen == 0 || namelen > F2FS_NAME_LEN)) { > + MSG(force, "Wrong name info\n\n"); > + return -1; > + } > + base_path = base_path ?: "./lost_found"; > if (force) > goto dump; > > @@ -508,31 +591,42 @@ static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, > if (c.show_file_map) > return dump_inode_blk(sbi, ni->ino, node_blk); > > - printf("Do you want to dump this file into ./lost_found/? [Y/N] "); > + printf("Do you want to dump this %s into %s/? [Y/N] ", > + S_ISREG(imode) || S_ISLNK(imode) ? "file" : "folder", > + base_path); > ret = scanf("%s", ans); > ASSERT(ret >= 0); > > if (!strcasecmp(ans, "y")) { > dump: > - ret = system("mkdir -p ./lost_found"); > - ASSERT(ret >= 0); > + if (is_base) { > + getcwd(path, sizeof(path)); > + ret = mkdir(base_path, 0777); > + ASSERT(ret == 0 || errno == EEXIST); > + ASSERT(chdir(base_path) == 0); > + } > > /* make a file */ > - strncpy(name, (const char *)inode->i_name, namelen); > - name[namelen] = 0; > - sprintf(path, "./lost_found/%s", name); > - > - c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666); > - ASSERT(c.dump_fd >= 0); > - > - /* dump file's data */ > - dump_inode_blk(sbi, ni->ino, node_blk); > + if (!is_root) { > + strncpy(name, (const char *)inode->i_name, namelen); > + name[namelen] = 0; > + } > > - /* adjust file size */ > - ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size)); > - ASSERT(ret >= 0); > + if (S_ISREG(imode) || S_ISLNK(imode)) { > + dump_file(sbi, ni, node_blk, name); > + } else { > + dump_folder(sbi, ni, node_blk, name, is_root); > + } > > - close(c.dump_fd); > + /* fix up mode/owner */ > + if (c.preserve_perms) { > + if (is_root) > + strncpy(name, ".", 2); > + chmod(name, imode); > + chown(name, inode->i_uid, inode->i_gid); > + } > + if (is_base) > + chdir(path); > } > return 0; > } > @@ -582,7 +676,7 @@ void dump_node_scan_disk(struct f2fs_sb_info *sbi, nid_t nid) > free(node_blk); > } > > -int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force) > +int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force, char *base_path, int base, int allow_folder) > { > struct node_info ni; > struct f2fs_node *node_blk; > @@ -617,7 +711,7 @@ int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force) > print_node_info(sbi, node_blk, force); > > if (ni.ino == ni.nid) > - ret = dump_file(sbi, &ni, node_blk, force); > + ret = dump_filesystem(sbi, &ni, node_blk, force, base_path, base, allow_folder); > } else { > print_node_info(sbi, node_blk, force); > MSG(force, "Invalid (i)node block\n\n"); > diff --git a/fsck/fsck.c b/fsck/fsck.c > index 5d345d0..7400dcf 100644 > --- a/fsck/fsck.c > +++ b/fsck/fsck.c > @@ -1651,7 +1651,7 @@ static void print_dentry(struct f2fs_sb_info *sbi, __u8 *name, > d = d->next; > } > printf("/%s", new); > - if (dump_node(sbi, le32_to_cpu(dentry[idx].ino), 0)) > + if (dump_node(sbi, le32_to_cpu(dentry[idx].ino), 0, NULL, 0, 0)) > printf("\33[2K\r"); > } else { > for (i = 1; i < depth; i++) > @@ -3632,7 +3632,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) > if (!strcasecmp(ans, "y")) { > for (i = 0; i < fsck->nr_nat_entries; i++) { > if (f2fs_test_bit(i, fsck->nat_area_bitmap)) > - dump_node(sbi, i, 1); > + dump_node(sbi, i, 1, NULL, 1, 0); > } > } > } > diff --git a/fsck/fsck.h b/fsck/fsck.h > index f5282e2..6cac926 100644 > --- a/fsck/fsck.h > +++ b/fsck/fsck.h > @@ -270,12 +270,14 @@ struct dump_option { > int end_ssa; > int32_t blk_addr; > nid_t scan_nid; > + int use_root_nid; > + char *base_path; > }; > > extern void nat_dump(struct f2fs_sb_info *, nid_t, nid_t); > extern void sit_dump(struct f2fs_sb_info *, unsigned int, unsigned int); > extern void ssa_dump(struct f2fs_sb_info *, int, int); > -extern int dump_node(struct f2fs_sb_info *, nid_t, int); > +extern int dump_node(struct f2fs_sb_info *, nid_t, int, char *, int, int); > extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32); > extern unsigned int start_bidx_of_node(unsigned int, struct f2fs_node *); > extern void dump_node_scan_disk(struct f2fs_sb_info *sbi, nid_t nid); > diff --git a/fsck/main.c b/fsck/main.c > index c4d0956..7d0578a 100644 > --- a/fsck/main.c > +++ b/fsck/main.c > @@ -34,7 +34,7 @@ struct f2fs_fsck gfsck; > > INIT_FEATURE_TABLE; > > -#ifdef WITH_SLOAD > +#if defined(WITH_SLOAD) || defined(WITH_DUMP) > static char *absolute_path(const char *file) > { > char *ret; > @@ -384,7 +384,7 @@ void f2fs_parse_options(int argc, char *argv[]) > } > } else if (!strcmp("dump.f2fs", prog)) { > #ifdef WITH_DUMP > - const char *option_string = "d:i:I:n:Ms:Sa:b:V"; > + const char *option_string = "d:fi:I:n:Mo:Prs:Sa:b:Vy"; > static struct dump_option dump_opt = { > .nid = 0, /* default root ino */ > .start_nat = -1, > @@ -395,6 +395,8 @@ void f2fs_parse_options(int argc, char *argv[]) > .end_ssa = -1, > .blk_addr = -1, > .scan_nid = 0, > + .use_root_nid = 0, > + .base_path = NULL, > }; > > c.func = DUMP; > @@ -456,6 +458,19 @@ void f2fs_parse_options(int argc, char *argv[]) > ret = sscanf(optarg, "%x", > &dump_opt.blk_addr); > break; > + case 'y': > + case 'f': > + c.force = 1; > + break; > + case 'r': > + dump_opt.use_root_nid = 1; > + break; > + case 'o': > + dump_opt.base_path = absolute_path(optarg); > + break; > + case 'P': > + c.preserve_perms = 1; > + break; > case 'V': > show_version(prog); > exit(0); > @@ -914,6 +929,9 @@ static void do_dump(struct f2fs_sb_info *sbi) > struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); > u32 flag = le32_to_cpu(ckpt->ckpt_flags); > > + if (opt->use_root_nid) > + opt->nid = sbi->root_ino_num; > + > if (opt->end_nat == -1) > opt->end_nat = NM_I(sbi)->max_nid; > if (opt->end_sit == -1) > @@ -929,7 +947,7 @@ static void do_dump(struct f2fs_sb_info *sbi) > if (opt->blk_addr != -1) > dump_info_from_blkaddr(sbi, opt->blk_addr); > if (opt->nid) > - dump_node(sbi, opt->nid, 0); > + dump_node(sbi, opt->nid, c.force, opt->base_path, 1, 1); > if (opt->scan_nid) > dump_node_scan_disk(sbi, opt->scan_nid); > > diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8 > index 94bf5f3..60d6783 100644 > --- a/man/dump.f2fs.8 > +++ b/man/dump.f2fs.8 > @@ -44,7 +44,8 @@ is used to retrieve f2fs metadata (usually in a disk partition). > \fIdevice\fP is the special file corresponding to the device (e.g. > \fI/dev/sdXX\fP). > > -Currently, it can retrieve 1) a file given its inode number, 2) NAT > +Currently, it can retrieve 1) a file or folder given its inode number > +(folders are dumped recursively), 2) NAT > entries into a file, 3) SIT entries into a file, 4) SSA entries into > a file, 5) reverse information from the given block address. > .PP > @@ -56,6 +57,20 @@ is 0 on success and -1 on failure. > .BI \-i " inode number" > Specify an inode number to dump out. > .TP > +.BI \-r > +Dump out from the root inode. > +.TP > +.BI \-f > +Do not prompt before dumping > +.TP > +.BI \-y > +Alias for \-f > +.TP > +.BI \-o " path" > +Dump inodes to the given path > +.BI \-P > +Preserve mode/owner/group for dumped inode > +.TP > .BI \-I " inode number" > Specify an inode number and scan full disk to dump out, include history inode block > .TP > > base-commit: 5da4e5241503b385e4a7e75b1b2bb3367b38be96 > -- > 2.45.0.rc1.225.g2a3ae87e7f-goog > > > > _______________________________________________ > Linux-f2fs-devel mailing list > Linux-f2fs-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
diff --git a/fsck/dump.c b/fsck/dump.c index b2e990b..f60f6f9 100644 --- a/fsck/dump.c +++ b/fsck/dump.c @@ -247,7 +247,26 @@ out: printf("\n"); } -static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) +static void dump_folder_contents(struct f2fs_sb_info *sbi, u8 *bitmap, + struct f2fs_dir_entry *dentry, + __u8 (*filenames)[F2FS_SLOT_LEN], int max) +{ + int i; + int name_len; + + for (i = 0; i < max; i++) { + if (test_bit_le(i, bitmap) == 0) + continue; + name_len = le16_to_cpu(dentry[i].name_len); + if (name_len == 1 && filenames[i][0] == '.') + continue; + if (name_len == 2 && filenames[i][0] == '.' && filenames[i][1] == '.') + continue; + dump_node(sbi, le32_to_cpu(dentry[i].ino), 1, NULL, 0, 1); + } +} + +static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr, bool is_folder) { char buf[F2FS_BLKSIZE]; @@ -288,12 +307,19 @@ static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) ASSERT(ret >= 0); } - /* write blkaddr */ - dev_write_dump(buf, offset, F2FS_BLKSIZE); + if (is_folder) { + struct f2fs_dentry_block *d = (struct f2fs_dentry_block *) buf; + + dump_folder_contents(sbi, d->dentry_bitmap, F2FS_DENTRY_BLOCK_DENTRIES(d), + F2FS_DENTRY_BLOCK_FILENAMES(d), NR_DENTRY_IN_BLOCK); + } else { + /* write blkaddr */ + dev_write_dump(buf, offset, F2FS_BLKSIZE); + } } static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, - u32 nid, u32 addr_per_block, u64 *ofs) + u32 nid, u32 addr_per_block, u64 *ofs, int is_dir) { struct node_info ni; struct f2fs_node *node_blk; @@ -330,20 +356,20 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, switch (ntype) { case TYPE_DIRECT_NODE: dump_data_blk(sbi, *ofs * F2FS_BLKSIZE, - le32_to_cpu(node_blk->dn.addr[i])); + le32_to_cpu(node_blk->dn.addr[i]), is_dir); (*ofs)++; break; case TYPE_INDIRECT_NODE: dump_node_blk(sbi, TYPE_DIRECT_NODE, le32_to_cpu(node_blk->in.nid[i]), addr_per_block, - ofs); + ofs, is_dir); break; case TYPE_DOUBLE_INDIRECT_NODE: dump_node_blk(sbi, TYPE_INDIRECT_NODE, le32_to_cpu(node_blk->in.nid[i]), addr_per_block, - ofs); + ofs, is_dir); break; } } @@ -435,8 +461,9 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, u32 i = 0; u64 ofs = 0; u32 addr_per_block; + bool is_dir = S_ISDIR(le16_to_cpu(node_blk->i.i_mode)); - if((node_blk->i.i_inline & F2FS_INLINE_DATA)) { + if ((node_blk->i.i_inline & F2FS_INLINE_DATA)) { DBG(3, "ino[0x%x] has inline data!\n", nid); /* recover from inline data */ dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET, @@ -444,13 +471,25 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, return -1; } + if ((node_blk->i.i_inline & F2FS_INLINE_DENTRY)) { + void *inline_dentry = inline_data_addr(node_blk); + struct f2fs_dentry_ptr d; + + make_dentry_ptr(&d, node_blk, inline_dentry, 2); + + DBG(3, "ino[0x%x] has inline dentries!\n", nid); + /* recover from inline dentry */ + dump_folder_contents(sbi, d.bitmap, d.dentry, d.filename, d.max); + return -1; + } + c.show_file_map_max_offset = f2fs_max_file_offset(&node_blk->i); addr_per_block = ADDRS_PER_BLOCK(&node_blk->i); /* check data blocks in inode */ for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++) dump_data_blk(sbi, ofs * F2FS_BLKSIZE, le32_to_cpu( - node_blk->i.i_addr[get_extra_isize(node_blk) + i])); + node_blk->i.i_addr[get_extra_isize(node_blk) + i]), is_dir); /* check node blocks in inode */ for (i = 0; i < 5; i++) { @@ -458,17 +497,20 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, dump_node_blk(sbi, TYPE_DIRECT_NODE, le32_to_cpu(F2FS_INODE_I_NID(&node_blk->i, i)), addr_per_block, - &ofs); + &ofs, + is_dir); else if (i == 2 || i == 3) dump_node_blk(sbi, TYPE_INDIRECT_NODE, le32_to_cpu(F2FS_INODE_I_NID(&node_blk->i, i)), addr_per_block, - &ofs); + &ofs, + is_dir); else if (i == 4) dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE, le32_to_cpu(F2FS_INODE_I_NID(&node_blk->i, i)), addr_per_block, - &ofs); + &ofs, + is_dir); else ASSERT(0); } @@ -479,8 +521,44 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, return 0; } -static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, - struct f2fs_node *node_blk, int force) +static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, + struct f2fs_node *node_blk, char *path) +{ + struct f2fs_inode *inode = &node_blk->i; + int ret; + + c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666); + ASSERT(c.dump_fd >= 0); + + /* dump file's data */ + dump_inode_blk(sbi, ni->ino, node_blk); + + /* adjust file size */ + ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size)); + ASSERT(ret >= 0); + + close(c.dump_fd); +} + +static void dump_folder(struct f2fs_sb_info *sbi, struct node_info *ni, + struct f2fs_node *node_blk, char *path, int is_root) +{ + if (!is_root) { + if (mkdir(path, 0777) < 0 && errno != EEXIST) { + MSG(0, "Failed to create directory %s\n", path); + return; + } + ASSERT(chdir(path) == 0); + } + /* dump folder data */ + dump_inode_blk(sbi, ni->ino, node_blk); + if (!is_root) + ASSERT(chdir("..") == 0); +} + +static int dump_filesystem(struct f2fs_sb_info *sbi, struct node_info *ni, + struct f2fs_node *node_blk, int force, char *base_path, + bool is_base, bool allow_folder) { struct f2fs_inode *inode = &node_blk->i; u32 imode = le16_to_cpu(inode->i_mode); @@ -489,6 +567,7 @@ static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, char path[1024] = {0}; char ans[255] = {0}; int is_encrypted = file_is_encrypt(inode); + int is_root = sbi->root_ino_num == ni->nid; int ret; if (is_encrypted) { @@ -496,11 +575,15 @@ static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, return -1; } - if ((!S_ISREG(imode) && !S_ISLNK(imode)) || - namelen == 0 || namelen > F2FS_NAME_LEN) { - MSG(force, "Not a regular file or wrong name info\n\n"); + if ((!S_ISREG(imode) && !S_ISLNK(imode) && !(S_ISDIR(imode) && allow_folder))) { + MSG(force, "Not a regular file\n\n"); return -1; } + if (!is_root && (namelen == 0 || namelen > F2FS_NAME_LEN)) { + MSG(force, "Wrong name info\n\n"); + return -1; + } + base_path = base_path ?: "./lost_found"; if (force) goto dump; @@ -508,31 +591,42 @@ static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, if (c.show_file_map) return dump_inode_blk(sbi, ni->ino, node_blk); - printf("Do you want to dump this file into ./lost_found/? [Y/N] "); + printf("Do you want to dump this %s into %s/? [Y/N] ", + S_ISREG(imode) || S_ISLNK(imode) ? "file" : "folder", + base_path); ret = scanf("%s", ans); ASSERT(ret >= 0); if (!strcasecmp(ans, "y")) { dump: - ret = system("mkdir -p ./lost_found"); - ASSERT(ret >= 0); + if (is_base) { + getcwd(path, sizeof(path)); + ret = mkdir(base_path, 0777); + ASSERT(ret == 0 || errno == EEXIST); + ASSERT(chdir(base_path) == 0); + } /* make a file */ - strncpy(name, (const char *)inode->i_name, namelen); - name[namelen] = 0; - sprintf(path, "./lost_found/%s", name); - - c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666); - ASSERT(c.dump_fd >= 0); - - /* dump file's data */ - dump_inode_blk(sbi, ni->ino, node_blk); + if (!is_root) { + strncpy(name, (const char *)inode->i_name, namelen); + name[namelen] = 0; + } - /* adjust file size */ - ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size)); - ASSERT(ret >= 0); + if (S_ISREG(imode) || S_ISLNK(imode)) { + dump_file(sbi, ni, node_blk, name); + } else { + dump_folder(sbi, ni, node_blk, name, is_root); + } - close(c.dump_fd); + /* fix up mode/owner */ + if (c.preserve_perms) { + if (is_root) + strncpy(name, ".", 2); + chmod(name, imode); + chown(name, inode->i_uid, inode->i_gid); + } + if (is_base) + chdir(path); } return 0; } @@ -582,7 +676,7 @@ void dump_node_scan_disk(struct f2fs_sb_info *sbi, nid_t nid) free(node_blk); } -int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force) +int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force, char *base_path, int base, int allow_folder) { struct node_info ni; struct f2fs_node *node_blk; @@ -617,7 +711,7 @@ int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force) print_node_info(sbi, node_blk, force); if (ni.ino == ni.nid) - ret = dump_file(sbi, &ni, node_blk, force); + ret = dump_filesystem(sbi, &ni, node_blk, force, base_path, base, allow_folder); } else { print_node_info(sbi, node_blk, force); MSG(force, "Invalid (i)node block\n\n"); diff --git a/fsck/fsck.c b/fsck/fsck.c index 5d345d0..7400dcf 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -1651,7 +1651,7 @@ static void print_dentry(struct f2fs_sb_info *sbi, __u8 *name, d = d->next; } printf("/%s", new); - if (dump_node(sbi, le32_to_cpu(dentry[idx].ino), 0)) + if (dump_node(sbi, le32_to_cpu(dentry[idx].ino), 0, NULL, 0, 0)) printf("\33[2K\r"); } else { for (i = 1; i < depth; i++) @@ -3632,7 +3632,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) if (!strcasecmp(ans, "y")) { for (i = 0; i < fsck->nr_nat_entries; i++) { if (f2fs_test_bit(i, fsck->nat_area_bitmap)) - dump_node(sbi, i, 1); + dump_node(sbi, i, 1, NULL, 1, 0); } } } diff --git a/fsck/fsck.h b/fsck/fsck.h index f5282e2..6cac926 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -270,12 +270,14 @@ struct dump_option { int end_ssa; int32_t blk_addr; nid_t scan_nid; + int use_root_nid; + char *base_path; }; extern void nat_dump(struct f2fs_sb_info *, nid_t, nid_t); extern void sit_dump(struct f2fs_sb_info *, unsigned int, unsigned int); extern void ssa_dump(struct f2fs_sb_info *, int, int); -extern int dump_node(struct f2fs_sb_info *, nid_t, int); +extern int dump_node(struct f2fs_sb_info *, nid_t, int, char *, int, int); extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32); extern unsigned int start_bidx_of_node(unsigned int, struct f2fs_node *); extern void dump_node_scan_disk(struct f2fs_sb_info *sbi, nid_t nid); diff --git a/fsck/main.c b/fsck/main.c index c4d0956..7d0578a 100644 --- a/fsck/main.c +++ b/fsck/main.c @@ -34,7 +34,7 @@ struct f2fs_fsck gfsck; INIT_FEATURE_TABLE; -#ifdef WITH_SLOAD +#if defined(WITH_SLOAD) || defined(WITH_DUMP) static char *absolute_path(const char *file) { char *ret; @@ -384,7 +384,7 @@ void f2fs_parse_options(int argc, char *argv[]) } } else if (!strcmp("dump.f2fs", prog)) { #ifdef WITH_DUMP - const char *option_string = "d:i:I:n:Ms:Sa:b:V"; + const char *option_string = "d:fi:I:n:Mo:Prs:Sa:b:Vy"; static struct dump_option dump_opt = { .nid = 0, /* default root ino */ .start_nat = -1, @@ -395,6 +395,8 @@ void f2fs_parse_options(int argc, char *argv[]) .end_ssa = -1, .blk_addr = -1, .scan_nid = 0, + .use_root_nid = 0, + .base_path = NULL, }; c.func = DUMP; @@ -456,6 +458,19 @@ void f2fs_parse_options(int argc, char *argv[]) ret = sscanf(optarg, "%x", &dump_opt.blk_addr); break; + case 'y': + case 'f': + c.force = 1; + break; + case 'r': + dump_opt.use_root_nid = 1; + break; + case 'o': + dump_opt.base_path = absolute_path(optarg); + break; + case 'P': + c.preserve_perms = 1; + break; case 'V': show_version(prog); exit(0); @@ -914,6 +929,9 @@ static void do_dump(struct f2fs_sb_info *sbi) struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); u32 flag = le32_to_cpu(ckpt->ckpt_flags); + if (opt->use_root_nid) + opt->nid = sbi->root_ino_num; + if (opt->end_nat == -1) opt->end_nat = NM_I(sbi)->max_nid; if (opt->end_sit == -1) @@ -929,7 +947,7 @@ static void do_dump(struct f2fs_sb_info *sbi) if (opt->blk_addr != -1) dump_info_from_blkaddr(sbi, opt->blk_addr); if (opt->nid) - dump_node(sbi, opt->nid, 0); + dump_node(sbi, opt->nid, c.force, opt->base_path, 1, 1); if (opt->scan_nid) dump_node_scan_disk(sbi, opt->scan_nid); diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8 index 94bf5f3..60d6783 100644 --- a/man/dump.f2fs.8 +++ b/man/dump.f2fs.8 @@ -44,7 +44,8 @@ is used to retrieve f2fs metadata (usually in a disk partition). \fIdevice\fP is the special file corresponding to the device (e.g. \fI/dev/sdXX\fP). -Currently, it can retrieve 1) a file given its inode number, 2) NAT +Currently, it can retrieve 1) a file or folder given its inode number +(folders are dumped recursively), 2) NAT entries into a file, 3) SIT entries into a file, 4) SSA entries into a file, 5) reverse information from the given block address. .PP @@ -56,6 +57,20 @@ is 0 on success and -1 on failure. .BI \-i " inode number" Specify an inode number to dump out. .TP +.BI \-r +Dump out from the root inode. +.TP +.BI \-f +Do not prompt before dumping +.TP +.BI \-y +Alias for \-f +.TP +.BI \-o " path" +Dump inodes to the given path +.BI \-P +Preserve mode/owner/group for dumped inode +.TP .BI \-I " inode number" Specify an inode number and scan full disk to dump out, include history inode block .TP
This adds the ability to dump folders as well as files. Folders are dumped recursively. Additionally, dumped files/folders may be directed to a folder specified by -o [path] instead of ./lost_found. The -r flag will dump the entire fs from the root inode. -f or -y will skip the prompt before dumping, and -P will preserve the mode/owner info for the created file/folder. Signed-off-by: Daniel Rosenberg <drosen@google.com> --- fsck/dump.c | 164 +++++++++++++++++++++++++++++++++++++----------- fsck/fsck.c | 4 +- fsck/fsck.h | 4 +- fsck/main.c | 24 ++++++- man/dump.f2fs.8 | 17 ++++- 5 files changed, 171 insertions(+), 42 deletions(-) base-commit: 5da4e5241503b385e4a7e75b1b2bb3367b38be96