On 2025/6/10 20:37, Sheng Yong wrote:
From: Sheng Yong <shengyo...@xiaomi.com>

This patch adds a new member `filename' in inject_dentry to inject
dentry filename. The dentry is specified by nid option.

Note that '.' and '..' dentries are special, because they are not in the
parent directory of nid. So this patch also adds a new option `--dots'
to inject these two dentries.

Signed-off-by: Sheng Yong <shengyo...@xiaomi.com>
---
  fsck/inject.c     | 94 ++++++++++++++++++++++++++++++++++++++++-------
  fsck/inject.h     |  1 +
  man/inject.f2fs.8 | 12 +++++-
  3 files changed, 92 insertions(+), 15 deletions(-)

diff --git a/fsck/inject.c b/fsck/inject.c
index 8c2f8c5dc332..5eb913fefad7 100644
--- a/fsck/inject.c
+++ b/fsck/inject.c
@@ -105,7 +105,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, "  --dent --mb <name> --nid <ino> [--dots <1|2>] --val/str 
<value/string> inject ino's dentry\n");
        MSG(0, "  --dry-run do not really inject\n");
exit(1);
@@ -212,12 +212,16 @@ static void inject_node_usage(void)
static void inject_dent_usage(void)
  {
-       MSG(0, "inject.f2fs --dent --mb <name> --nid <nid> [--idx <index>] --val 
<value> inject dentry\n");
+       MSG(0, "inject.f2fs --dent --mb <name> --nid <nid> [--dots <1|2>] --val/str 
<value/string> inject dentry\n");
+       MSG(0, "[dots]:\n");
+       MSG(0, "  1: inject \".\" in directory which is specified by nid\n");
+       MSG(0, "  2: inject \"..\" in directory which is specified by nid\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");
+       MSG(0, "  filename: inject dentry filename, its hash and len are updated 
implicitly\n");
  }
int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
@@ -241,6 +245,7 @@ int inject_parse_options(int argc, char *argv[], struct 
inject_option *opt)
                {"ssa", no_argument, 0, 12},
                {"node", no_argument, 0, 13},
                {"dent", no_argument, 0, 14},
+               {"dots", required_argument, 0, 15},
                {0, 0, 0, 0}
        };
@@ -339,6 +344,14 @@ int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
                        opt->dent = true;
                        MSG(0, "Info: inject dentry\n");
                        break;
+               case 15:
+                       opt->dots = atoi(optarg);
+                       if (opt->dots != TYPE_DOT &&
+                           opt->dots != TYPE_DOTDOT)
+                               return -ERANGE;
+                       MSG(0, "Info: inject %s dentry\n",
+                           opt->dots == TYPE_DOT ? "dot" : "dotdot");
+                       break;
                case 'd':
                        if (optarg[0] == '-' || !is_digits(optarg))
                                return EWRONG_OPT;
@@ -371,6 +384,9 @@ int inject_parse_options(int argc, char *argv[], struct 
inject_option *opt)
                        } else if (opt->dent) {
                                inject_dent_usage();
                                exit(0);
+                       } else {
+                               MSG(0, "\tError: Wrong option -%c (%d) %s\n",
+                                   o, o, optarg);
                        }
                        return EUNKNOWN_OPT;
                }
@@ -1146,12 +1162,12 @@ static int find_dir_entry(struct f2fs_dentry_ptr *d, 
nid_t ino)
                }
de = &d->dentry[slot];
-               if (le32_to_cpu(de->ino) == ino && de->hash_code != 0)
-                       return slot;
                if (de->name_len == 0) {
                        slot++;
                        continue;
                }
+               if (le32_to_cpu(de->ino) == ino)
+                       return slot;
                slot += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
        }
@@ -1164,14 +1180,15 @@ static int inject_dentry(struct f2fs_sb_info *sbi, struct inject_option *opt)
        struct f2fs_node *node_blk = NULL;
        struct f2fs_inode *inode;
        struct f2fs_dentry_ptr d;
-       void *inline_dentry;
+       void *buf = NULL, *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;
+       int slot = -ENOENT, namelen, namecap, ret;
+       unsigned int dentry_hash;
+       char *name;
node_blk = malloc(F2FS_BLKSIZE);
        ASSERT(node_blk != NULL);
@@ -1180,12 +1197,25 @@ static int inject_dentry(struct f2fs_sb_info *sbi, 
struct inject_option *opt)
        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);
+       if (opt->dots) {
+               if (!LINUX_S_ISDIR(le16_to_cpu(node_blk->i.i_mode))) {
+                       ERR_MSG("ino %u is not a directory, cannot inject "
+                               "its %s\n", opt->nid,
+                               opt->dots == TYPE_DOT ? "." : "..");
+                       ret = -EINVAL;
+                       goto out;
+               }
+               /* pino is itself */
+               pino = opt->nid;
+       } else {
+               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 */
@@ -1195,7 +1225,10 @@ static int inject_dentry(struct f2fs_sb_info *sbi, 
struct inject_option *opt)
                addr = ni.blk_addr;
                buf = node_blk;
- slot = find_dir_entry(&d, opt->nid);
+               if (opt->dots == TYPE_DOTDOT)
+                       slot = find_dir_entry(&d, 
le32_to_cpu(node_blk->i.i_pino));
+               else
+                       slot = find_dir_entry(&d, opt->nid);
                if (slot >= 0)
                        dent = &d.dentry[slot];
        } else {
@@ -1231,7 +1264,10 @@ static int inject_dentry(struct f2fs_sb_info *sbi, 
struct inject_option *opt)
                        ASSERT(ret >= 0);
make_dentry_ptr(&d, node_blk, dent_blk, 1);
-                       slot = find_dir_entry(&d, opt->nid);
+                       if (opt->dots == TYPE_DOTDOT)
+                               slot = find_dir_entry(&d, 
le32_to_cpu(node_blk->i.i_pino));
+                       else
+                               slot = find_dir_entry(&d, opt->nid);
                        if (slot >= 0) {
                                dent = &d.dentry[slot];
                                buf = dent_blk;
@@ -1265,6 +1301,36 @@ static int inject_dentry(struct f2fs_sb_info *sbi, 
struct inject_option *opt)
                    "%d -> %d\n", opt->nid, dent->file_type,
                    (u8)opt->val);
                dent->file_type = (u8)opt->val;
+       } else if (!strcmp(opt->mb, "filename")) {
+               if (!opt->str) {
+                       ERR_MSG("option str is needed\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+               namecap = ALIGN_UP(le16_to_cpu(dent->name_len), F2FS_SLOT_LEN);
+               namelen = strlen(opt->str);
+               if (namelen > namecap || namelen > F2FS_NAME_LEN) {
+                       ERR_MSG("option str too long\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+               name = (char *)d.filename[slot];
+               MSG(0, "Info: inject dentry filename of nid %u: "
+                   "%.*s -> %s\n", opt->nid, le16_to_cpu(dent->name_len),
+                   name, opt->str);
+               memcpy(name, opt->str, namelen);
+               MSG(0, "Info: inject dentry namelen of nid %u: "
+                   "%d -> %d\n", opt->nid, le16_to_cpu(dent->name_len),
+                   namelen);
+               dent->name_len = cpu_to_le16(namelen);
+               dentry_hash = f2fs_dentry_hash(get_encoding(sbi),
+                                               IS_CASEFOLDED(inode),
+                                               (unsigned char *)name,
+                                               namelen);
+               MSG(0, "Info: inject dentry d_hash of nid %u: "
+                   "0x%x -> 0x%x\n", opt->nid, le32_to_cpu(dent->hash_code),
+                   dentry_hash);
+               dent->hash_code = cpu_to_le32(dentry_hash);

Yong,

Out of curiosity, if we inject filename in target dirent, according new
filename's hash, it may be located in dentry block which belongs to another
bucket, can fsck repair it?

Thanks,

        } else {
                ERR_MSG("unknown or unsupported member \"%s\"\n", opt->mb);
                ret = -EINVAL;
diff --git a/fsck/inject.h b/fsck/inject.h
index 43c21b56a7eb..706a211bc645 100644
--- a/fsck/inject.h
+++ b/fsck/inject.h
@@ -30,6 +30,7 @@ struct inject_option {
        int cp;                 /* which cp */
        int nat;                /* which nat pack */
        int sit;                /* which sit pack */
+       int dots;               /* . or .. dentry */
        bool ssa;
        bool node;
        bool dent;
diff --git a/man/inject.f2fs.8 b/man/inject.f2fs.8
index 72d1c90f7ce4..27b66f59b77d 100644
--- a/man/inject.f2fs.8
+++ b/man/inject.f2fs.8
@@ -214,7 +214,14 @@ inode i_nid array specified by \fIidx\fP.
  .RE
  .TP
  .BI \-\-dent
-Inject dentry block or dir entry specified \fInid\fP.
+Inject dentry block or dir entry specified by \fInid\fP.
+.RS 1.2i
+.TP
+.BI \-\-dots " 1 or 2"
+The option means the "." or ".." directory entry of \fInid\fP is going to be 
injected.
+.RE
+.TP
+.BI ""
  The available \fImb\fP of \fIdent\fP are:
  .RS 1.2i
  .TP
@@ -229,6 +236,9 @@ dentry ino.
  .TP
  .BI d_ftype
  dentry ftype.
+.TP
+.BI filename
+dentry filename, and corresponding d_hash and namelen are updated implicitly.
  .RE
  .TP
  .BI \-\-dry\-run



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

Reply via email to