diff mbox series

[f2fs-dev,RFC,v2,09/10] inject.f2fs: add dentry injection

Message ID 20240628013140.2444209-10-shengyong@oppo.com (mailing list archive)
State Superseded
Headers show
Series f2fs-tools: introduce inject.f2fs | expand

Commit Message

Sheng Yong June 28, 2024, 1:31 a.m. UTC
This patch enables injecting dentry block or dir entry.
print_raw_dentry_info() is added to show values of node footer.

The meanings of options are:
 * dent: means dentry is injected.

The members could be injected in cp contains:
 * d_bitmap: dentry block d_bitmap of nid
 * d_hash: dentry hash
 * d_ino: dentry ino
 * d_ftype: dentry ftype

Signed-off-by: Sheng Yong <shengyong@oppo.com>
---
 fsck/inject.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fsck/inject.h |   1 +
 2 files changed, 180 insertions(+)

Comments

Chao Yu July 2, 2024, 7:01 a.m. UTC | #1
On 2024/6/28 9:31, Sheng Yong wrote:
> This patch enables injecting dentry block or dir entry.
> print_raw_dentry_info() is added to show values of node footer.
> 
> The meanings of options are:
>   * dent: means dentry is injected.
> 
> The members could be injected in cp contains:
>   * d_bitmap: dentry block d_bitmap of nid
>   * d_hash: dentry hash
>   * d_ino: dentry ino
>   * d_ftype: dentry ftype
> 
> Signed-off-by: Sheng Yong <shengyong@oppo.com>
> ---
>   fsck/inject.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   fsck/inject.h |   1 +
>   2 files changed, 180 insertions(+)
> 
> diff --git a/fsck/inject.c b/fsck/inject.c
> index c48728e..bae9afb 100644
> --- a/fsck/inject.c
> +++ b/fsck/inject.c
> @@ -10,6 +10,7 @@
>    */
>   
>   #include <getopt.h>
> +#include "node.h"
>   #include "inject.h"
>   
>   static void print_raw_nat_entry_info(struct f2fs_nat_entry *ne)
> @@ -74,6 +75,17 @@ static void print_node_footer_info(struct node_footer *footer)
>   	DISP_u32(footer, next_blkaddr);
>   }
>   
> +static void print_raw_dentry_info(struct f2fs_dir_entry *dentry)
> +{
> +	if (!c.dbg_lv)
> +		return;
> +
> +	DISP_u32(dentry, hash_code);
> +	DISP_u32(dentry, ino);
> +	DISP_u16(dentry, name_len);
> +	DISP_u8(dentry, file_type);
> +}
> +
>   void inject_usage(void)
>   {
>   	MSG(0, "\nUsage: inject.f2fs [options] device\n");
> @@ -92,6 +104,7 @@ void inject_usage(void)
>   	MSG(0, "  --sit <0|1|2> --mb <name> --blk <blk> [--idx <index>] --val <value> inject sit entry\n");
>   	MSG(0, "  --ssa --mb <name> --blk <blk> [--idx <index>] --val <value> inject summary entry\n");
>   	MSG(0, "  --node --mb <name> --nid <nid> [--idx <index>] --val <value> inject node\n");
> +	MSG(0, "  --dent --mb <name> --nid <ino> [--idx <index>] --val <value> inject ino's dentry\n");
>   	MSG(0, "  --dry-run do not really inject\n");
>   
>   	exit(1);
> @@ -186,6 +199,16 @@ static void inject_node_usage(void)
>   	MSG(0, "  addr: inject {in}direct node nid/addr array selected by --idx <index>\n");
>   }
>   
> +static void inject_dent_usage(void)
> +{
> +	MSG(0, "inject.f2fs --dent --mb <name> --nid <nid> [--idx <index>] --val <value> inject dentry\n");
> +	MSG(0, "[mb]:\n");
> +	MSG(0, "  d_bitmap: inject dentry block d_bitmap of nid\n");
> +	MSG(0, "  d_hash: inject dentry hash\n");
> +	MSG(0, "  d_ino: inject dentry ino\n");
> +	MSG(0, "  d_ftype: inject dentry ftype\n");
> +}
> +
>   int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
>   {
>   	int o = 0;
> @@ -205,6 +228,7 @@ int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
>   		{"blk", required_argument, 0, 11},
>   		{"ssa", no_argument, 0, 12},
>   		{"node", no_argument, 0, 13},
> +		{"dent", no_argument, 0, 14},
>   		{0, 0, 0, 0}
>   	};
>   
> @@ -280,6 +304,10 @@ int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
>   			opt->node = true;
>   			MSG(0, "Info: inject node\n");
>   			break;
> +		case 14:
> +			opt->dent = true;
> +			MSG(0, "Info: inject dentry\n");
> +			break;
>   		case 'd':
>   			if (optarg[0] == '-' || !is_digits(optarg))
>   				return EWRONG_OPT;
> @@ -309,6 +337,9 @@ int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
>   			} else if (opt->node) {
>   				inject_node_usage();
>   				exit(0);
> +			} else if (opt->dent) {
> +				inject_dent_usage();
> +				exit(0);
>   			}
>   			return EUNKNOWN_OPT;
>   		}
> @@ -884,6 +915,152 @@ out:
>   	return ret;
>   }
>   
> +static int find_dir_entry(struct f2fs_dentry_ptr *d, nid_t ino)
> +{
> +	struct f2fs_dir_entry *de;
> +	int slot = 0;
> +
> +	while (slot < d->max) {
> +		if (!test_bit_le(slot, d->bitmap)) {
> +			slot++;
> +			continue;
> +		}
> +
> +		de = &d->dentry[slot];
> +		if (le32_to_cpu(de->ino) == ino && de->hash_code != 0)
> +			return slot;
> +		if (de->name_len == 0) {
> +			slot++;
> +			continue;
> +		}
> +		slot += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
> +	}
> +
> +	return -ENOENT;
> +}
> +
> +static int inject_dentry(struct f2fs_sb_info *sbi, struct inject_option *opt)
> +{
> +	struct node_info ni;
> +	struct f2fs_node *node_blk = NULL;
> +	struct f2fs_inode *inode;
> +	struct f2fs_dentry_ptr d;
> +	void *inline_dentry;
> +	struct f2fs_dentry_block *dent_blk = NULL;
> +	block_t addr = 0;
> +	void *buf = NULL;
> +	struct f2fs_dir_entry *dent = NULL;
> +	struct dnode_of_data dn;
> +	nid_t pino;
> +	int slot = -ENOENT, ret;
> +
> +	node_blk = malloc(F2FS_BLKSIZE);
> +	ASSERT(node_blk != NULL);
> +
> +	/* get child inode */
> +	get_node_info(sbi, opt->nid, &ni);
> +	ret = dev_read_block(node_blk, ni.blk_addr);
> +	ASSERT(ret >= 0);
> +	pino = le32_to_cpu(node_blk->i.i_pino);
> +
> +	/* get parent inode */
> +	get_node_info(sbi, pino, &ni);
> +	ret = dev_read_block(node_blk, ni.blk_addr);
> +	ASSERT(ret >= 0);
> +	inode = &node_blk->i;
> +
> +	/* find child dentry */
> +	if (inode->i_inline & F2FS_INLINE_DENTRY) {
> +		inline_dentry = inline_data_addr(node_blk);
> +		make_dentry_ptr(&d, node_blk, inline_dentry, 2);
> +		addr = ni.blk_addr;
> +		buf = node_blk;
> +
> +		slot = find_dir_entry(&d, opt->nid);
> +		if (slot >= 0)
> +			dent = &d.dentry[slot];
> +	} else {
> +		unsigned int level, dirlevel, nbucket;
> +		unsigned long i, end;
> +
> +		level = le32_to_cpu(inode->i_current_depth);
> +		dirlevel = le32_to_cpu(inode->i_dir_level);
> +		nbucket = dir_buckets(level, dirlevel);
> +		end = dir_block_index(level, dirlevel, nbucket) +
> +						bucket_blocks(level);
> +
> +		dent_blk = malloc(F2FS_BLKSIZE);
> +		ASSERT(dent_blk != NULL);
> +
> +		for (i = 0; i < end; i++) {
> +			memset(&dn, 0, sizeof(dn));
> +			set_new_dnode(&dn, node_blk, NULL, pino);
> +			ret = get_dnode_of_data(sbi, &dn, i, LOOKUP_NODE);
> +			if (ret < 0)
> +				break;
> +			addr = dn.data_blkaddr;
> +			if (dn.inode_blk != dn.node_blk)
> +				free(dn.node_blk);
> +			if (addr == NULL_ADDR || addr == NEW_ADDR)

check addr w/ f2fs_is_valid_blkaddr() ? otherwise, we may read dentry block
from invalid blkaddr.

Thanks,

> +				continue;
> +			ret = dev_read_block(dent_blk, addr);
> +			ASSERT(ret >= 0);
> +
> +			make_dentry_ptr(&d, node_blk, dent_blk, 1);
> +			slot = find_dir_entry(&d, opt->nid);
> +			if (slot >= 0) {
> +				dent = &d.dentry[slot];
> +				buf = dent_blk;
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (slot < 0) {
> +		ERR_MSG("dentry of ino %u not found\n", opt->nid);
> +		ret = -ENOENT;
> +		goto out;
> +	}
> +
> +	if (!strcmp(opt->mb, "d_bitmap")) {
> +		MSG(0, "Info: inject dentry bitmap of nid %u: 1 -> 0\n",
> +		    opt->nid);
> +		test_and_clear_bit_le(slot, d.bitmap);
> +	} else if (!strcmp(opt->mb, "d_hash")) {
> +		MSG(0, "Info: inject dentry d_hash of nid %u: "
> +		    "0x%x -> 0x%x\n", opt->nid, le32_to_cpu(dent->hash_code),
> +		    (u32)opt->val);
> +		dent->hash_code = cpu_to_le32((u32)opt->val);
> +	} else if (!strcmp(opt->mb, "d_ino")) {
> +		MSG(0, "Info: inject dentry d_ino of nid %u: "
> +		    "%u -> %u\n", opt->nid, le32_to_cpu(dent->ino),
> +		    (nid_t)opt->val);
> +		dent->ino = cpu_to_le32((nid_t)opt->val);
> +	} else if (!strcmp(opt->mb, "d_ftype")) {
> +		MSG(0, "Info: inject dentry d_type of nid %u: "
> +		    "%d -> %d\n", opt->nid, dent->file_type,
> +		    (u8)opt->val);
> +		dent->file_type = (u8)opt->val;
> +	} else {
> +		ERR_MSG("unknown or unsupported member \"%s\"\n", opt->mb);
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	print_raw_dentry_info(dent);
> +
> +	if (inode->i_inline & F2FS_INLINE_DENTRY)
> +		ret = update_inode(sbi, buf, &addr);
> +	else
> +		ret = update_block(sbi, buf, &addr, NULL);
> +	ASSERT(ret >= 0);
> +
> +out:
> +	free(node_blk);
> +	free(dent_blk);
> +	return ret;
> +}
> +
>   int do_inject(struct f2fs_sb_info *sbi)
>   {
>   	struct inject_option *opt = (struct inject_option *)c.private;
> @@ -901,6 +1078,8 @@ int do_inject(struct f2fs_sb_info *sbi)
>   		ret = inject_ssa(sbi, opt);
>   	else if (opt->node)
>   		ret = inject_node(sbi, opt);
> +	else if (opt->dent)
> +		ret = inject_dentry(sbi, opt);
>   
>   	return ret;
>   }
> diff --git a/fsck/inject.h b/fsck/inject.h
> index 9b14c31..43c21b5 100644
> --- a/fsck/inject.h
> +++ b/fsck/inject.h
> @@ -32,6 +32,7 @@ struct inject_option {
>   	int sit;		/* which sit pack */
>   	bool ssa;
>   	bool node;
> +	bool dent;
>   };
>   
>   void inject_usage(void);
diff mbox series

Patch

diff --git a/fsck/inject.c b/fsck/inject.c
index c48728e..bae9afb 100644
--- a/fsck/inject.c
+++ b/fsck/inject.c
@@ -10,6 +10,7 @@ 
  */
 
 #include <getopt.h>
+#include "node.h"
 #include "inject.h"
 
 static void print_raw_nat_entry_info(struct f2fs_nat_entry *ne)
@@ -74,6 +75,17 @@  static void print_node_footer_info(struct node_footer *footer)
 	DISP_u32(footer, next_blkaddr);
 }
 
+static void print_raw_dentry_info(struct f2fs_dir_entry *dentry)
+{
+	if (!c.dbg_lv)
+		return;
+
+	DISP_u32(dentry, hash_code);
+	DISP_u32(dentry, ino);
+	DISP_u16(dentry, name_len);
+	DISP_u8(dentry, file_type);
+}
+
 void inject_usage(void)
 {
 	MSG(0, "\nUsage: inject.f2fs [options] device\n");
@@ -92,6 +104,7 @@  void inject_usage(void)
 	MSG(0, "  --sit <0|1|2> --mb <name> --blk <blk> [--idx <index>] --val <value> inject sit entry\n");
 	MSG(0, "  --ssa --mb <name> --blk <blk> [--idx <index>] --val <value> inject summary entry\n");
 	MSG(0, "  --node --mb <name> --nid <nid> [--idx <index>] --val <value> inject node\n");
+	MSG(0, "  --dent --mb <name> --nid <ino> [--idx <index>] --val <value> inject ino's dentry\n");
 	MSG(0, "  --dry-run do not really inject\n");
 
 	exit(1);
@@ -186,6 +199,16 @@  static void inject_node_usage(void)
 	MSG(0, "  addr: inject {in}direct node nid/addr array selected by --idx <index>\n");
 }
 
+static void inject_dent_usage(void)
+{
+	MSG(0, "inject.f2fs --dent --mb <name> --nid <nid> [--idx <index>] --val <value> inject dentry\n");
+	MSG(0, "[mb]:\n");
+	MSG(0, "  d_bitmap: inject dentry block d_bitmap of nid\n");
+	MSG(0, "  d_hash: inject dentry hash\n");
+	MSG(0, "  d_ino: inject dentry ino\n");
+	MSG(0, "  d_ftype: inject dentry ftype\n");
+}
+
 int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
 {
 	int o = 0;
@@ -205,6 +228,7 @@  int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
 		{"blk", required_argument, 0, 11},
 		{"ssa", no_argument, 0, 12},
 		{"node", no_argument, 0, 13},
+		{"dent", no_argument, 0, 14},
 		{0, 0, 0, 0}
 	};
 
@@ -280,6 +304,10 @@  int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
 			opt->node = true;
 			MSG(0, "Info: inject node\n");
 			break;
+		case 14:
+			opt->dent = true;
+			MSG(0, "Info: inject dentry\n");
+			break;
 		case 'd':
 			if (optarg[0] == '-' || !is_digits(optarg))
 				return EWRONG_OPT;
@@ -309,6 +337,9 @@  int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
 			} else if (opt->node) {
 				inject_node_usage();
 				exit(0);
+			} else if (opt->dent) {
+				inject_dent_usage();
+				exit(0);
 			}
 			return EUNKNOWN_OPT;
 		}
@@ -884,6 +915,152 @@  out:
 	return ret;
 }
 
+static int find_dir_entry(struct f2fs_dentry_ptr *d, nid_t ino)
+{
+	struct f2fs_dir_entry *de;
+	int slot = 0;
+
+	while (slot < d->max) {
+		if (!test_bit_le(slot, d->bitmap)) {
+			slot++;
+			continue;
+		}
+
+		de = &d->dentry[slot];
+		if (le32_to_cpu(de->ino) == ino && de->hash_code != 0)
+			return slot;
+		if (de->name_len == 0) {
+			slot++;
+			continue;
+		}
+		slot += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
+	}
+
+	return -ENOENT;
+}
+
+static int inject_dentry(struct f2fs_sb_info *sbi, struct inject_option *opt)
+{
+	struct node_info ni;
+	struct f2fs_node *node_blk = NULL;
+	struct f2fs_inode *inode;
+	struct f2fs_dentry_ptr d;
+	void *inline_dentry;
+	struct f2fs_dentry_block *dent_blk = NULL;
+	block_t addr = 0;
+	void *buf = NULL;
+	struct f2fs_dir_entry *dent = NULL;
+	struct dnode_of_data dn;
+	nid_t pino;
+	int slot = -ENOENT, ret;
+
+	node_blk = malloc(F2FS_BLKSIZE);
+	ASSERT(node_blk != NULL);
+
+	/* get child inode */
+	get_node_info(sbi, opt->nid, &ni);
+	ret = dev_read_block(node_blk, ni.blk_addr);
+	ASSERT(ret >= 0);
+	pino = le32_to_cpu(node_blk->i.i_pino);
+
+	/* get parent inode */
+	get_node_info(sbi, pino, &ni);
+	ret = dev_read_block(node_blk, ni.blk_addr);
+	ASSERT(ret >= 0);
+	inode = &node_blk->i;
+
+	/* find child dentry */
+	if (inode->i_inline & F2FS_INLINE_DENTRY) {
+		inline_dentry = inline_data_addr(node_blk);
+		make_dentry_ptr(&d, node_blk, inline_dentry, 2);
+		addr = ni.blk_addr;
+		buf = node_blk;
+
+		slot = find_dir_entry(&d, opt->nid);
+		if (slot >= 0)
+			dent = &d.dentry[slot];
+	} else {
+		unsigned int level, dirlevel, nbucket;
+		unsigned long i, end;
+
+		level = le32_to_cpu(inode->i_current_depth);
+		dirlevel = le32_to_cpu(inode->i_dir_level);
+		nbucket = dir_buckets(level, dirlevel);
+		end = dir_block_index(level, dirlevel, nbucket) +
+						bucket_blocks(level);
+
+		dent_blk = malloc(F2FS_BLKSIZE);
+		ASSERT(dent_blk != NULL);
+
+		for (i = 0; i < end; i++) {
+			memset(&dn, 0, sizeof(dn));
+			set_new_dnode(&dn, node_blk, NULL, pino);
+			ret = get_dnode_of_data(sbi, &dn, i, LOOKUP_NODE);
+			if (ret < 0)
+				break;
+			addr = dn.data_blkaddr;
+			if (dn.inode_blk != dn.node_blk)
+				free(dn.node_blk);
+			if (addr == NULL_ADDR || addr == NEW_ADDR)
+				continue;
+			ret = dev_read_block(dent_blk, addr);
+			ASSERT(ret >= 0);
+
+			make_dentry_ptr(&d, node_blk, dent_blk, 1);
+			slot = find_dir_entry(&d, opt->nid);
+			if (slot >= 0) {
+				dent = &d.dentry[slot];
+				buf = dent_blk;
+				break;
+			}
+		}
+	}
+
+	if (slot < 0) {
+		ERR_MSG("dentry of ino %u not found\n", opt->nid);
+		ret = -ENOENT;
+		goto out;
+	}
+
+	if (!strcmp(opt->mb, "d_bitmap")) {
+		MSG(0, "Info: inject dentry bitmap of nid %u: 1 -> 0\n",
+		    opt->nid);
+		test_and_clear_bit_le(slot, d.bitmap);
+	} else if (!strcmp(opt->mb, "d_hash")) {
+		MSG(0, "Info: inject dentry d_hash of nid %u: "
+		    "0x%x -> 0x%x\n", opt->nid, le32_to_cpu(dent->hash_code),
+		    (u32)opt->val);
+		dent->hash_code = cpu_to_le32((u32)opt->val);
+	} else if (!strcmp(opt->mb, "d_ino")) {
+		MSG(0, "Info: inject dentry d_ino of nid %u: "
+		    "%u -> %u\n", opt->nid, le32_to_cpu(dent->ino),
+		    (nid_t)opt->val);
+		dent->ino = cpu_to_le32((nid_t)opt->val);
+	} else if (!strcmp(opt->mb, "d_ftype")) {
+		MSG(0, "Info: inject dentry d_type of nid %u: "
+		    "%d -> %d\n", opt->nid, dent->file_type,
+		    (u8)opt->val);
+		dent->file_type = (u8)opt->val;
+	} else {
+		ERR_MSG("unknown or unsupported member \"%s\"\n", opt->mb);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	print_raw_dentry_info(dent);
+
+	if (inode->i_inline & F2FS_INLINE_DENTRY)
+		ret = update_inode(sbi, buf, &addr);
+	else
+		ret = update_block(sbi, buf, &addr, NULL);
+	ASSERT(ret >= 0);
+
+out:
+	free(node_blk);
+	free(dent_blk);
+	return ret;
+}
+
 int do_inject(struct f2fs_sb_info *sbi)
 {
 	struct inject_option *opt = (struct inject_option *)c.private;
@@ -901,6 +1078,8 @@  int do_inject(struct f2fs_sb_info *sbi)
 		ret = inject_ssa(sbi, opt);
 	else if (opt->node)
 		ret = inject_node(sbi, opt);
+	else if (opt->dent)
+		ret = inject_dentry(sbi, opt);
 
 	return ret;
 }
diff --git a/fsck/inject.h b/fsck/inject.h
index 9b14c31..43c21b5 100644
--- a/fsck/inject.h
+++ b/fsck/inject.h
@@ -32,6 +32,7 @@  struct inject_option {
 	int sit;		/* which sit pack */
 	bool ssa;
 	bool node;
+	bool dent;
 };
 
 void inject_usage(void);