On 07/23, Daniel Rosenberg wrote:
> Previously, dumped symlinks would always create regular files instead.
> This allows symlinks to be dumped as symlinks with the -L option.
> 
> The i_name field's name may not be the same as the actual name from the
> dirent, so we use the dirent name when available.
> 
> Currently hardlinks aren't detected, so print a warning if we notice a
> nondirectory with a link count over 1.
> 
> Signed-off-by: Daniel Rosenberg <dro...@google.com>
> ---
> 
> V3: Fixed le32_to_cpu => le64_to_cpu
> V2: Fixed some issues on the Windows build. Also S_ISLNK is defined as
> false for the Windows build, so I adjusted some checks to not rely on that.
> Removed some unused variables.
> 
>  fsck/dump.c       | 121 ++++++++++++++++++++++++++++++++++------------
>  fsck/fsck.c       |   4 +-
>  fsck/fsck.h       |   2 +-
>  fsck/main.c       |  13 ++++-
>  include/f2fs_fs.h |   8 +++
>  lib/libf2fs_io.c  |  10 ++++
>  man/dump.f2fs.8   |   3 ++
>  7 files changed, 125 insertions(+), 36 deletions(-)
> 
> diff --git a/fsck/dump.c b/fsck/dump.c
> index 8d5613e..493a80d 100644
> --- a/fsck/dump.c
> +++ b/fsck/dump.c
> @@ -253,20 +253,27 @@ static void dump_folder_contents(struct f2fs_sb_info 
> *sbi, u8 *bitmap,
>  {
>       int i;
>       int name_len;
> +     char name[F2FS_NAME_LEN + 1] = {0};
>  
>       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 == 0 || name_len > F2FS_NAME_LEN) {
> +                     MSG(c.force, "Wrong name info\n\n");
> +                     ASSERT(name_len == 0 || name_len > F2FS_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);
> +             strncpy(name, (const char *)filenames[i], name_len);
> +             name[name_len] = 0;
> +             dump_node(sbi, le32_to_cpu(dentry[i].ino), 1, NULL, 0, 1, name);
>       }
>  }
>  
> -static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 
> blkaddr, bool is_folder)
> +static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 
> blkaddr, int type)
>  {
>       char buf[F2FS_BLKSIZE];
>  
> @@ -307,11 +314,15 @@ static void dump_data_blk(struct f2fs_sb_info *sbi, 
> __u64 offset, u32 blkaddr, b
>               ASSERT(ret >= 0);
>       }
>  
> -     if (is_folder) {
> +     if (S_ISDIR(type)) {
>               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);
> +#if !defined(__MINGW32__)
> +     } if (S_ISLNK(type)) {
> +             dev_write_symlink(buf, c.dump_sym_target_len);
> +#endif
>       } else {
>               /* write blkaddr */
>               dev_write_dump(buf, offset, F2FS_BLKSIZE);
> @@ -319,7 +330,7 @@ static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 
> offset, u32 blkaddr, b
>  }
>  
>  static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
> -                             u32 nid, u32 addr_per_block, u64 *ofs, int 
> is_dir)
> +                             u32 nid, u32 addr_per_block, u64 *ofs, int type)
>  {
>       struct node_info ni;
>       struct f2fs_node *node_blk;
> @@ -356,20 +367,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]), 
> is_dir);
> +                                     le32_to_cpu(node_blk->dn.addr[i]), 
> type);
>                       (*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, is_dir);
> +                                     ofs, type);
>                       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, is_dir);
> +                                     ofs, type);
>                       break;
>               }
>       }
> @@ -377,7 +388,7 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int 
> ntype,
>  }
>  
>  #ifdef HAVE_FSETXATTR
> -static void dump_xattr(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk, 
> int is_dir)
> +static void dump_xattr(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk, 
> int type)
>  {
>       void *xattr;
>       void *last_base_addr;
> @@ -431,19 +442,26 @@ static void dump_xattr(struct f2fs_sb_info *sbi, struct 
> f2fs_node *node_blk, int
>  
>               DBG(1, "fd %d xattr_name %s\n", c.dump_fd, xattr_name);
>  #if defined(__linux__)
> -             if (is_dir) {
> +             if (S_ISDIR(type)) {
>                       ret = setxattr(".", xattr_name, value,
>                                                       
> le16_to_cpu(ent->e_value_size), 0);
> +             } if (S_ISLNK(type) && c.preserve_symlinks) {
> +                     ret = lsetxattr(c.dump_symlink, xattr_name, value,
> +                                                     
> le16_to_cpu(ent->e_value_size), 0);
>               } else {
>                       ret = fsetxattr(c.dump_fd, xattr_name, value,
>                                                       
> le16_to_cpu(ent->e_value_size), 0);
>               }
>  
>  #elif defined(__APPLE__)
> -             if (is_dir) {
> +             if (S_ISDIR(type)) {
>                       ret = setxattr(".", xattr_name, value,
>                                       le16_to_cpu(ent->e_value_size), 0,
>                                       XATTR_CREATE);
> +             } if (S_ISLNK(type) && c.preserve_symlinks) {
> +                     ret = lsetxattr(c.dump_symlink, xattr_name, value,
> +                                     le16_to_cpu(ent->e_value_size), 0,
> +                                     XATTR_CREATE);
>               } else {
>                       ret = fsetxattr(c.dump_fd, xattr_name, value,
>                                       le16_to_cpu(ent->e_value_size), 0,
> @@ -473,14 +491,21 @@ 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));
> +     u16 type = le16_to_cpu(node_blk->i.i_mode);
>       int ret = 0;
>  
>       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(inline_data_addr(node_blk),
> +#if !defined(__MINGW32__)
> +             if (S_ISLNK(type) && c.preserve_symlinks) {
> +                     dev_write_symlink(inline_data_addr(node_blk), 
> c.dump_sym_target_len);
> +             } else
> +#endif
> +             {
> +                     dev_write_dump(inline_data_addr(node_blk),
>                                               0, MAX_INLINE_DATA(node_blk));
> +             }
>               ret = -1;
>               goto dump_xattr;
>       }
> @@ -504,7 +529,7 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 
> nid,
>       /* 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]), 
> is_dir);
> +                     node_blk->i.i_addr[get_extra_isize(node_blk) + i]), 
> type);
>  
>       /* check node blocks in inode */
>       for (i = 0; i < 5; i++) {
> @@ -513,26 +538,26 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 
> nid,
>                                       
> le32_to_cpu(F2FS_INODE_I_NID(&node_blk->i, i)),
>                                       addr_per_block,
>                                       &ofs,
> -                                     is_dir);
> +                                     type);
>               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,
> -                                     is_dir);
> +                                     type);
>               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,
> -                                     is_dir);
> +                                     type);
>               else
>                       ASSERT(0);
>       }
>       /* last block in extent cache */
>       print_extent(true);
>  dump_xattr:
> -     dump_xattr(sbi, node_blk, is_dir);
> +     dump_xattr(sbi, node_blk, type);
>       return ret;
>  }
>  
> @@ -555,6 +580,23 @@ static void dump_file(struct f2fs_sb_info *sbi, struct 
> node_info *ni,
>       close(c.dump_fd);
>  }
>  
> +static void dump_link(struct f2fs_sb_info *sbi, struct node_info *ni,
> +                             struct f2fs_node *node_blk, char *name)
> +{
> +#if defined(__MINGW32__)
> +     dump_file(sbi, ni, node_blk, name);
> +#else
> +     struct f2fs_inode *inode = &node_blk->i;
> +     int len = le64_to_cpu(inode->i_size);
> +
> +     if (!c.preserve_symlinks)
> +             return dump_file(sbi, ni, node_blk, name);
> +     c.dump_symlink = name;
> +     c.dump_sym_target_len = len + 1;
> +     dump_inode_blk(sbi, ni->ino, node_blk);
> +#endif
> +}
> +
>  static void dump_folder(struct f2fs_sb_info *sbi, struct node_info *ni,
>                               struct f2fs_node *node_blk, char *path, int 
> is_root)
>  {
> @@ -580,18 +622,24 @@ static void dump_folder(struct f2fs_sb_info *sbi, 
> struct node_info *ni,
>  
>  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)
> +                             bool is_base, bool allow_folder, char 
> *dirent_name)
>  {
>       struct f2fs_inode *inode = &node_blk->i;
>       u32 imode = le16_to_cpu(inode->i_mode);
> -     u32 namelen = le32_to_cpu(inode->i_namelen);
> -     char name[F2FS_NAME_LEN + 1] = {0};
> +     u32 ilinks = le32_to_cpu(inode->i_links);
> +     u32 i_namelen = le32_to_cpu(inode->i_namelen);
> +     char i_name[F2FS_NAME_LEN + 1] = {0};
> +     char *name;

        char *name = NULL;
Assigned NULL to avoid build warning.

>       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 (!S_ISDIR(imode) && ilinks != 1) {
> +             MSG(force, "Warning: Hard link detected. Dumped files may be 
> duplicated\n");
> +     }
> +
>       if (is_encrypted) {
>               MSG(force, "File is encrypted\n");
>               return -1;
> @@ -601,7 +649,7 @@ static int dump_filesystem(struct f2fs_sb_info *sbi, 
> struct node_info *ni,
>               MSG(force, "Not a valid file type\n\n");
>               return -1;
>       }
> -     if (!is_root && (namelen == 0 || namelen > F2FS_NAME_LEN)) {
> +     if (!is_root && !dirent_name && (i_namelen == 0 || i_namelen > 
> F2FS_NAME_LEN)) {
>               MSG(force, "Wrong name info\n\n");
>               return -1;
>       }
> @@ -614,7 +662,7 @@ static int dump_filesystem(struct f2fs_sb_info *sbi, 
> struct node_info *ni,
>               return dump_inode_blk(sbi, ni->ino, node_blk);
>  
>       printf("Do you want to dump this %s into %s/? [Y/N] ",
> -                     S_ISREG(imode) || S_ISLNK(imode) ? "file" : "folder",
> +                     S_ISDIR(imode) ? "folder" : "file",
>                       base_path);
>       ret = scanf("%s", ans);
>       ASSERT(ret >= 0);
> @@ -635,23 +683,34 @@ dump:
>  
>               /* make a file */
>               if (!is_root) {
> -                     strncpy(name, (const char *)inode->i_name, namelen);
> -                     name[namelen] = 0;
> +                     /* The i_name name may be out of date. Prefer 
> dirent_name */
> +                     if (dirent_name) {
> +                             name = dirent_name;
> +                     } else  {
> +                             strncpy(i_name, (const char *)inode->i_name, 
> i_namelen);
> +                             i_name[i_namelen] = 0;
> +                             name = i_name;
> +                     }
>               }
>  
> -             if (S_ISREG(imode) || S_ISLNK(imode)) {
> +             if (S_ISREG(imode)) {
>                       dump_file(sbi, ni, node_blk, name);
> -             } else {
> +             } else if (S_ISDIR(imode)) {
>                       dump_folder(sbi, ni, node_blk, name, is_root);
> +             } else {
> +                     dump_link(sbi, ni, node_blk, name);
>               }
>  
>  #if !defined(__MINGW32__)
>               /* fix up mode/owner */
>               if (c.preserve_perms) {
> -                     if (is_root)
> +                     if (is_root) {
> +                             name = i_name;
>                               strncpy(name, ".", 2);
> -                     ASSERT(chmod(name, imode) == 0);
> -                     ASSERT(chown(name, inode->i_uid, inode->i_gid) == 0);
> +                     }
> +                     if (!S_ISLNK(imode))
> +                             ASSERT(chmod(name, imode) == 0);
> +                     ASSERT(lchown(name, inode->i_uid, inode->i_gid) == 0);
>               }
>  #endif
>               if (is_base)
> @@ -705,7 +764,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, char 
> *base_path, int base, int allow_folder)
> +int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force, char 
> *base_path, int base, int allow_folder, char *dirent_name)
>  {
>       struct node_info ni;
>       struct f2fs_node *node_blk;
> @@ -740,7 +799,7 @@ int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int 
> force, char *base_path, i
>                       print_node_info(sbi, node_blk, force);
>  
>               if (ni.ino == ni.nid)
> -                     ret = dump_filesystem(sbi, &ni, node_blk, force, 
> base_path, base, allow_folder);
> +                     ret = dump_filesystem(sbi, &ni, node_blk, force, 
> base_path, base, allow_folder, dirent_name);
>       } 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 7400dcf..b79b354 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, NULL, 0, 0))
> +             if (dump_node(sbi, le32_to_cpu(dentry[idx].ino), 0, NULL, 0, 0, 
> NULL))
>                       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, NULL, 1, 0);
> +                                     dump_node(sbi, i, 1, NULL, 1, 0, NULL);
>                       }
>               }
>       }
> diff --git a/fsck/fsck.h b/fsck/fsck.h
> index 6cac926..476b436 100644
> --- a/fsck/fsck.h
> +++ b/fsck/fsck.h
> @@ -277,7 +277,7 @@ struct dump_option {
>  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, char *, int, int);
> +extern int dump_node(struct f2fs_sb_info *, nid_t, int, char *, int, int, 
> char *);
>  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 c13e287..af00fa5 100644
> --- a/fsck/main.c
> +++ b/fsck/main.c
> @@ -102,6 +102,7 @@ void dump_usage()
>       MSG(0, "  -y alias for -f\n");
>       MSG(0, "  -o dump inodes to the given path\n");
>       MSG(0, "  -P preserve mode/owner/group for dumped inode\n");
> +     MSG(0, "  -L Preserves symlinks. Otherwise symlinks are dumped as 
> regular files.\n");
>       MSG(0, "  -V print the version number and exit\n");
>  
>       exit(1);
> @@ -389,7 +390,7 @@ void f2fs_parse_options(int argc, char *argv[])
>               }
>       } else if (!strcmp("dump.f2fs", prog)) {
>  #ifdef WITH_DUMP
> -             const char *option_string = "d:fi:I:n:Mo:Prs:Sa:b:Vy";
> +             const char *option_string = "d:fi:I:n:LMo:Prs:Sa:b:Vy";
>               static struct dump_option dump_opt = {
>                       .nid = 0,       /* default root ino */
>                       .start_nat = -1,
> @@ -479,6 +480,14 @@ void f2fs_parse_options(int argc, char *argv[])
>                               err = EWRONG_OPT;
>  #else
>                               c.preserve_perms = 1;
> +#endif
> +                             break;
> +                     case 'L':
> +#if defined(__MINGW32__)
> +                             MSG(0, "-L not supported for Windows\n");
> +                             err = EWRONG_OPT;
> +#else
> +                             c.preserve_symlinks = 1;
>  #endif
>                               break;
>                       case 'V':
> @@ -957,7 +966,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, c.force, opt->base_path, 1, 1);
> +             dump_node(sbi, opt->nid, c.force, opt->base_path, 1, 1, NULL);
>       if (opt->scan_nid)
>               dump_node_scan_disk(sbi, opt->scan_nid);
>  
> diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
> index 870a6e4..08ba32d 100644
> --- a/include/f2fs_fs.h
> +++ b/include/f2fs_fs.h
> @@ -1478,6 +1478,8 @@ struct f2fs_configuration {
>       uint16_t s_encoding_flags;
>       int32_t kd;
>       int32_t dump_fd;
> +     char *dump_symlink;
> +     int dump_sym_target_len;
>       struct device_info devices[MAX_DEVICES];
>       int ndevs;
>       char *extension_list[2];
> @@ -1540,7 +1542,10 @@ struct f2fs_configuration {
>       struct selinux_opt seopt_file[8];
>       int nr_opt;
>  #endif
> +
> +     /* dump parameters */
>       int preserve_perms;
> +     int preserve_symlinks;
>  
>       /* resize parameters */
>       int safe_resize;
> @@ -1614,6 +1619,9 @@ extern int dev_readahead(__u64, size_t UNUSED(len));
>  extern int dev_write(void *, __u64, size_t);
>  extern int dev_write_block(void *, __u64);
>  extern int dev_write_dump(void *, __u64, size_t);
> +#if !defined(__MINGW32__)
> +extern int dev_write_symlink(char *, size_t);
> +#endif
>  /* All bytes in the buffer must be 0 use dev_fill(). */
>  extern int dev_fill(void *, __u64, size_t);
>  extern int dev_fill_block(void *, __u64);
> diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c
> index b2d6933..f39367a 100644
> --- a/lib/libf2fs_io.c
> +++ b/lib/libf2fs_io.c
> @@ -598,6 +598,16 @@ int dev_write_dump(void *buf, __u64 offset, size_t len)
>       return 0;
>  }
>  
> +#if !defined(__MINGW32__)
> +int dev_write_symlink(char *buf, size_t len)
> +{
> +     buf[len] = 0;
> +     if (symlink(buf, c.dump_symlink))
> +             return -1;
> +     return 0;
> +}
> +#endif
> +
>  int dev_fill(void *buf, __u64 offset, size_t len)
>  {
>       int fd;
> diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
> index 60d6783..4035d57 100644
> --- a/man/dump.f2fs.8
> +++ b/man/dump.f2fs.8
> @@ -71,6 +71,9 @@ Dump inodes to the given path
>  .BI \-P
>  Preserve mode/owner/group for dumped inode
>  .TP
> +.BI \-L
> +Preserves symlinks. Otherwise symlinks are dumped as regular files.
> +.TP
>  .BI \-I " inode number"
>  Specify an inode number and scan full disk to dump out, include history 
> inode block
>  .TP
> 
> base-commit: 584ebc710bc0779381595135e0686492c3908a20
> -- 
> 2.45.2.1089.g2a221341d9-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