]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck: track ea_inode references
authorTahsin Erdogan <tahsin@google.com>
Wed, 5 Jul 2017 03:53:59 +0000 (23:53 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Wed, 5 Jul 2017 03:53:59 +0000 (23:53 -0400)
An extended attribute inode has a ref count to track how many entries
point to it. Update e2fsck to verify that the stored ref count is
correct.

Signed-off-by: Tahsin Erdogan <tahsin@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
e2fsck/e2fsck.c
e2fsck/e2fsck.h
e2fsck/message.c
e2fsck/pass1.c
e2fsck/pass4.c
e2fsck/problem.c
e2fsck/problem.h

index e0f449ee2047d37baf24c1fb67b19b2aff2ddeff..63a986b92af9d9c7b86fc580749c5b1c3a814648 100644 (file)
@@ -102,6 +102,10 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx)
                ea_refcount_free(ctx->ea_block_quota);
                ctx->ea_block_quota = 0;
        }
+       if (ctx->ea_inode_refs) {
+               ea_refcount_free(ctx->ea_inode_refs);
+               ctx->ea_inode_refs = 0;
+       }
        if (ctx->block_dup_map) {
                ext2fs_free_block_bitmap(ctx->block_dup_map);
                ctx->block_dup_map = 0;
index 79eeda9fbafbe34e568b1c4655088c496fcd7a8b..b25c5eb9179b4b3a604b9c201bbc4aa190466731 100644 (file)
@@ -254,7 +254,6 @@ struct e2fsck_struct {
        ext2fs_inode_bitmap inode_bb_map; /* Inodes which are in bad blocks */
        ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */
        ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/
-       ext2fs_inode_bitmap inode_ea_map; /* EA inodes which are non-orphan */
 
        ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
        ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
@@ -274,6 +273,11 @@ struct e2fsck_struct {
         */
        ext2_refcount_t ea_block_quota;
 
+       /*
+        * ea_inode references from attr entries.
+        */
+       ext2_refcount_t ea_inode_refs;
+
        /*
         * Array of flags indicating whether an inode bitmap, block
         * bitmap, or inode table is invalid
index 34201a37fd4bfcfc2206777aec4acf77191ce11f..525f4a1e7628e0a1ce1cd93431821c9cc9923f70 100644 (file)
@@ -466,6 +466,13 @@ static _INLINE_ void expand_percent_expression(FILE *f, ext2_filsys fs,
                fprintf(f, "%*u", width, ctx->num);
 #else
                fprintf(f, "%*llu", width, (long long)ctx->num);
+#endif
+               break;
+       case 'n':
+#ifdef EXT2_NO_64_TYPE
+               fprintf(f, "%*u", width, ctx->num2);
+#else
+               fprintf(f, "%*llu", width, (long long)ctx->num2);
 #endif
                break;
        case 'p':
index c0bfb894b619a31e6b1fa8ca072c08c512f930df..4ea7962f987dc62dee5bb615b4cc7b8f464aeca4 100644 (file)
@@ -26,7 +26,7 @@
  *     - A bitmap of which blocks are in use.          (block_found_map)
  *     - A bitmap of which blocks are in use by two inodes     (block_dup_map)
  *     - The data blocks of the directory inodes.      (dir_map)
- *     - A bitmap of EA inodes.                        (inode_ea_map)
+ *     - Ref counts for ea_inodes.                     (ea_inode_refs)
  *
  * Pass 1 is designed to stash away enough information so that the
  * other passes should not need to read in the inode information
@@ -335,23 +335,6 @@ static void check_size(e2fsck_t ctx, struct problem_context *pctx)
        e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
 }
 
-static void mark_inode_ea_map(e2fsck_t ctx, struct problem_context *pctx,
-                             ext2_ino_t ino)
-{
-       if (!ctx->inode_ea_map) {
-               pctx->errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
-                                        _("EA inode map"),
-                                        &ctx->inode_ea_map);
-               if (pctx->errcode) {
-                       fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR,
-                                   pctx);
-                       exit(1);
-               }
-       }
-
-       ext2fs_mark_inode_bitmap2(ctx->inode_ea_map, ino);
-}
-
 /*
  * For a given size, calculate how many blocks would be charged towards quota.
  */
@@ -428,13 +411,38 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
        return 0;
 }
 
+static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
+                             struct ext2_ext_attr_entry *first, void *end)
+{
+       struct ext2_ext_attr_entry *entry;
+
+       for (entry = first;
+            (void *)entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry);
+            entry = EXT2_EXT_ATTR_NEXT(entry)) {
+               if (!entry->e_value_inum)
+                       continue;
+               if (!ctx->ea_inode_refs) {
+                       pctx->errcode = ea_refcount_create(0,
+                                                          &ctx->ea_inode_refs);
+                       if (pctx->errcode) {
+                               pctx->num = 4;
+                               fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+                               ctx->flags |= E2F_FLAG_ABORT;
+                               return;
+                       }
+               }
+               ea_refcount_increment(ctx->ea_inode_refs, entry->e_value_inum,
+                                     0);
+       }
+}
+
 static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
                              blk64_t *ea_ibody_quota_blocks)
 {
        struct ext2_super_block *sb = ctx->fs->super;
        struct ext2_inode_large *inode;
        struct ext2_ext_attr_entry *entry;
-       char *start, *header;
+       char *start, *header, *end;
        unsigned int storage_size, remain;
        problem_t problem = 0;
        region_t region = 0;
@@ -447,6 +455,7 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
                inode->i_extra_isize;
        header = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
                 inode->i_extra_isize;
+       end = header + storage_size;
        start = header + sizeof(__u32);
        entry = (struct ext2_ext_attr_entry *) start;
 
@@ -524,7 +533,6 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
                        if (problem != 0)
                                goto fix;
 
-                       mark_inode_ea_map(ctx, pctx, entry->e_value_inum);
                        quota_blocks += entry_quota_blocks;
                }
 
@@ -549,6 +557,8 @@ fix:
         * EA(s) in automatic fashion -bzzz
         */
        if (problem == 0 || !fix_problem(ctx, problem, pctx)) {
+               inc_ea_inode_refs(ctx, pctx,
+                                 (struct ext2_ext_attr_entry *)start, end);
                *ea_ibody_quota_blocks = quota_blocks;
                return;
        }
@@ -1988,6 +1998,11 @@ void e2fsck_pass1(e2fsck_t ctx)
                ctx->refcount_extra = 0;
        }
 
+       if (ctx->ea_block_quota) {
+               ea_refcount_free(ctx->ea_block_quota);
+               ctx->ea_block_quota = 0;
+       }
+
        if (ctx->invalid_bitmaps)
                handle_fs_bad_blocks(ctx);
 
@@ -2349,7 +2364,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
        blk64_t         blk;
        char *          end;
        struct ext2_ext_attr_header *header;
-       struct ext2_ext_attr_entry *entry;
+       struct ext2_ext_attr_entry *first, *entry;
        blk64_t         quota_blocks = EXT2FS_C2B(fs, 1);
        region_t        region = 0;
        int             failed_csum = 0;
@@ -2473,8 +2488,9 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
                        goto clear_extattr;
        }
 
-       entry = (struct ext2_ext_attr_entry *)(header+1);
+       first = (struct ext2_ext_attr_entry *)(header+1);
        end = block_buf + fs->blocksize;
+       entry = first;
        while ((char *)entry < end && *(__u32 *)entry) {
                __u32 hash;
 
@@ -2522,10 +2538,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 
                        problem = check_large_ea_inode(ctx, entry, pctx,
                                                       &entry_quota_blocks);
-                       if (problem == 0)
-                               mark_inode_ea_map(ctx, pctx,
-                                                 entry->e_value_inum);
-                       else if (fix_problem(ctx, problem, pctx))
+                       if (problem && fix_problem(ctx, problem, pctx))
                                goto clear_extattr;
 
                        quota_blocks += entry_quota_blocks;
@@ -2565,6 +2578,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
                }
                ea_refcount_store(ctx->ea_block_quota, blk, quota_blocks);
        }
+       inc_ea_inode_refs(ctx, pctx, first, end);
        ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1);
        mark_block_used(ctx, blk);
        ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk);
index e44fc69c16138102c7107da12fb26dd67a55c866..663f87ab59c0a1e4cdede1249d64d6868158a171 100644 (file)
@@ -11,7 +11,7 @@
  * Pass 4 frees the following data structures:
  *     - A bitmap of which inodes are in bad blocks.   (inode_bb_map)
  *     - A bitmap of which inodes are imagic inodes.   (inode_imagic_map)
- *     - A bitmap of EA inodes.                        (inode_ea_map)
+ *     - Ref counts for ea_inodes.                     (ea_inode_refs)
  */
 
 #include "config.h"
@@ -40,20 +40,6 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i,
        if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
                extra_size = inode->i_extra_isize;
 
-       if (inode->i_flags & EXT4_EA_INODE_FL) {
-               if (ext2fs_test_inode_bitmap2(ctx->inode_ea_map, i)) {
-                       ext2fs_icount_store(ctx->inode_count, i, 1);
-                       return 0;
-               } else {
-                       /* Zero the link count so that when inode is linked to
-                        * lost+found it has correct link count */
-                       inode->i_links_count = 0;
-                       e2fsck_write_inode(ctx, i, EXT2_INODE(inode),
-                                          "disconnect_inode");
-                       ext2fs_icount_store(ctx->inode_link_info, i, 0);
-               }
-       }
-
        clear_problem_context(&pctx);
        pctx.ino = i;
        pctx.inode = EXT2_INODE(inode);
@@ -101,6 +87,77 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i,
        return 0;
 }
 
+/*
+ * Get/set ref functions below could later be moved to somewhere in lib/ext2fs/.
+ * Currently the only user is e2fsck so we rather not expose it in common
+ * library until there are more users.
+ */
+static __u64 ea_inode_get_ref(struct ext2_inode_large *inode)
+{
+       return ((__u64)inode->i_ctime << 32) | inode->osd1.linux1.l_i_version;
+}
+
+static void ea_inode_set_ref(struct ext2_inode_large *inode, __u64 ref_count)
+{
+       inode->i_ctime = (__u32)(ref_count >> 32);
+       inode->osd1.linux1.l_i_version = (__u32)ref_count;
+}
+
+static void check_ea_inode(e2fsck_t ctx, ext2_ino_t i,
+                          struct ext2_inode_large *inode, __u16 *link_counted)
+{
+       __u64 actual_refs = 0;
+       __u64 ref_count;
+
+       /*
+        * This function is called when link_counted is zero. So this may not
+        * be an xattr inode at all. Return immediately if EA_INODE flag is not
+        * set.
+        */
+       e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode),
+                              EXT2_INODE_SIZE(ctx->fs->super),
+                              "pass4: check_ea_inode");
+       if (!(inode->i_flags & EXT4_EA_INODE_FL))
+               return;
+
+       if (ctx->ea_inode_refs)
+               ea_refcount_fetch(ctx->ea_inode_refs, i, &actual_refs);
+       if (!actual_refs) {
+               /*
+                * There are no attribute references to the ea_inode.
+                * Zero the link count so that when  inode is linked to
+                * lost+found it has correct link count.
+                */
+               inode->i_links_count = 0;
+               e2fsck_write_inode(ctx, i, EXT2_INODE(inode), "check_ea_inode");
+               ext2fs_icount_store(ctx->inode_link_info, i, 0);
+               return;
+       }
+
+       /*
+        * There are some attribute references, link_counted is now considered
+        * to be 1.
+        */
+       *link_counted = 1;
+
+       ref_count = ea_inode_get_ref(inode);
+
+       /* Old Lustre-style xattr inodes do not have a stored refcount.
+        * However, their i_ctime and i_atime should be the same.
+        */
+       if (ref_count != actual_refs && inode->i_ctime != inode->i_atime) {
+               struct problem_context pctx;
+
+               clear_problem_context(&pctx);
+               pctx.ino = i;
+               pctx.num = ref_count;
+               pctx.num2 = actual_refs;
+               if (fix_problem(ctx, PR_4_EA_INODE_REF_COUNT, &pctx)) {
+                       ea_inode_set_ref(inode, actual_refs);
+                       e2fsck_write_inode(ctx, i, EXT2_INODE(inode), "pass4");
+               }
+       }
+}
 
 void e2fsck_pass4(e2fsck_t ctx)
 {
@@ -168,6 +225,16 @@ void e2fsck_pass4(e2fsck_t ctx)
                        continue;
                ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
                ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
+
+               if (link_counted == 0) {
+                       /*
+                        * link_counted is expected to be 0 for an ea_inode.
+                        * check_ea_inode() will update link_counted if
+                        * necessary.
+                        */
+                       check_ea_inode(ctx, i, inode, &link_counted);
+               }
+
                if (link_counted == 0) {
                        if (!buf)
                                buf = e2fsck_allocate_memory(ctx,
@@ -211,10 +278,10 @@ void e2fsck_pass4(e2fsck_t ctx)
        }
        ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
        ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
-       ext2fs_free_inode_bitmap(ctx->inode_ea_map);
-       ctx->inode_ea_map = 0;
        ext2fs_free_inode_bitmap(ctx->inode_bb_map);
        ctx->inode_bb_map = 0;
+       ea_refcount_free(ctx->ea_inode_refs);
+       ctx->ea_inode_refs = 0;
        ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
        ctx->inode_imagic_map = 0;
 errout:
index deffa4d66a892552ea69aa7873965f552ca3a3e0..97069335156c38d5de8065463bbb154e5a66054e 100644 (file)
@@ -1869,6 +1869,10 @@ static struct e2fsck_problem problem_table[] = {
          "They @s the same!\n"),
          PROMPT_NONE, 0 },
 
+       { PR_4_EA_INODE_REF_COUNT,
+         N_("@a @i %i ref count is %N, @s %n. "),
+         PROMPT_FIX, PR_PREEN_OK },
+
        /* Pass 5 errors */
 
        /* Pass 5: Checking group summary information */
index 60cc764fd2dfec9e6ff3aa8e7fa8d4521840d646..f30f8f08a3711a511316aff68927d7d4570c8769 100644 (file)
@@ -20,7 +20,7 @@ struct problem_context {
        e2_blkcnt_t     blkcount;
        dgrp_t          group;
        __u32           csum1, csum2;
-       __u64   num;
+       __u64           num, num2;
        const char *str;
 };
 
@@ -1131,6 +1131,9 @@ struct problem_context {
 /* Inconsistent inode count information cached */
 #define PR_4_INCONSISTENT_COUNT                0x040004
 
+/* Extended attribute inode ref count wrong */
+#define PR_4_EA_INODE_REF_COUNT                0x040005
+
 /*
  * Pass 5 errors
  */