* [f2fs-dev] [PATCH v3] dump.f2fs: Dump symlinks as symlinks
@ 2024-07-23 21:11 Daniel Rosenberg via Linux-f2fs-devel
2024-07-25 2:27 ` Chao Yu
2024-07-25 17:46 ` Jaegeuk Kim
0 siblings, 2 replies; 3+ messages in thread
From: Daniel Rosenberg via Linux-f2fs-devel @ 2024-07-23 21:11 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: Jaegeuk Kim, kernel-team, Daniel Rosenberg
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 <drosen@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 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
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [f2fs-dev] [PATCH v3] dump.f2fs: Dump symlinks as symlinks
2024-07-23 21:11 [f2fs-dev] [PATCH v3] dump.f2fs: Dump symlinks as symlinks Daniel Rosenberg via Linux-f2fs-devel
@ 2024-07-25 2:27 ` Chao Yu
2024-07-25 17:46 ` Jaegeuk Kim
1 sibling, 0 replies; 3+ messages in thread
From: Chao Yu @ 2024-07-25 2:27 UTC (permalink / raw)
To: Daniel Rosenberg, linux-f2fs-devel; +Cc: Jaegeuk Kim, kernel-team
On 2024/7/24 5:11, 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 <drosen@google.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Thanks,
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [f2fs-dev] [PATCH v3] dump.f2fs: Dump symlinks as symlinks
2024-07-23 21:11 [f2fs-dev] [PATCH v3] dump.f2fs: Dump symlinks as symlinks Daniel Rosenberg via Linux-f2fs-devel
2024-07-25 2:27 ` Chao Yu
@ 2024-07-25 17:46 ` Jaegeuk Kim
1 sibling, 0 replies; 3+ messages in thread
From: Jaegeuk Kim @ 2024-07-25 17:46 UTC (permalink / raw)
To: Daniel Rosenberg; +Cc: kernel-team, linux-f2fs-devel
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 <drosen@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
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2024-07-25 17:46 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-23 21:11 [f2fs-dev] [PATCH v3] dump.f2fs: Dump symlinks as symlinks Daniel Rosenberg via Linux-f2fs-devel
2024-07-25 2:27 ` Chao Yu
2024-07-25 17:46 ` Jaegeuk Kim
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.