2021-01-21 02:47:06

by harshad shirwadkar

[permalink] [raw]
Subject: [PATCH v3 11/15] e2fsck: add fc replay for link, unlink, creat tags

From: Harshad Shirwadkar <[email protected]>

Add fast commit replay for directory entry updates.

Signed-off-by: Harshad Shirwadkar <[email protected]>
---
e2fsck/journal.c | 93 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2fs.h | 4 ++
lib/ext2fs/unlink.c | 6 +--
3 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/e2fsck/journal.c b/e2fsck/journal.c
index 9d0637e3..27ff5a76 100644
--- a/e2fsck/journal.c
+++ b/e2fsck/journal.c
@@ -380,6 +380,95 @@ static int ext4_fc_replay_scan(journal_t *j, struct buffer_head *bh,
out_err:
return ret;
}
+/* Helper struct for dentry replay routines */
+struct dentry_info_args {
+ int parent_ino, dname_len, ino, inode_len;
+ char *dname;
+};
+
+static inline void tl_to_darg(struct dentry_info_args *darg,
+ struct ext4_fc_tl *tl)
+{
+ struct ext4_fc_dentry_info *fcd;
+ int tag = le16_to_cpu(tl->fc_tag);
+
+ fcd = (struct ext4_fc_dentry_info *)ext4_fc_tag_val(tl);
+
+ darg->parent_ino = le32_to_cpu(fcd->fc_parent_ino);
+ darg->ino = le32_to_cpu(fcd->fc_ino);
+ darg->dname = fcd->fc_dname;
+ darg->dname_len = ext4_fc_tag_len(tl) -
+ sizeof(struct ext4_fc_dentry_info);
+ darg->dname = malloc(darg->dname_len + 1);
+ memcpy(darg->dname, fcd->fc_dname, darg->dname_len);
+ darg->dname[darg->dname_len] = 0;
+ jbd_debug(1, "%s: %s, ino %d, parent %d\n",
+ tag == EXT4_FC_TAG_CREAT ? "create" :
+ (tag == EXT4_FC_TAG_LINK ? "link" :
+ (tag == EXT4_FC_TAG_UNLINK ? "unlink" : "error")),
+ darg->dname, darg->ino, darg->parent_ino);
+}
+
+static int ext4_fc_handle_unlink(e2fsck_t ctx, struct ext4_fc_tl *tl)
+{
+ struct ext2_inode inode;
+ struct dentry_info_args darg;
+ ext2_filsys fs = ctx->fs;
+
+ tl_to_darg(&darg, tl);
+ ext2fs_unlink(ctx->fs, darg.parent_ino, darg.dname, darg.ino, 0);
+ /* It's okay if the above call fails */
+ free(darg.dname);
+}
+
+static int ext4_fc_handle_link_and_create(e2fsck_t ctx, struct ext4_fc_tl *tl)
+{
+ struct dentry_info_args darg;
+ ext2_filsys fs = ctx->fs;
+ struct ext2_inode_large inode_large;
+ int ret, filetype, mode;
+
+ tl_to_darg(&darg, tl);
+ ret = ext2fs_read_inode(fs, darg.ino,
+ (struct ext2_inode *)&inode_large);
+ if (ret)
+ goto out;
+
+ mode = inode_large.i_mode;
+
+ if (LINUX_S_ISREG(mode))
+ filetype = EXT2_FT_REG_FILE;
+ else if (LINUX_S_ISDIR(mode))
+ filetype = EXT2_FT_DIR;
+ else if (LINUX_S_ISCHR(mode))
+ filetype = EXT2_FT_CHRDEV;
+ else if (LINUX_S_ISBLK(mode))
+ filetype = EXT2_FT_BLKDEV;
+ else if (LINUX_S_ISLNK(mode))
+ return EXT2_FT_SYMLINK;
+ else if (LINUX_S_ISFIFO(mode))
+ filetype = EXT2_FT_FIFO;
+ else if (LINUX_S_ISSOCK(mode))
+ filetype = EXT2_FT_SOCK;
+ else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Forcefully unlink if the same name is present and ignore the error
+ * if any, since this dirent might not exist
+ */
+ ext2fs_unlink(fs, darg.parent_ino, darg.dname, darg.ino,
+ EXT2FS_UNLINK_FORCE);
+
+ ret = ext2fs_link(fs, darg.parent_ino, darg.dname, darg.ino,
+ filetype);
+out:
+ free(darg.dname);
+ return ret;
+
+}
/*
* Main recovery path entry point. This function returns JBD2_FC_REPLAY_CONTINUE
* to indicate that it is expecting more fast commit blocks. It returns
@@ -437,7 +526,11 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
switch (le16_to_cpu(tl->fc_tag)) {
case EXT4_FC_TAG_CREAT:
case EXT4_FC_TAG_LINK:
+ ret = ext4_fc_handle_link_and_create(ctx, tl);
+ break;
case EXT4_FC_TAG_UNLINK:
+ ret = ext4_fc_handle_unlink(ctx, tl);
+ break;
case EXT4_FC_TAG_ADD_RANGE:
case EXT4_FC_TAG_DEL_RANGE:
case EXT4_FC_TAG_INODE:
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index eb2e6549..931cd569 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1684,6 +1684,10 @@ extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t
char **name);

/* link.c */
+#define EXT2FS_UNLINK_FORCE 0x1 /* Forcefully unlink even if
+ * the inode number doesn't
+ * match the dirent
+ */
errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
ext2_ino_t ino, int flags);
errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name,
diff --git a/lib/ext2fs/unlink.c b/lib/ext2fs/unlink.c
index 8ab27ee2..3ec04cfb 100644
--- a/lib/ext2fs/unlink.c
+++ b/lib/ext2fs/unlink.c
@@ -49,7 +49,7 @@ static int unlink_proc(struct ext2_dir_entry *dirent,
if (strncmp(ls->name, dirent->name, ext2fs_dirent_name_len(dirent)))
return 0;
}
- if (ls->inode) {
+ if (!(ls->flags & EXT2FS_UNLINK_FORCE) && ls->inode) {
if (dirent->inode != ls->inode)
return 0;
} else {
@@ -70,7 +70,7 @@ static int unlink_proc(struct ext2_dir_entry *dirent,
#endif
errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir,
const char *name, ext2_ino_t ino,
- int flags EXT2FS_ATTR((unused)))
+ int flags)
{
errcode_t retval;
struct link_struct ls;
@@ -86,7 +86,7 @@ errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir,
ls.name = name;
ls.namelen = name ? strlen(name) : 0;
ls.inode = ino;
- ls.flags = 0;
+ ls.flags = flags;
ls.done = 0;
ls.prev = 0;

--
2.30.0.284.gd98b1dd5eaa7-goog