Whamcloud - gitweb
libext2fs: strengthen i_extra_isize checks when reading/writing xattrs
[tools/e2fsprogs.git] / resize / resize2fs.c
index a8af969..2febfde 100644 (file)
@@ -56,6 +56,9 @@ static errcode_t mark_table_blocks(ext2_filsys fs,
 static errcode_t clear_sparse_super2_last_group(ext2_resize_t rfs);
 static errcode_t reserve_sparse_super2_last_group(ext2_resize_t rfs,
                                                 ext2fs_block_bitmap meta_bmap);
+static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size);
+static errcode_t move_bg_metadata(ext2_resize_t rfs);
+static errcode_t zero_high_bits_in_inodes(ext2_resize_t rfs);
 
 /*
  * Some helper CPP macros
@@ -122,13 +125,30 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
        if (retval)
                goto errout;
 
+       init_resource_track(&rtrack, "resize_group_descriptors", fs->io);
+       retval = resize_group_descriptors(rfs, *new_size);
+       if (retval)
+               goto errout;
+       print_resource_track(rfs, &rtrack, fs->io);
+
+       init_resource_track(&rtrack, "move_bg_metadata", fs->io);
+       retval = move_bg_metadata(rfs);
+       if (retval)
+               goto errout;
+       print_resource_track(rfs, &rtrack, fs->io);
+
+       init_resource_track(&rtrack, "zero_high_bits_in_metadata", fs->io);
+       retval = zero_high_bits_in_inodes(rfs);
+       if (retval)
+               goto errout;
+       print_resource_track(rfs, &rtrack, fs->io);
+
        init_resource_track(&rtrack, "adjust_superblock", fs->io);
        retval = adjust_superblock(rfs, *new_size);
        if (retval)
                goto errout;
        print_resource_track(rfs, &rtrack, fs->io);
 
-
        init_resource_track(&rtrack, "fix_uninit_block_bitmaps 2", fs->io);
        fix_uninit_block_bitmaps(rfs->new_fs);
        print_resource_track(rfs, &rtrack, fs->io);
@@ -198,11 +218,15 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
        if (retval)
                goto errout;
 
+       retval = ext2fs_set_gdt_csum(rfs->new_fs);
+       if (retval)
+               goto errout;
+
        rfs->new_fs->super->s_state &= ~EXT2_ERROR_FS;
        rfs->new_fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
 
        print_resource_track(rfs, &overall_track, fs->io);
-       retval = ext2fs_close(rfs->new_fs);
+       retval = ext2fs_close_free(&rfs->new_fs);
        if (retval)
                goto errout;
 
@@ -231,6 +255,322 @@ errout:
        return retval;
 }
 
+/* Keep the size of the group descriptor region constant */
+static void adjust_reserved_gdt_blocks(ext2_filsys old_fs, ext2_filsys fs)
+{
+       if ((fs->super->s_feature_compat &
+            EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
+           (old_fs->desc_blocks != fs->desc_blocks)) {
+               int new;
+
+               new = ((int) fs->super->s_reserved_gdt_blocks) +
+                       (old_fs->desc_blocks - fs->desc_blocks);
+               if (new < 0)
+                       new = 0;
+               if (new > (int) fs->blocksize/4)
+                       new = fs->blocksize/4;
+               fs->super->s_reserved_gdt_blocks = new;
+       }
+}
+
+/* Toggle 64bit mode */
+static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size)
+{
+       void *o, *n, *new_group_desc;
+       dgrp_t i;
+       int copy_size;
+       errcode_t retval;
+
+       if (!(rfs->flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)))
+               return 0;
+
+       if (new_size != ext2fs_blocks_count(rfs->new_fs->super) ||
+           ext2fs_blocks_count(rfs->new_fs->super) >= (1ULL << 32) ||
+           (rfs->flags & RESIZE_DISABLE_64BIT &&
+            rfs->flags & RESIZE_ENABLE_64BIT))
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       if (rfs->flags & RESIZE_DISABLE_64BIT) {
+               rfs->new_fs->super->s_feature_incompat &=
+                               ~EXT4_FEATURE_INCOMPAT_64BIT;
+               rfs->new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE;
+       } else if (rfs->flags & RESIZE_ENABLE_64BIT) {
+               rfs->new_fs->super->s_feature_incompat |=
+                               EXT4_FEATURE_INCOMPAT_64BIT;
+               rfs->new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT;
+       }
+
+       if (EXT2_DESC_SIZE(rfs->old_fs->super) ==
+           EXT2_DESC_SIZE(rfs->new_fs->super))
+               return 0;
+
+       o = rfs->new_fs->group_desc;
+       rfs->new_fs->desc_blocks = ext2fs_div_ceil(
+                       rfs->old_fs->group_desc_count,
+                       EXT2_DESC_PER_BLOCK(rfs->new_fs->super));
+       retval = ext2fs_get_arrayzero(rfs->new_fs->desc_blocks,
+                                     rfs->old_fs->blocksize, &new_group_desc);
+       if (retval)
+               return retval;
+
+       n = new_group_desc;
+
+       if (EXT2_DESC_SIZE(rfs->old_fs->super) <=
+           EXT2_DESC_SIZE(rfs->new_fs->super))
+               copy_size = EXT2_DESC_SIZE(rfs->old_fs->super);
+       else
+               copy_size = EXT2_DESC_SIZE(rfs->new_fs->super);
+       for (i = 0; i < rfs->old_fs->group_desc_count; i++) {
+               memcpy(n, o, copy_size);
+               n += EXT2_DESC_SIZE(rfs->new_fs->super);
+               o += EXT2_DESC_SIZE(rfs->old_fs->super);
+       }
+
+       ext2fs_free_mem(&rfs->new_fs->group_desc);
+       rfs->new_fs->group_desc = new_group_desc;
+
+       for (i = 0; i < rfs->old_fs->group_desc_count; i++)
+               ext2fs_group_desc_csum_set(rfs->new_fs, i);
+
+       adjust_reserved_gdt_blocks(rfs->old_fs, rfs->new_fs);
+
+       return 0;
+}
+
+/* Move bitmaps/inode tables out of the way. */
+static errcode_t move_bg_metadata(ext2_resize_t rfs)
+{
+       dgrp_t i;
+       blk64_t b, c, d, old_desc_blocks, new_desc_blocks, j;
+       ext2fs_block_bitmap old_map, new_map;
+       int old, new;
+       errcode_t retval;
+       int cluster_ratio;
+
+       if (!(rfs->flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)))
+               return 0;
+
+       retval = ext2fs_allocate_block_bitmap(rfs->old_fs, "oldfs", &old_map);
+       if (retval)
+               return retval;
+
+       retval = ext2fs_allocate_block_bitmap(rfs->new_fs, "newfs", &new_map);
+       if (retval)
+               goto out;
+
+       if (EXT2_HAS_INCOMPAT_FEATURE(rfs->old_fs->super,
+                                     EXT2_FEATURE_INCOMPAT_META_BG)) {
+               old_desc_blocks = rfs->old_fs->super->s_first_meta_bg;
+               new_desc_blocks = rfs->new_fs->super->s_first_meta_bg;
+       } else {
+               old_desc_blocks = rfs->old_fs->desc_blocks +
+                               rfs->old_fs->super->s_reserved_gdt_blocks;
+               new_desc_blocks = rfs->new_fs->desc_blocks +
+                               rfs->new_fs->super->s_reserved_gdt_blocks;
+       }
+
+       /* Construct bitmaps of super/descriptor blocks in old and new fs */
+       for (i = 0; i < rfs->old_fs->group_desc_count; i++) {
+               retval = ext2fs_super_and_bgd_loc2(rfs->old_fs, i, &b, &c, &d,
+                                                  NULL);
+               if (retval)
+                       goto out;
+               if (b)
+                       ext2fs_mark_block_bitmap2(old_map, b);
+               for (j = 0; c != 0 && j < old_desc_blocks; j++)
+                       ext2fs_mark_block_bitmap2(old_map, c + j);
+               if (d)
+                       ext2fs_mark_block_bitmap2(old_map, d);
+
+               retval = ext2fs_super_and_bgd_loc2(rfs->new_fs, i, &b, &c, &d,
+                                                  NULL);
+               if (retval)
+                       goto out;
+               if (b)
+                       ext2fs_mark_block_bitmap2(new_map, b);
+               for (j = 0; c != 0 && j < new_desc_blocks; j++)
+                       ext2fs_mark_block_bitmap2(new_map, c + j);
+               if (d)
+                       ext2fs_mark_block_bitmap2(new_map, d);
+       }
+
+       cluster_ratio = EXT2FS_CLUSTER_RATIO(rfs->new_fs);
+
+       /* Find changes in block allocations for bg metadata */
+       for (b = EXT2FS_B2C(rfs->old_fs,
+                           rfs->old_fs->super->s_first_data_block);
+            b < ext2fs_blocks_count(rfs->new_fs->super);
+            b += cluster_ratio) {
+               old = ext2fs_test_block_bitmap2(old_map, b);
+               new = ext2fs_test_block_bitmap2(new_map, b);
+
+               if (old && !new) {
+                       /* mark old_map, unmark new_map */
+                       if (cluster_ratio == 1)
+                               ext2fs_unmark_block_bitmap2(
+                                               rfs->new_fs->block_map, b);
+               } else if (!old && new)
+                       ; /* unmark old_map, mark new_map */
+               else {
+                       ext2fs_unmark_block_bitmap2(old_map, b);
+                       ext2fs_unmark_block_bitmap2(new_map, b);
+               }
+       }
+
+       /*
+        * new_map now shows blocks that have been newly allocated.
+        * old_map now shows blocks that have been newly freed.
+        */
+
+       /*
+        * Move any conflicting bitmaps and inode tables.  Ensure that we
+        * don't try to free clusters associated with bitmaps or tables.
+        */
+       for (i = 0; i < rfs->old_fs->group_desc_count; i++) {
+               b = ext2fs_block_bitmap_loc(rfs->new_fs, i);
+               if (ext2fs_test_block_bitmap2(new_map, b))
+                       ext2fs_block_bitmap_loc_set(rfs->new_fs, i, 0);
+               else if (ext2fs_test_block_bitmap2(old_map, b))
+                       ext2fs_unmark_block_bitmap2(old_map, b);
+
+               b = ext2fs_inode_bitmap_loc(rfs->new_fs, i);
+               if (ext2fs_test_block_bitmap2(new_map, b))
+                       ext2fs_inode_bitmap_loc_set(rfs->new_fs, i, 0);
+               else if (ext2fs_test_block_bitmap2(old_map, b))
+                       ext2fs_unmark_block_bitmap2(old_map, b);
+
+               c = ext2fs_inode_table_loc(rfs->new_fs, i);
+               for (b = 0;
+                    b < rfs->new_fs->inode_blocks_per_group;
+                    b++) {
+                       if (ext2fs_test_block_bitmap2(new_map, b + c))
+                               ext2fs_inode_table_loc_set(rfs->new_fs, i, 0);
+                       else if (ext2fs_test_block_bitmap2(old_map, b + c))
+                               ext2fs_unmark_block_bitmap2(old_map, b + c);
+               }
+       }
+
+       /* Free unused clusters */
+       for (b = 0;
+            cluster_ratio > 1 && b < ext2fs_blocks_count(rfs->new_fs->super);
+            b += cluster_ratio)
+               if (ext2fs_test_block_bitmap2(old_map, b))
+                       ext2fs_unmark_block_bitmap2(rfs->new_fs->block_map, b);
+out:
+       if (old_map)
+               ext2fs_free_block_bitmap(old_map);
+       if (new_map)
+               ext2fs_free_block_bitmap(new_map);
+       return retval;
+}
+
+/* Zero out the high bits of extent fields */
+static errcode_t zero_high_bits_in_extents(ext2_filsys fs, ext2_ino_t ino,
+                                struct ext2_inode *inode)
+{
+       ext2_extent_handle_t    handle;
+       struct ext2fs_extent    extent;
+       int                     op = EXT2_EXTENT_ROOT;
+       errcode_t               errcode;
+
+       if (!(inode->i_flags & EXT4_EXTENTS_FL))
+               return 0;
+
+       errcode = ext2fs_extent_open(fs, ino, &handle);
+       if (errcode)
+               return errcode;
+
+       while (1) {
+               errcode = ext2fs_extent_get(handle, op, &extent);
+               if (errcode)
+                       break;
+
+               op = EXT2_EXTENT_NEXT_SIB;
+
+               if (extent.e_pblk > (1ULL << 32)) {
+                       extent.e_pblk &= (1ULL << 32) - 1;
+                       errcode = ext2fs_extent_replace(handle, 0, &extent);
+                       if (errcode)
+                               break;
+               }
+       }
+
+       /* Ok if we run off the end */
+       if (errcode == EXT2_ET_EXTENT_NO_NEXT)
+               errcode = 0;
+       ext2fs_extent_free(handle);
+       return errcode;
+}
+
+/* Zero out the high bits of inodes. */
+static errcode_t zero_high_bits_in_inodes(ext2_resize_t rfs)
+{
+       ext2_filsys     fs = rfs->old_fs;
+       int length = EXT2_INODE_SIZE(fs->super);
+       struct ext2_inode *inode = NULL;
+       ext2_inode_scan scan = NULL;
+       errcode_t       retval;
+       ext2_ino_t      ino;
+
+       if (!(rfs->flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)))
+               return 0;
+
+       if (fs->super->s_creator_os != EXT2_OS_LINUX)
+               return 0;
+
+       retval = ext2fs_open_inode_scan(fs, 0, &scan);
+       if (retval)
+               return retval;
+
+       retval = ext2fs_get_mem(length, &inode);
+       if (retval)
+               goto out;
+
+       do {
+               retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
+               if (retval)
+                       goto out;
+               if (!ino)
+                       break;
+               if (!ext2fs_test_inode_bitmap2(fs->inode_map, ino))
+                       continue;
+
+               /*
+                * Here's how we deal with high block number fields:
+                *
+                *  - i_size_high has been been written out with i_size_lo
+                *    since the ext2 days, so no conversion is needed.
+                *
+                *  - i_blocks_hi is guarded by both the huge_file feature and
+                *    inode flags and has always been written out with
+                *    i_blocks_lo if the feature is set.  The field is only
+                *    ever read if both feature and inode flag are set, so
+                *    we don't need to zero it now.
+                *
+                *  - i_file_acl_high can be uninitialized, so zero it if
+                *    it isn't already.
+                */
+               if (inode->osd2.linux2.l_i_file_acl_high) {
+                       inode->osd2.linux2.l_i_file_acl_high = 0;
+                       retval = ext2fs_write_inode_full(fs, ino, inode,
+                                                        length);
+                       if (retval)
+                               goto out;
+               }
+
+               retval = zero_high_bits_in_extents(fs, ino, inode);
+               if (retval)
+                       goto out;
+       } while (ino);
+
+out:
+       if (inode)
+               ext2fs_free_mem(&inode);
+       if (scan)
+               ext2fs_close_inode_scan(scan);
+       return retval;
+}
+
 /*
  * Clean up the bitmaps for unitialized bitmaps
  */
@@ -435,8 +775,7 @@ retry:
                                            fs->inode_map);
        if (retval) goto errout;
 
-       real_end = (((blk64_t) EXT2_BLOCKS_PER_GROUP(fs->super) *
-                    fs->group_desc_count)) - 1 +
+       real_end = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count) - 1 +
                fs->super->s_first_data_block;
        retval = ext2fs_resize_block_bitmap2(new_size - 1,
                                             real_end, fs->block_map);
@@ -455,7 +794,8 @@ retry:
        /*
         * Reallocate the group descriptors as necessary.
         */
-       if (old_fs->desc_blocks != fs->desc_blocks) {
+       if (EXT2_DESC_SIZE(old_fs->super) == EXT2_DESC_SIZE(fs->super) &&
+           old_fs->desc_blocks != fs->desc_blocks) {
                retval = ext2fs_resize_mem(old_fs->desc_blocks *
                                           fs->blocksize,
                                           fs->desc_blocks * fs->blocksize,
@@ -474,19 +814,17 @@ retry:
         * number of descriptor blocks, then adjust
         * s_reserved_gdt_blocks if possible to avoid needing to move
         * the inode table either now or in the future.
+        *
+        * Note: If we're converting to 64bit mode, we did this earlier.
         */
-       if ((fs->super->s_feature_compat &
-            EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
-           (old_fs->desc_blocks != fs->desc_blocks)) {
-               int new;
+       if (EXT2_DESC_SIZE(old_fs->super) == EXT2_DESC_SIZE(fs->super))
+               adjust_reserved_gdt_blocks(old_fs, fs);
 
-               new = ((int) fs->super->s_reserved_gdt_blocks) +
-                       (old_fs->desc_blocks - fs->desc_blocks);
-               if (new < 0)
-                       new = 0;
-               if (new > (int) fs->blocksize/4)
-                       new = fs->blocksize/4;
-               fs->super->s_reserved_gdt_blocks = new;
+       if ((fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
+           (fs->super->s_first_meta_bg > fs->desc_blocks)) {
+               fs->super->s_feature_incompat &=
+                       ~EXT2_FEATURE_INCOMPAT_META_BG;
+               fs->super->s_first_meta_bg = 0;
        }
 
        /*
@@ -752,11 +1090,11 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size)
                /*
                 * Write out the new inode table
                 */
-               retval = io_channel_write_blk64(fs->io,
-                                               ext2fs_inode_table_loc(fs, i),
-                                               fs->inode_blocks_per_group,
-                                               rfs->itable_buf);
-               if (retval) goto errout;
+               retval = ext2fs_zero_blocks2(fs, ext2fs_inode_table_loc(fs, i),
+                                            fs->inode_blocks_per_group, NULL,
+                                            NULL);
+               if (retval)
+                       goto errout;
 
                io_channel_flush(fs->io);
                if (rfs->progress) {
@@ -994,19 +1332,23 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
                ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
        }
 
-       if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) {
+       if (old_fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
                old_blocks = old_fs->super->s_first_meta_bg;
+       else
+               old_blocks = old_fs->desc_blocks +
+                       old_fs->super->s_reserved_gdt_blocks;
+       if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
                new_blocks = fs->super->s_first_meta_bg;
-       } else {
-               old_blocks = old_fs->desc_blocks + old_fs->super->s_reserved_gdt_blocks;
+       else
                new_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
-       }
 
        retval = reserve_sparse_super2_last_group(rfs, meta_bmap);
        if (retval)
                goto errout;
 
-       if (old_blocks == new_blocks) {
+       if (EXT2_DESC_SIZE(rfs->old_fs->super) ==
+           EXT2_DESC_SIZE(rfs->new_fs->super) &&
+           old_blocks == new_blocks) {
                retval = 0;
                goto errout;
        }
@@ -1034,7 +1376,7 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
                }
 
                for (i = 0; i < max_groups; i++) {
-                       if (!ext2fs_bg_has_super(fs, i)) {
+                       if (!ext2fs_bg_has_super(old_fs, i)) {
                                group_blk += fs->super->s_blocks_per_group;
                                continue;
                        }
@@ -1484,16 +1826,6 @@ static int process_block(ext2_filsys fs, blk64_t *block_nr,
                }
        }
 
-       /*
-        * If we moved inodes and metadata_csum is enabled, we must force the
-        * extent block to be rewritten with new checksum.
-        */
-       if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
-           pb->has_extents &&
-           pb->old_ino != pb->ino)
-               ret |= BLOCK_CHANGED;
-
        if (pb->is_dir) {
                retval = ext2fs_add_dir_block2(fs->dblist, pb->ino,
                                               block, (int) blockcnt);
@@ -1536,7 +1868,7 @@ static errcode_t progress_callback(ext2_filsys fs,
 static errcode_t migrate_ea_block(ext2_resize_t rfs, ext2_ino_t ino,
                                  struct ext2_inode *inode, int *changed)
 {
-       char *buf;
+       char *buf = NULL;
        blk64_t new_block;
        errcode_t err = 0;
 
@@ -1573,6 +1905,64 @@ out:
        return err;
 }
 
+/* Rewrite extents */
+static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino)
+{
+       ext2_extent_handle_t    handle;
+       struct ext2fs_extent    extent;
+       errcode_t               errcode;
+       struct ext2_extent_info info;
+
+       errcode = ext2fs_extent_open(fs, ino, &handle);
+       if (errcode)
+               return errcode;
+
+       errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
+       if (errcode)
+               goto out;
+
+       do {
+               errcode = ext2fs_extent_get_info(handle, &info);
+               if (errcode)
+                       break;
+
+               /*
+                * If this is the first extent in an extent block that we
+                * haven't visited, rewrite the extent to force the ETB
+                * checksum to be rewritten.
+                */
+               if (info.curr_entry == 1 && info.curr_level != 0 &&
+                   !(extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)) {
+                       errcode = ext2fs_extent_replace(handle, 0, &extent);
+                       if (errcode)
+                               break;
+               }
+
+               /* Skip to the end of a block of leaf nodes */
+               if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
+                       errcode = ext2fs_extent_get(handle,
+                                                   EXT2_EXTENT_LAST_SIB,
+                                                   &extent);
+                       if (errcode)
+                               break;
+               }
+
+               errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
+       } while (errcode == 0);
+
+out:
+       /* Ok if we run off the end */
+       if (errcode == EXT2_ET_EXTENT_NO_NEXT)
+               errcode = 0;
+       ext2fs_extent_free(handle);
+       return errcode;
+}
+
+static void quiet_com_err_proc(const char *whoami, errcode_t code,
+                              const char *fmt, va_list args)
+{
+}
+
 static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
 {
        struct process_block_struct     pb;
@@ -1582,7 +1972,6 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
        errcode_t               retval;
        char                    *block_buf = 0;
        ext2_ino_t              start_to_move;
-       blk64_t                 orig_size;
        int                     inode_size;
 
        if ((rfs->old_fs->group_desc_count <=
@@ -1590,16 +1979,7 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
            !rfs->bmap)
                return 0;
 
-       /*
-        * Save the original size of the old filesystem, and
-        * temporarily set the size to be the new size if the new size
-        * is larger.  We need to do this to avoid catching an error
-        * by the block iterator routines
-        */
-       orig_size = ext2fs_blocks_count(rfs->old_fs->super);
-       if (orig_size < ext2fs_blocks_count(rfs->new_fs->super))
-               ext2fs_blocks_count_set(rfs->old_fs->super,
-                               ext2fs_blocks_count(rfs->new_fs->super));
+       set_com_err_hook(quiet_com_err_proc);
 
        retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan);
        if (retval) goto errout;
@@ -1691,6 +2071,17 @@ remap_blocks:
                if (retval)
                        goto errout;
 
+               /* Rewrite extent block checksums with new inode number */
+               if (EXT2_HAS_RO_COMPAT_FEATURE(rfs->old_fs->super,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+                   (inode->i_flags & EXT4_EXTENTS_FL)) {
+                       rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+                       retval = rewrite_extents(rfs->old_fs, new_inode);
+                       rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+                       if (retval)
+                               goto errout;
+               }
+
                /*
                 * Update inodes to point to new blocks; schedule directory
                 * blocks for inode remapping.  Need to write out dir blocks
@@ -1724,7 +2115,7 @@ remap_blocks:
        io_channel_flush(rfs->old_fs->io);
 
 errout:
-       ext2fs_blocks_count_set(rfs->old_fs->super, orig_size);
+       reset_com_err_hook();
        if (rfs->bmap) {
                ext2fs_free_extent_table(rfs->bmap);
                rfs->bmap = 0;
@@ -1920,8 +2311,10 @@ static errcode_t move_itables(ext2_resize_t rfs)
                    ext2fs_inode_table_loc(fs, i))
                        to_move++;
 
-       if (to_move == 0)
-               return 0;
+       if (to_move == 0) {
+               retval = 0;
+               goto errout;
+       }
 
        if (rfs->progress) {
                retval = rfs->progress(rfs, E2_RSZ_MOVE_ITABLE_PASS,
@@ -2118,7 +2511,7 @@ static errcode_t reserve_sparse_super2_last_group(ext2_resize_t rfs,
        if (retval)
                return retval;
 
-       if (!sb) {
+       if (last_bg && !sb) {
                fputs(_("Should never happen!  No sb in last super_sparse bg?\n"),
                      stderr);
                exit(1);
@@ -2178,15 +2571,11 @@ static errcode_t fix_resize_inode(ext2_filsys fs)
 {
        struct ext2_inode       inode;
        errcode_t               retval;
-       char                    *block_buf = NULL;
 
        if (!(fs->super->s_feature_compat &
              EXT2_FEATURE_COMPAT_RESIZE_INODE))
                return 0;
 
-       retval = ext2fs_get_mem(fs->blocksize, &block_buf);
-       if (retval) goto errout;
-
        retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
        if (retval) goto errout;
 
@@ -2206,19 +2595,16 @@ static errcode_t fix_resize_inode(ext2_filsys fs)
                exit(1);
        }
 
-       memset(block_buf, 0, fs->blocksize);
-
-       retval = io_channel_write_blk64(fs->io, inode.i_block[EXT2_DIND_BLOCK],
-                                       1, block_buf);
-       if (retval) goto errout;
+       retval = ext2fs_zero_blocks2(fs, inode.i_block[EXT2_DIND_BLOCK], 1,
+                                    NULL, NULL);
+       if (retval)
+               goto errout;
 
        retval = ext2fs_create_resize_inode(fs);
        if (retval)
                goto errout;
 
 errout:
-       if (block_buf)
-               ext2fs_free_mem(&block_buf);
        return retval;
 }
 
@@ -2394,7 +2780,7 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags)
                fs->super->s_free_inodes_count;
        blks_needed = ext2fs_div_ceil(inode_count,
                                      fs->super->s_inodes_per_group) *
-               EXT2_BLOCKS_PER_GROUP(fs->super);
+               (blk64_t) EXT2_BLOCKS_PER_GROUP(fs->super);
        groups = ext2fs_div64_ceil(blks_needed,
                                   EXT2_BLOCKS_PER_GROUP(fs->super));
 #ifdef RESIZE2FS_DEBUG
@@ -2441,7 +2827,7 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags)
         * figure out how many data blocks we have given the number of groups
         * we need for our inodes
         */
-       data_blocks = groups * EXT2_BLOCKS_PER_GROUP(fs->super);
+       data_blocks = EXT2_GROUPS_TO_BLOCKS(fs->super, groups);
        last_start = 0;
        for (grp = 0; grp < flex_groups; grp++) {
                overhead = calc_group_overhead(fs, grp, old_desc_blocks);
@@ -2479,7 +2865,7 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags)
                extra_grps = ext2fs_div64_ceil(remainder,
                                               EXT2_BLOCKS_PER_GROUP(fs->super));
 
-               data_blocks += extra_grps * EXT2_BLOCKS_PER_GROUP(fs->super);
+               data_blocks += EXT2_GROUPS_TO_BLOCKS(fs->super, extra_grps);
 
                /* ok we have to account for the last group */
                overhead = calc_group_overhead(fs, groups-1, old_desc_blocks);
@@ -2577,7 +2963,7 @@ blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags)
         * blocks needed to handle the group descriptor metadata+data
         * that we need
         */
-       blks_needed = (groups-1) * EXT2_BLOCKS_PER_GROUP(fs->super);
+       blks_needed = EXT2_GROUPS_TO_BLOCKS(fs->super, groups - 1);
        blks_needed += overhead;
 
        /*