]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
Add support to detect corrupted resize_inode's to e2fsck.
authorTheodore Ts'o <tytso@mit.edu>
Fri, 24 Dec 2004 06:42:22 +0000 (01:42 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 24 Dec 2004 06:42:22 +0000 (01:42 -0500)
e2fsck/ChangeLog
e2fsck/e2fsck.h
e2fsck/pass1.c
e2fsck/problem.c
e2fsck/problem.h
e2fsck/super.c

index d6df0eca3324e963f28ee3a464139090090c80b4..b843459ae6acbc6d0077301f5ee4f7f6279ade8d 100644 (file)
@@ -1,3 +1,18 @@
+2004-12-24  Theodore Ts'o  <tytso@mit.edu>
+
+       * pass1.c (e2fsck_pass1): At the end of the pass 1 processing, if
+               we have been signalled to do so, recreate the resize inode.
+
+       * super.c (check_resize_inode): New function which checks to make
+               sure the resize inode is valid.  It is called by
+               check_super_block().  If it is invalid, it will signal to
+               pass1.c that the resize inode needs to recreate.
+
+       * e2fsck.h (E2F_FLAG_RESIZE_INODE): New flag
+
+       * problem.c, problem.h (PR_0_RESIZE_INODE_INVALID,
+               PR_1_RESIZE_INODE_CREATE): Add new problem codes.
+
 2004-12-23  Theodore Ts'o  <tytso@mit.edu>
 
        * swapfs.c (swap_inodes): Since swap_inodes bypasses the inode
index 99ee20a59fe80d545053b46138f98133a3db638d..6dec935743047bd800262fcc32956e15b6a98c65 100644 (file)
@@ -163,6 +163,7 @@ struct resource_track {
 #define E2F_FLAG_SB_SPECIFIED  0x0100 /* The superblock was explicitly 
                                        * specified by the user */
 #define E2F_FLAG_RESTARTED     0x0200 /* E2fsck has been restarted */
+#define E2F_FLAG_RESIZE_INODE  0x0400 /* Request to recreate resize inode */
 
 /*
  * Defines for indicating the e2fsck pass number
index 96322142064ca3eec3936d78b2c2af48d8204749..1e76ef9c565f14f2b16e471516b38e44d3f3d7c2 100644 (file)
@@ -721,6 +721,24 @@ void e2fsck_pass1(e2fsck_t ctx)
                ctx->block_ea_map = 0;
        }
 
+       if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
+               ext2fs_block_bitmap save_bmap;
+               errcode_t retval;
+
+               save_bmap = fs->block_map;
+               fs->block_map = ctx->block_found_map;
+               clear_problem_context(&pctx);
+               pctx.errcode = ext2fs_create_resize_inode(fs);
+               if (pctx.errcode) {
+                       fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
+                       /* Should never get here */
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       return;
+               }
+               fs->block_map = save_bmap;
+               ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
+       }
+                      
        if (ctx->flags & E2F_FLAG_RESTART) {
                /*
                 * Only the master copy of the superblock and block
@@ -1526,14 +1544,14 @@ static int process_block(ext2_filsys fs,
        }
 
        if (p->ino == EXT2_RESIZE_INO) {
-               if (blockcnt >= 0) {
-                       /* Check that the block is in the correct place
-                        * in the appropriate backup reserved gdt area */
-               } else if (blockcnt == BLOCK_COUNT_IND) {
-                       /* Check that the block is in the correct place
-                        * in the primary reserved gdt area */
-               } else  /* The resize inode's DIND block should be
-                        * allocated as a normal block. */
+               /* 
+                * The resize inode has already be sanity checked
+                * during pass #0 (the superblock checks).  All we
+                * have to do is mark the double indirect block as
+                * being in use; all of the other blocks are handled
+                * by mark_table_blocks()).
+                */
+               if (blockcnt == BLOCK_COUNT_DIND)
                        mark_block_used(ctx, blk);
        } else
                mark_block_used(ctx, blk);
index bb81d5208fe19531f194bb3b0afe71bdda8a2366..8951082a08210bd24c6318a59872015a0dc6f4ad 100644 (file)
@@ -39,7 +39,8 @@
 #define PROMPT_SUPPRESS 16
 #define PROMPT_UNLINK  17
 #define PROMPT_CLEAR_HTREE 18
-#define PROMPT_NULL    19
+#define PROMPT_RECREATE 19
+#define PROMPT_NULL    20
 
 /*
  * These are the prompts which are used to ask the user if they want
@@ -65,7 +66,8 @@ static const char *prompt[] = {
        N_("Suppress messages"),/* 16 */
        N_("Unlink"),           /* 17 */
        N_("Clear HTree index"),/* 18 */
-       "",                     /* 19 */
+       N_("Recreate"),         /* 19 */
+       "",                     /* 29 */
 };
 
 /*
@@ -92,7 +94,8 @@ static const char *preen_msg[] = {
        N_("SUPPRESSED"),       /* 16 */
        N_("UNLINKED"),         /* 17 */
        N_("HTREE INDEX CLEARED"),/* 18 */
-       "",                     /* 19 */
+       N_("WILL RECREATE"),    /* 19 */
+       "",                     /* 20 */
 };
 
 static const struct e2fsck_problem problem_table[] = {
@@ -325,6 +328,11 @@ static const struct e2fsck_problem problem_table[] = {
          N_("Resize_@i not enabled, but the resize inode is non-zero.  "),
          PROMPT_CLEAR, 0 },
 
+       /* Resize inode invalid */
+       { PR_0_RESIZE_INODE_INVALID,
+         N_("Resize @i not valid.  "),
+         PROMPT_RECREATE, 0 },
+
        /* Pass 1 errors */
        
        /* Pass 1: Checking inodes, blocks, and sizes */
@@ -718,7 +726,12 @@ static const struct e2fsck_problem problem_table[] = {
          N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
             "@f metadata.  "),
          PROMPT_CLEAR, PR_LATCH_BBLOCK },
-                 
+
+       /* Resize inode failed */
+       { PR_1_RESIZE_INODE_CREATE,
+         N_("Resize @i (re)creation failed: %m."),
+         PROMPT_ABORT, 0 },
+
        /* Pass 1b errors */
 
        /* Pass 1B: Rescan for duplicate/bad blocks */
index 1eb4ab6b7110085e1c13f5c4d4d7f00a2b00804f..aa0d30e4b6afb740a1f9716a428e1fbdbe5f7c1d 100644 (file)
@@ -181,6 +181,9 @@ struct problem_context {
 /* Resize_inode not enabled, but resize inode is non-zero */
 #define PR_0_CLEAR_RESIZE_INODE                        0x00002F
 
+/* Resize inode invalid */
+#define PR_0_RESIZE_INODE_INVALID              0x000030
+
 /*
  * Pass 1 errors
  */
@@ -416,6 +419,9 @@ struct problem_context {
 /* Bad block has indirect block that conflicts with filesystem block */
 #define PR_1_BB_FS_BLOCK               0x01004D
 
+/* Resize inode failed */
+#define PR_1_RESIZE_INODE_CREATE       0x01004E
+
 /*
  * Pass 1b errors
  */
index 11ab7c9a2343e9905987ce7ba74d0eeb5e520476..71968ea662cd288bd7db099860b7253234ed89e3 100644 (file)
@@ -312,6 +312,116 @@ return_abort:
        return 1;
 }
 
+/*
+ * Check the resize inode to make sure it is sane.  We check both for
+ * the case where on-line resizing is not enabled (in which case the
+ * resize inode should be cleared) as well as the case where on-line
+ * resizing is enabled.
+ */
+void check_resize_inode(e2fsck_t ctx)
+{
+       ext2_filsys fs = ctx->fs;
+       struct ext2_inode inode;
+       struct problem_context  pctx;
+       int             i, j, gdt_off, ind_off;
+       blk_t           blk, pblk, expect;
+       __u32           *dind_buf = 0, *ind_buf;
+       errcode_t       retval;
+
+       clear_problem_context(&pctx);
+       pctx.ino = EXT2_RESIZE_INO;
+       retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
+       if (retval) {
+               ctx->flags |= E2F_FLAG_RESIZE_INODE;
+               return;
+       }
+
+       /* 
+        * If the resize inode feature isn't set, then
+        * s_reserved_gdt_blocks must be zero, and the resize inode
+        * must be cleared.
+        */
+       if (!(fs->super->s_feature_compat & 
+             EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
+               if (fs->super->s_reserved_gdt_blocks) {
+                       pctx.num = fs->super->s_reserved_gdt_blocks;
+                       if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
+                                       &pctx)) {
+                               fs->super->s_reserved_gdt_blocks = 0;
+                               ext2fs_mark_super_dirty(fs);
+                       }
+               }
+               for (i=0; i < EXT2_N_BLOCKS; i++) {
+                       if (inode.i_block[i])
+                               break;
+               }
+               if ((i < EXT2_N_BLOCKS) &&
+                   fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
+                       memset(&inode, 0, sizeof(inode));
+                       e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
+                                          "clear_resize");
+               }
+               return;
+       }
+       /* 
+        * The resize inode feature is enabled; check to make sure the
+        * only block in use is the double indirect block
+        */
+       blk = inode.i_block[EXT2_DIND_BLOCK];
+       for (i=0; i < EXT2_N_BLOCKS; i++) {
+               if (i != EXT2_DIND_BLOCK && inode.i_block[i])
+                       break;
+       }
+       if ((i < EXT2_N_BLOCKS) || !blk ||
+           (blk < fs->super->s_first_data_block ||
+            blk >= fs->super->s_blocks_count)) {
+       resize_inode_invalid:
+               if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
+                       memset(&inode, 0, sizeof(inode));
+                       e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
+                                          "clear_resize");
+                       ctx->flags |= E2F_FLAG_RESIZE_INODE;
+               }
+               if (!(ctx->options & E2F_OPT_READONLY)) {
+                       fs->super->s_state &= ~EXT2_VALID_FS;
+                       ext2fs_mark_super_dirty(fs);
+               }
+               goto cleanup;
+       }
+       dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
+                                                   "resize dind buffer");
+       ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
+
+       retval = io_channel_read_blk(fs->io, blk, 1, dind_buf);
+       if (retval)
+               goto resize_inode_invalid;
+
+       gdt_off = fs->desc_blocks;
+       pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
+       for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4; 
+            i++, gdt_off++, pblk++) {
+               gdt_off %= fs->blocksize/4;
+               if (dind_buf[gdt_off] != pblk)
+                       goto resize_inode_invalid;
+               retval = io_channel_read_blk(fs->io, pblk, 1, ind_buf);
+               if (retval) 
+                       goto resize_inode_invalid;
+               ind_off = 0;
+               for (j = 1; j < fs->group_desc_count; j++) {
+                       if (!ext2fs_bg_has_super(fs, j))
+                               continue;
+                       expect = pblk + (j * fs->super->s_blocks_per_group);
+                       if (ind_buf[ind_off] != expect)
+                               goto resize_inode_invalid;
+                       ind_off++;
+               }
+       }
+
+cleanup:
+       if (dind_buf)
+               ext2fs_free_mem(&dind_buf);
+
+ }
 
 void check_super_block(e2fsck_t ctx)
 {
@@ -325,7 +435,6 @@ void check_super_block(e2fsck_t ctx)
        dgrp_t  i;
        blk_t   should_be;
        struct problem_context  pctx;
-       struct ext2_inode inode;
        __u32   free_blocks = 0, free_inodes = 0;
 
        inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
@@ -368,6 +477,9 @@ void check_super_block(e2fsck_t ctx)
                          MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
        check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
                          MAX_CHECK, 0, sb->s_blocks_count / 4);
+       check_super_value(ctx, "reserved_gdt_blocks", 
+                         sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
+                         fs->blocksize/4);
 
        if (!ctx->num_blocks) {
                pctx.errcode = e2fsck_get_device_size(ctx);
@@ -542,38 +654,7 @@ void check_super_block(e2fsck_t ctx)
                ext2fs_mark_super_dirty(fs);
        }
 
-       /* 
-        * If the resize inode feature isn't set, then
-        * s_reserved_gdt_blocks must be zero, and the resize inode
-        * must be cleared.
-        */
-       if (!(fs->super->s_feature_compat & 
-             EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
-               if (fs->super->s_reserved_gdt_blocks) {
-                       pctx.num = fs->super->s_reserved_gdt_blocks;
-                       if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
-                                       &pctx)) {
-                               fs->super->s_reserved_gdt_blocks = 0;
-                               ext2fs_mark_super_dirty(fs);
-                       }
-               }
-               e2fsck_read_inode(ctx, EXT2_RESIZE_INO, &inode, 
-                                 "check_resize");
-               for (i=0; i < EXT2_N_BLOCKS; i++) {
-                       if (inode.i_block[i])
-                               break;
-               }
-               pctx.ino = EXT2_RESIZE_INO;
-               if ((i < EXT2_N_BLOCKS) &&
-                   fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
-                       for (i=0; i < EXT2_N_BLOCKS; i++) {
-                               inode.i_block[i] = 0;
-                       }
-                       inode.i_blocks = 0;
-                       e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
-                                          "clear_resize");
-               }
-       }
+       check_resize_inode(ctx);
 
        /*
         * Clean up any orphan inodes, if present.