All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Rosenberg via Linux-f2fs-devel <linux-f2fs-devel@lists.sourceforge.net>
To: linux-f2fs-devel@lists.sourceforge.net
Cc: Jaegeuk Kim <jaegeuk@kernel.org>,
	kernel-team@android.com, Daeho Jeong <daehojeong@google.com>,
	Daniel Rosenberg <drosen@google.com>
Subject: [f2fs-dev] [PATCH v4 1/2] dump.f2fs: Add ability to dump folders
Date: Thu, 23 May 2024 15:46:59 -0700	[thread overview]
Message-ID: <20240523224700.265251-1-drosen@google.com> (raw)

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>
Reviewed-by: Daeho Jeong <daehojeong@google.com>
---
 fsck/dump.c     | 178 ++++++++++++++++++++++++++++++++++++++----------
 fsck/fsck.c     |   4 +-
 fsck/fsck.h     |   4 +-
 fsck/main.c     |  29 +++++++-
 man/dump.f2fs.8 |  17 ++++-
 5 files changed, 190 insertions(+), 42 deletions(-)

diff --git a/fsck/dump.c b/fsck/dump.c
index b2e990b..fa68456 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,51 @@ 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 defined(__MINGW32__)
+		if (mkdir(path) < 0 && errno != EEXIST) {
+			MSG(0, "Failed to create directory %s\n", path);
+			return;
+		}
+#else
+		if (mkdir(path, 0777) < 0 && errno != EEXIST) {
+			MSG(0, "Failed to create directory %s\n", path);
+			return;
+		}
+#endif
+		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 +574,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 +582,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 +598,49 @@ 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);
-
-		/* make a file */
-		strncpy(name, (const char *)inode->i_name, namelen);
-		name[namelen] = 0;
-		sprintf(path, "./lost_found/%s", name);
+		if (is_base) {
+			ASSERT(getcwd(path, sizeof(path)) != NULL);
+#if defined(__MINGW32__)
+			ret = mkdir(base_path);
+#else
+			ret = mkdir(base_path, 0777);
+#endif
 
-		c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666);
-		ASSERT(c.dump_fd >= 0);
+			ASSERT(ret == 0 || errno == EEXIST);
+			ASSERT(chdir(base_path) == 0);
+		}
 
-		/* dump file's data */
-		dump_inode_blk(sbi, ni->ino, node_blk);
+		/* make a file */
+		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);
+#if !defined(__MINGW32__)
+		/* fix up mode/owner */
+		if (c.preserve_perms) {
+			if (is_root)
+				strncpy(name, ".", 2);
+			ASSERT(chmod(name, imode) == 0);
+			ASSERT(chown(name, inode->i_uid, inode->i_gid) == 0);
+		}
+#endif
+		if (is_base)
+			ASSERT(chdir(path) == 0);
 	}
 	return 0;
 }
@@ -582,7 +690,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 +725,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..6edc902 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,24 @@ 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':
+#if defined(__MINGW32__)
+				MSG(0, "-P not supported for Windows\n");
+				err = EWRONG_OPT;
+#else
+				c.preserve_perms = 1;
+#endif
+				break;
 			case 'V':
 				show_version(prog);
 				exit(0);
@@ -914,6 +934,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 +952,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.1.288.g0e0cd299f1-goog



_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

             reply	other threads:[~2024-05-23 22:47 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-23 22:46 Daniel Rosenberg via Linux-f2fs-devel [this message]
2024-05-23 22:47 ` [f2fs-dev] [PATCH v4 2/2] dump.f2fs: Fix xattr dumping Daniel Rosenberg via Linux-f2fs-devel
2024-05-23 23:34   ` Daeho Jeong
2024-05-29  6:41   ` Chao Yu
2024-05-23 23:33 ` [f2fs-dev] [PATCH v4 1/2] dump.f2fs: Add ability to dump folders Daeho Jeong
2024-05-29  6:39 ` Chao Yu
2024-05-30 23:41   ` Jaegeuk Kim
2024-05-31  1:13     ` Chao Yu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240523224700.265251-1-drosen@google.com \
    --to=linux-f2fs-devel@lists.sourceforge.net \
    --cc=daehojeong@google.com \
    --cc=drosen@google.com \
    --cc=jaegeuk@kernel.org \
    --cc=kernel-team@android.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.