Whamcloud - gitweb
e2fsck: Add support for extents
authorTheodore Ts'o <tytso@mit.edu>
Tue, 21 Aug 2007 01:31:11 +0000 (21:31 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 19 Feb 2008 01:06:18 +0000 (20:06 -0500)
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
e2fsck/message.c
e2fsck/pass1.c
e2fsck/pass1b.c
e2fsck/problem.c
e2fsck/problem.h
lib/ext2fs/ext2fs.h
tests/f_bad_disconnected_inode/expect.1
tests/f_bad_disconnected_inode/expect.2

index b2e3e0f..8a3705e 100644 (file)
@@ -80,6 +80,7 @@
  *     @S      superblock
  *     @u      unattached
  *     @v      device
+ *     @x      extent
  *     @z      zero-length
  */
 
@@ -134,6 +135,7 @@ static const char *abbrevs[] = {
        N_("Ssuper@b"),
        N_("uunattached"),
        N_("vdevice"),
+       N_("xextent"),
        N_("zzero-length"),
        "@@",
        0
@@ -388,7 +390,11 @@ static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
                fputc('%', stdout);
                break;
        case 'b':
-               printf("%u", ctx->blk);
+#ifdef EXT2_NO_64_TYPE
+               printf("%u", (unsigned long) ctx->blk);
+#else
+               printf("%llu", (unsigned long long) ctx->blk);
+#endif
                break;
        case 'B':
 #ifdef EXT2_NO_64_TYPE
@@ -398,7 +404,11 @@ static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
 #endif
                break;
        case 'c':
-               printf("%u", ctx->blk2);
+#ifdef EXT2_NO_64_TYPE
+               printf("%u", (unsigned long) ctx->blk2);
+#else
+               printf("%llu", (unsigned long long) ctx->blk2);
+#endif
                break;
        case 'd':
                printf("%u", ctx->dir);
index e1aa029..03232e5 100644 (file)
@@ -480,7 +480,7 @@ void e2fsck_pass1(e2fsck_t ctx)
        struct          scan_callback_struct scan_struct;
        struct ext2_super_block *sb = ctx->fs->super;
        const char      *old_op;
-       int             imagic_fs;
+       int             imagic_fs, extent_fs;
        int             busted_fs_time = 0;
        int             inode_size;
        
@@ -514,6 +514,7 @@ void e2fsck_pass1(e2fsck_t ctx)
 #undef EXT2_BPP
 
        imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
+       extent_fs = (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS);
 
        /*
         * Allocate bitmaps structures
@@ -653,6 +654,39 @@ void e2fsck_pass1(e2fsck_t ctx)
                                return;
                        }
                }
+               
+               if ((inode->i_flags & EXT4_EXTENTS_FL) && !extent_fs && 
+                   (inode->i_links_count || (ino == EXT2_BAD_INO) ||
+                    (ino == EXT2_ROOT_INO) || (ino == EXT2_JOURNAL_INO))) {
+                       if ((ext2fs_extent_header_verify(inode->i_block, 
+                                                sizeof(inode->i_block)) == 0) &&
+                           fix_problem(ctx, PR_1_EXTENT_FEATURE, &pctx)) {
+                               sb->s_feature_incompat |= EXT3_FEATURE_INCOMPAT_EXTENTS;
+                               ext2fs_mark_super_dirty(fs);
+                               extent_fs = 1;
+                       } else if (fix_problem(ctx, PR_1_EXTENTS_SET, &pctx)) {
+                       clear_inode:
+                               e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
+                               if (ino == EXT2_BAD_INO)
+                                       ext2fs_mark_inode_bitmap(ctx->inode_used_map, 
+                                                                ino);
+                               continue;
+                       }
+               }
+
+               if (extent_fs && !(inode->i_flags & EXT4_EXTENTS_FL) &&
+                   (inode->i_links_count || (ino == EXT2_BAD_INO) ||
+                    (ino == EXT2_ROOT_INO) || (ino == EXT2_JOURNAL_INO)) &&
+                   (LINUX_S_ISREG(inode->i_mode) ||
+                    LINUX_S_ISDIR(inode->i_mode)) &&
+                   (ext2fs_extent_header_verify(inode->i_block, 
+                                                sizeof(inode->i_block)) == 0)) {
+                       if (fix_problem(ctx, PR_1_UNSET_EXTENT_FL, &pctx)) {
+                               inode->i_flags |= EXT4_EXTENTS_FL;
+                               e2fsck_write_inode(ctx, ino, inode, "pass1");
+                       }
+               }
+
                if (ino == EXT2_BAD_INO) {
                        struct process_block_struct pb;
                        
@@ -695,10 +729,8 @@ void e2fsck_pass1(e2fsck_t ctx)
                         * regnerated in pass #3.
                         */
                        if (!LINUX_S_ISDIR(inode->i_mode)) {
-                               if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
-                                       e2fsck_clear_inode(ctx, ino, inode,
-                                                          0, "pass1");
-                               }
+                               if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx))
+                                       goto clear_inode;
                        }
                        /*
                         * If dtime is set, offer to clear it.  mke2fs
@@ -904,10 +936,11 @@ void e2fsck_pass1(e2fsck_t ctx)
                        ctx->fs_dind_count++;
                if (inode->i_block[EXT2_TIND_BLOCK])
                        ctx->fs_tind_count++;
-               if (inode->i_block[EXT2_IND_BLOCK] ||
-                   inode->i_block[EXT2_DIND_BLOCK] ||
-                   inode->i_block[EXT2_TIND_BLOCK] ||
-                   inode->i_file_acl) {
+               if (!(inode->i_flags & EXT4_EXTENTS_FL) &&
+                   (inode->i_block[EXT2_IND_BLOCK] ||
+                    inode->i_block[EXT2_DIND_BLOCK] ||
+                    inode->i_block[EXT2_TIND_BLOCK] ||
+                    inode->i_file_acl)) {
                        inodes_to_process[process_inode_count].ino = ino;
                        inodes_to_process[process_inode_count].inode = *inode;
                        process_inode_count++;
@@ -1407,8 +1440,7 @@ clear_extattr:
 
 /* Returns 1 if bad htree, 0 if OK */
 static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
-                       ext2_ino_t ino EXT2FS_ATTR((unused)),
-                       struct ext2_inode *inode,
+                       ext2_ino_t ino, struct ext2_inode *inode,
                        char *block_buf)
 {
        struct ext2_dx_root_info        *root;
@@ -1422,12 +1454,17 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
             fix_problem(ctx, PR_1_HTREE_SET, pctx)))
                return 1;
 
-       blk = inode->i_block[0];
-       if (((blk == 0) ||
-            (blk < fs->super->s_first_data_block) ||
-            (blk >= fs->super->s_blocks_count)) &&
-           fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
-               return 1;
+       pctx->errcode = ext2fs_bmap(fs, ino, inode, 0, 0, 0, &blk);
+
+       if ((pctx->errcode) ||
+           (blk == 0) ||
+           (blk < fs->super->s_first_data_block) ||
+           (blk >= fs->super->s_blocks_count)) {
+               if (fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
+                       return 1;
+               else
+                       return 0;
+       }
 
        retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
        if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
@@ -1463,7 +1500,7 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino,
                        struct ext2_inode *inode, int restart_flag,
                        const char *source)
 {
-
+       inode->i_flags = 0;
        inode->i_links_count = 0;
        ext2fs_icount_store(ctx->inode_link_info, ino, 0);
        inode->i_dtime = ctx->now;
@@ -1484,6 +1521,119 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino,
        e2fsck_write_inode(ctx, ino, inode, source);
 }
 
+static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
+                            struct process_block_struct *pb, 
+                            ext2_extent_handle_t ehandle)
+{
+       struct ext2fs_extent    extent;
+       blk_t                   blk;
+       e2_blkcnt_t             blockcnt;
+       int                     i;
+       int                     is_dir, is_leaf;
+       errcode_t               problem;
+
+
+       pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_FIRST_SIB,
+                                         &extent);
+       while (!pctx->errcode) {
+               is_leaf = extent.e_flags & EXT2_EXTENT_FLAGS_LEAF;
+               is_dir = LINUX_S_ISDIR(pctx->inode->i_mode);
+
+               problem = 0;
+               if (extent.e_pblk < ctx->fs->super->s_first_data_block ||
+                   extent.e_pblk >= ctx->fs->super->s_blocks_count)
+                       problem = PR_1_EXTENT_BAD_START_BLK;
+               else if ((extent.e_pblk + extent.e_len) >
+                        ctx->fs->super->s_blocks_count)
+                       problem = PR_1_EXTENT_ENDS_BEYOND;
+
+               if (problem) {
+                       pctx->blk = extent.e_pblk;
+                       pctx->blk2 = extent.e_lblk;
+                       pctx->num = extent.e_len;
+                       if (fix_problem(ctx, problem, pctx)) {
+                               pctx->errcode =
+                                       ext2fs_extent_delete(ehandle, 0);
+                               if (pctx->errcode) {
+                                       fix_problem(ctx,
+                                                   PR_1_EXTENT_DELETE_FAIL,
+                                                   pctx);
+                                       /* Should never get here */
+                                       ctx->flags |= E2F_FLAG_ABORT;
+                                       return;
+                               }
+                       }
+                       goto next;
+               }
+
+               if (!is_leaf) {
+                       mark_block_used(ctx, extent.e_pblk);
+                       pctx->errcode = ext2fs_extent_get(ehandle,
+                                                 EXT2_EXTENT_DOWN, &extent);
+                       if (pctx->errcode) {
+                               printf("Error1: %s\n", error_message(pctx->errcode));
+                               abort();
+                       }
+                       scan_extent_node(ctx, pctx, pb, ehandle);
+                       pctx->errcode = ext2fs_extent_get(ehandle,
+                                                 EXT2_EXTENT_UP, &extent);
+                       if (pctx->errcode) {
+                               printf("Error1: %s\n", error_message(pctx->errcode));
+                               abort();
+                       }
+                       goto next;
+               }
+
+               for (blk = extent.e_pblk, blockcnt = extent.e_lblk, i = 0;
+                    i < extent.e_len;
+                    blk++, blockcnt++, i++) {
+                       mark_block_used(ctx, blk);
+
+                       if (is_dir) {
+                               pctx->errcode = ext2fs_add_dir_block(ctx->fs->dblist, pctx->ino, blk, blockcnt);
+                               if (pctx->errcode) {
+                                       pctx->blk = blk;
+                                       pctx->num = blockcnt;
+                                       fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
+                                       /* Should never get here */
+                                       ctx->flags |= E2F_FLAG_ABORT;
+                                       return;
+                               }
+                       }
+               }
+               pb->num_blocks += extent.e_len;
+               pb->last_block = extent.e_lblk + extent.e_len - 1;
+       next:
+               pctx->errcode = ext2fs_extent_get(ehandle,
+                                                 EXT2_EXTENT_NEXT_SIB,
+                                                 &extent);
+       }
+       if (pctx->errcode == EXT2_ET_EXTENT_NO_NEXT)
+               pctx->errcode = 0;
+}
+
+static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx,
+                                struct process_block_struct *pb, 
+                                char *block_buf)
+{
+       struct ext2_inode       *inode = pctx->inode;
+       ext2_extent_handle_t    ehandle;
+       ext2_filsys             fs = ctx->fs;
+       ext2_ino_t              ino = pctx->ino;
+
+       pctx->errcode = ext2fs_extent_open(fs, ino, &ehandle);
+       if (pctx->errcode &&
+           fix_problem(ctx, PR_1_READ_EXTENT, pctx)) {
+               e2fsck_clear_inode(ctx, ino, inode, 0, "check_blocks_extents");
+               pctx->errcode = 0;
+               return;
+       }
+
+       scan_extent_node(ctx, pctx, pb, ehandle);
+
+       ext2fs_extent_free(ehandle);
+}
+
 /*
  * This subroutine is called on each inode to account for all of the
  * blocks used by that inode.
@@ -1531,10 +1681,16 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
                pb.num_blocks++;
 
-       if (ext2fs_inode_has_valid_blocks(inode))
-               pctx->errcode = ext2fs_block_iterate2(fs, ino,
-                                      pb.is_dir ? BLOCK_FLAG_HOLE : 0,
-                                      block_buf, process_block, &pb);
+       if (ext2fs_inode_has_valid_blocks(inode)) {
+               if ((ctx->fs->super->s_feature_incompat &
+                    EXT3_FEATURE_INCOMPAT_EXTENTS) &&
+                   (inode->i_flags & EXT4_EXTENTS_FL))
+                       check_blocks_extents(ctx, pctx, &pb, block_buf);
+               else
+                       pctx->errcode = ext2fs_block_iterate2(fs, ino,
+                                               pb.is_dir ? BLOCK_FLAG_HOLE : 0,
+                                               block_buf, process_block, &pb);
+       }
        end_problem_latch(ctx, PR_LATCH_BLOCK);
        end_problem_latch(ctx, PR_LATCH_TOOBIG);
        if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
index 5d062ca..6956d23 100644 (file)
@@ -294,7 +294,8 @@ static void pass1b(e2fsck_t ctx, char *block_buf)
                if (ext2fs_inode_has_valid_blocks(&inode) ||
                    (ino == EXT2_BAD_INO))
                        pctx.errcode = ext2fs_block_iterate2(fs, ino,
-                                    0, block_buf, process_pass1b_block, &pb);
+                                            BLOCK_FLAG_READ_ONLY, block_buf,
+                                            process_pass1b_block, &pb);
                if (inode.i_file_acl)
                        process_pass1b_block(fs, &inode.i_file_acl,
                                             BLOCK_COUNT_EXTATTR, 0, 0, &pb);
@@ -590,8 +591,8 @@ static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
 
        e2fsck_read_inode(ctx, ino, &inode, "delete_file");
        if (ext2fs_inode_has_valid_blocks(&inode))
-               pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
-                                                    delete_file_block, &pb);
+               pctx.errcode = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_READ_ONLY, 
+                                                    block_buf, delete_file_block, &pb);
        if (pctx.errcode)
                fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
        if (ctx->inode_bad_map)
index 7c3ebea..6a127c6 100644 (file)
@@ -784,6 +784,41 @@ static struct e2fsck_problem problem_table[] = {
          N_("@i %i is a %It but it looks like it is really a directory.\n"),
          PROMPT_FIX, 0 },
 
+       /* Error while reading extent tree */
+       { PR_1_READ_EXTENT,
+         N_("Error while reading over @x tree in @i %i: %m\n"),
+         PROMPT_CLEAR_INODE, 0 },
+
+       /* Error deleting a bogus extent */
+       { PR_1_EXTENT_DELETE_FAIL,
+         N_("Error while deleting extent: %m\n"),
+         PROMPT_ABORT, 0 },
+
+       /* Bad starting block in extent */
+       { PR_1_EXTENT_BAD_START_BLK,
+         N_("@i %i has an @n extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"),
+         PROMPT_CLEAR, 0 },
+
+       /* Extent ends beyond filesystem */
+       { PR_1_EXTENT_ENDS_BEYOND,
+         N_("@i %i has an @n extent\n\t(logical @b %c, physical @b %b, @n len %N)\n"),
+         PROMPT_CLEAR, 0 },
+
+       /* EXTENTS_FL flag set on a non-extents filesystem */
+       { PR_1_EXTENTS_SET,
+         N_("@i %i has EXTENTS_FL flag set on @f without extents support.\n"),
+         PROMPT_CLEAR, 0 },
+
+       /* inode has extents, superblock missing INCOMPAT_EXTENTS feature */
+       { PR_1_EXTENT_FEATURE,
+         N_("@i %i is in extent format, but @S is missing EXTENTS feature\n"),
+         PROMPT_FIX, 0 },
+
+       /* inode missing EXTENTS_FL, but is an extent inode */
+       { PR_1_UNSET_EXTENT_FL,
+         N_("@i %i missing EXTENT_FL, but is in extents format\n"),
+         PROMPT_FIX, PR_PREEN_OK },
+
        /* Pass 1b errors */
 
        /* Pass 1B: Rescan for duplicate/bad blocks */
index f5f7212..55a197d 100644 (file)
@@ -16,7 +16,7 @@ struct problem_context {
        ext2_ino_t ino, ino2, dir;
        struct ext2_inode *inode;
        struct ext2_dir_entry *dirent;
-       blk_t   blk, blk2;
+       blk64_t blk, blk2;
        e2_blkcnt_t     blkcount;
        int             group;
        __u64   num;
@@ -455,6 +455,27 @@ struct problem_context {
 /* inode appears to be a directory */
 #define PR_1_TREAT_AS_DIRECTORY                0x010055
 
+/* Error while reading extent tree */
+#define PR_1_READ_EXTENT               0x010056
+
+/* Error deleting a bogus extent */
+#define PR_1_EXTENT_DELETE_FAIL                0x010057
+
+/* Bad starting block in extent */
+#define PR_1_EXTENT_BAD_START_BLK      0x010058
+
+/* Extent ends beyond filesystem */
+#define PR_1_EXTENT_ENDS_BEYOND                0x010059
+
+/* EXTENTS_FL flag set on a non-extents capable filesystem */
+#define PR_1_EXTENTS_SET               0x01005A
+
+/* inode has extents, superblock missing INCOMPAT_EXTENTS feature */
+#define PR_1_EXTENT_FEATURE            0x01005B
+
+/* inode missing EXTENTS_FL, but is an extent inode */
+#define PR_1_UNSET_EXTENT_FL           0x01005C
+
 /*
  * Pass 1b errors
  */
index 02d2b42..b8c316c 100644 (file)
@@ -502,12 +502,14 @@ typedef struct ext2_icount *ext2_icount_t;
                                         EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
                                         EXT2_FEATURE_INCOMPAT_META_BG|\
                                         EXT3_FEATURE_INCOMPAT_RECOVER|\
+                                        EXT3_FEATURE_INCOMPAT_EXTENTS|\
                                         EXT4_FEATURE_INCOMPAT_FLEX_BG)
 #else
 #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
                                         EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
                                         EXT2_FEATURE_INCOMPAT_META_BG|\
                                         EXT3_FEATURE_INCOMPAT_RECOVER|\
+                                        EXT3_FEATURE_INCOMPAT_EXTENTS|\
                                         EXT4_FEATURE_INCOMPAT_FLEX_BG)
 #endif
 #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP        (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
index b4851f0..11862f6 100644 (file)
@@ -1,4 +1,13 @@
 Pass 1: Checking inodes, blocks, and sizes
+Inode 1 has EXTENTS_FL flag set on filesystem without extents support.
+Clear? yes
+
+Inode 15 has EXTENTS_FL flag set on filesystem without extents support.
+Clear? yes
+
+Inode 16 has EXTENTS_FL flag set on filesystem without extents support.
+Clear? yes
+
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 /lost+found not found.  Create? yes
@@ -18,26 +27,6 @@ Clear? yes
 Inode 14 (...) has invalid mode (0154247).
 Clear? yes
 
-i_file_acl for inode 15 (...) is 1143674715, should be zero.
-Clear? yes
-
-Inode 15 (...) has invalid mode (074044).
-Clear? yes
-
-i_file_acl for inode 16 (...) is 2007517039, should be zero.
-Clear? yes
-
-i_faddr for inode 16 (...) is 1003914917, should be zero.
-Clear? yes
-
-i_blocks_hi for inode 16 (...) is 62762, should be zero.
-Clear? yes
-
-Unattached inode 16
-Connect to /lost+found? yes
-
-Inode 16 ref count is 5925, should be 1.  Fix? yes
-
 Pass 5: Checking group summary information
 Block bitmap differences:  -(9--19)
 Fix? yes
@@ -48,19 +37,16 @@ Fix? yes
 Free blocks count wrong (79, counted=91).
 Fix? yes
 
-Inode bitmap differences:  +16
-Fix? yes
-
-Free inodes count wrong for group #0 (7, counted=4).
+Free inodes count wrong for group #0 (6, counted=5).
 Fix? yes
 
 Directories count wrong for group #0 (3, counted=2).
 Fix? yes
 
-Free inodes count wrong (7, counted=4).
+Free inodes count wrong (6, counted=5).
 Fix? yes
 
 
 test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
-test_filesys: 12/16 files (0.0% non-contiguous), 9/100 blocks
+test_filesys: 11/16 files (0.0% non-contiguous), 9/100 blocks
 Exit status is 1
index 1739210..8dfeb70 100644 (file)
@@ -3,5 +3,5 @@ Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 12/16 files (0.0% non-contiguous), 9/100 blocks
+test_filesys: 11/16 files (0.0% non-contiguous), 9/100 blocks
 Exit status is 0