]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck: add fc replay for link, unlink, creat tags
authorHarshad Shirwadkar <harshadshirwadkar@gmail.com>
Fri, 22 Jan 2021 05:45:02 +0000 (21:45 -0800)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 28 Jan 2021 01:12:33 +0000 (20:12 -0500)
Add fast commit replay for directory entry updates.

Signed-off-by: Harshad Shirwadkar <harshadshirwadkar@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
e2fsck/journal.c
lib/ext2fs/ext2fs.h
lib/ext2fs/unlink.c

index 007c32c6e27d82ec4ddbcc21ff7a9b622348297a..2afe0929ca8a46565eef2c8f9fbe496698ff53d2 100644 (file)
@@ -380,6 +380,114 @@ static int ext4_fc_replay_scan(journal_t *j, struct buffer_head *bh,
 out_err:
        return ret;
 }
+
+static int __errcode_to_errno(errcode_t err, const char *func, int line)
+{
+       if (err == 0)
+               return 0;
+       fprintf(stderr, "Error \"%s\" encountered in function %s at line %d\n",
+               error_message(err), func, line);
+       if (err <= 256)
+               return -err;
+       return -EFAULT;
+}
+
+#define errcode_to_errno(err)  __errcode_to_errno(err, __func__, __LINE__)
+
+/* 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;
+       int ret;
+
+       tl_to_darg(&darg, tl);
+       ret = errcode_to_errno(
+                      ext2fs_unlink(ctx->fs, darg.parent_ino,
+                                    darg.dname, darg.ino, 0));
+       /* It's okay if the above call fails */
+       free(darg.dname);
+       return ret;
+}
+
+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 = errcode_to_errno(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 = errcode_to_errno(
+                      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 +545,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:
index 7a25e0e5bfdf6de68e4e7cdd93420a02e1b18836..b1752ac7eeec01153f6abc37e27f59b1d718ed74 100644 (file)
@@ -1693,6 +1693,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,
index 8ab27ee24da9db7b60be8d3e2d50117ab9154cd9..3ec04cfbe9566543d94d6d112c09b239a6172556 100644 (file)
@@ -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;