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 <dro...@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..aecc293 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 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

Reply via email to