Whamcloud - gitweb
libext2fs: fix endian handling of ext3_extent_header in inline_data
[tools/e2fsprogs.git] / e2fsck / super.c
index a5e8005..1e7e749 100644 (file)
@@ -22,6 +22,7 @@
 
 #define MIN_CHECK 1
 #define MAX_CHECK 2
+#define LOG2_CHECK 4
 
 static void check_super_value(e2fsck_t ctx, const char *descr,
                              unsigned long value, int flags,
@@ -29,8 +30,9 @@ static void check_super_value(e2fsck_t ctx, const char *descr,
 {
        struct          problem_context pctx;
 
-       if (((flags & MIN_CHECK) && (value < min_val)) ||
-           ((flags & MAX_CHECK) && (value > max_val))) {
+       if ((flags & MIN_CHECK && value < min_val) ||
+           (flags & MAX_CHECK && value > max_val) ||
+           (flags & LOG2_CHECK && (value & (value - 1)) != 0)) {
                clear_problem_context(&pctx);
                pctx.num = value;
                pctx.str = descr;
@@ -199,9 +201,9 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
                ext2fs_iblk_sub_blocks(fs, inode, pb.truncated_blocks);
 
        if (ext2fs_file_acl_block(fs, inode)) {
-               retval = ext2fs_adjust_ea_refcount2(fs,
-                                       ext2fs_file_acl_block(fs, inode),
-                                       block_buf, -1, &count);
+               retval = ext2fs_adjust_ea_refcount3(fs,
+                               ext2fs_file_acl_block(fs, inode),
+                               block_buf, -1, &count, ino);
                if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
                        retval = 0;
                        count = 1;
@@ -317,7 +319,8 @@ void check_resize_inode(e2fsck_t ctx)
        struct problem_context  pctx;
        int             i, gdt_off, ind_off;
        dgrp_t          j;
-       blk64_t         blk, pblk, expect;
+       blk_t           blk, pblk;
+       blk_t           expect; /* for resize inode, which is 32-bit only */
        __u32           *dind_buf = 0, *ind_buf;
        errcode_t       retval;
 
@@ -418,7 +421,7 @@ void check_resize_inode(e2fsck_t ctx)
                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);
+                       expect = pblk + EXT2_GROUPS_TO_BLOCKS(fs->super, j);
                        if (ind_buf[ind_off] != expect)
                                goto resize_inode_invalid;
                        ind_off++;
@@ -464,11 +467,11 @@ void check_super_block(e2fsck_t ctx)
        ext2_filsys fs = ctx->fs;
        blk64_t first_block, last_block;
        struct ext2_super_block *sb = fs->super;
+       unsigned int    ipg_max;
        problem_t       problem;
        blk64_t blocks_per_group = fs->super->s_blocks_per_group;
        __u32   bpg_max, cpg_max;
        int     inodes_per_block;
-       int     ipg_max;
        int     inode_size;
        int     accept_time_fudge;
        int     broken_system_clock;
@@ -526,14 +529,17 @@ void check_super_block(e2fsck_t ctx)
                          MAX_CHECK, 0, ext2fs_blocks_count(sb) / 2);
        check_super_value(ctx, "reserved_gdt_blocks",
                          sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
-                         fs->blocksize/4);
+                         fs->blocksize / sizeof(__u32));
+       check_super_value(ctx, "desc_size",
+                         sb->s_desc_size, MAX_CHECK | LOG2_CHECK, 0,
+                         EXT2_MAX_DESC_SIZE);
        if (sb->s_rev_level > EXT2_GOOD_OLD_REV)
                check_super_value(ctx, "first_ino", sb->s_first_ino,
                                  MIN_CHECK | MAX_CHECK,
                                  EXT2_GOOD_OLD_FIRST_INO, sb->s_inodes_count);
        inode_size = EXT2_INODE_SIZE(sb);
        check_super_value(ctx, "inode_size",
-                         inode_size, MIN_CHECK | MAX_CHECK,
+                         inode_size, MIN_CHECK | MAX_CHECK | LOG2_CHECK,
                          EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
        if (sb->s_blocks_per_group != (sb->s_clusters_per_group *
                                       EXT2FS_CLUSTER_RATIO(fs))) {
@@ -543,13 +549,6 @@ void check_super_block(e2fsck_t ctx)
                ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
                return;
        }
-       if (inode_size & (inode_size - 1)) {
-               pctx.num = inode_size;
-               pctx.str = "inode_size";
-               fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
-               ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
-               return;
-       }
 
        if ((ctx->flags & E2F_FLAG_GOT_DEVSIZE) &&
            (ctx->num_blocks < ext2fs_blocks_count(sb))) {
@@ -571,7 +570,9 @@ void check_super_block(e2fsck_t ctx)
                return;
        }
 
-       should_be = sb->s_inodes_per_group * fs->group_desc_count;
+       should_be = (blk64_t)sb->s_inodes_per_group * fs->group_desc_count;
+       if (should_be > UINT_MAX)
+               should_be = UINT_MAX;
        if (sb->s_inodes_count != should_be) {
                pctx.ino = sb->s_inodes_count;
                pctx.ino2 = should_be;
@@ -581,14 +582,49 @@ void check_super_block(e2fsck_t ctx)
                }
        }
 
+       /* Are metadata_csum and uninit_bg both set? */
+       if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+           EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                      EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+           fix_problem(ctx, PR_0_META_AND_GDT_CSUM_SET, &pctx)) {
+               fs->super->s_feature_ro_compat &=
+                       ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
+               ext2fs_mark_super_dirty(fs);
+               for (i = 0; i < fs->group_desc_count; i++)
+                       ext2fs_group_desc_csum_set(fs, i);
+       }
+
+       /* Is 64bit set and extents unset? */
+       if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+                                     EXT4_FEATURE_INCOMPAT_64BIT) &&
+           !EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+                                      EXT3_FEATURE_INCOMPAT_EXTENTS) &&
+           fix_problem(ctx, PR_0_64BIT_WITHOUT_EXTENTS, &pctx)) {
+               fs->super->s_feature_incompat |=
+                       EXT3_FEATURE_INCOMPAT_EXTENTS;
+               ext2fs_mark_super_dirty(fs);
+       }
+
+       if ((fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
+           (fs->super->s_first_meta_bg > fs->desc_blocks)) {
+               pctx.group = fs->desc_blocks;
+               pctx.num = fs->super->s_first_meta_bg;
+               if (fix_problem(ctx, PR_0_FIRST_META_BG_TOO_BIG, &pctx)) {
+                       fs->super->s_feature_incompat &=
+                               ~EXT2_FEATURE_INCOMPAT_META_BG;
+                       fs->super->s_first_meta_bg = 0;
+                       ext2fs_mark_super_dirty(fs);
+               }
+       }
+
        /*
         * Verify the group descriptors....
         */
        first_block = sb->s_first_data_block;
        last_block = ext2fs_blocks_count(sb)-1;
 
-       csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-                                              EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+       csum_flag = ext2fs_has_group_desc_csum(fs);
        for (i = 0; i < fs->group_desc_count; i++) {
                pctx.group = i;
 
@@ -712,8 +748,11 @@ void check_super_block(e2fsck_t ctx)
 #ifndef EXT2_SKIP_UUID
        /*
         * If the UUID field isn't assigned, assign it.
+        * Skip if checksums are enabled and the filesystem is mounted,
+        * if the id changes under the kernel remounting rw may fail.
         */
-       if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
+       if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid) &&
+           (!csum_flag || !(ctx->mount_flags & EXT2_MF_MOUNTED))) {
                if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
                        uuid_generate(sb->s_uuid);
                        ext2fs_init_csum_seed(fs);
@@ -915,8 +954,7 @@ int check_backup_super_block(e2fsck_t ctx)
                if (!ext2fs_bg_has_super(fs, g))
                        continue;
 
-               sb = fs->super->s_first_data_block +
-                       (g * fs->super->s_blocks_per_group);
+               sb = ext2fs_group_first_block2(fs, g);
 
                retval = io_channel_read_blk(fs->io, sb, -SUPERBLOCK_SIZE,
                                             buf);
@@ -946,6 +984,7 @@ int check_backup_super_block(e2fsck_t ctx)
                    SUPER_INCOMPAT_DIFFERENT(s_feature_incompat) ||
                    SUPER_RO_COMPAT_DIFFERENT(s_feature_ro_compat) ||
                    SUPER_DIFFERENT(s_blocks_count) ||
+                   SUPER_DIFFERENT(s_blocks_count_hi) ||
                    SUPER_DIFFERENT(s_inodes_count) ||
                    memcmp(fs->super->s_uuid, backup_sb->s_uuid,
                           sizeof(fs->super->s_uuid)))