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
+ * Some helper functions to check if a block is in a metadata area
*/
-#define IS_BLOCK_BM(fs, i, blk) ((blk) == ext2fs_block_bitmap_loc((fs),(i)))
-#define IS_INODE_BM(fs, i, blk) ((blk) == ext2fs_inode_bitmap_loc((fs),(i)))
+static inline int is_block_bm(ext2_filsys fs, unsigned int grp, blk64_t blk)
+{
+ return blk == ext2fs_block_bitmap_loc(fs, grp);
+}
+
+static inline int is_inode_bm(ext2_filsys fs, unsigned int grp, blk64_t blk)
+{
+ return blk == ext2fs_inode_bitmap_loc(fs, grp);
+}
-#define IS_INODE_TB(fs, i, blk) (((blk) >= ext2fs_inode_table_loc((fs), (i))) && \
- ((blk) < (ext2fs_inode_table_loc((fs), (i)) + \
- (fs)->inode_blocks_per_group)))
+static int is_inode_tb(ext2_filsys fs, unsigned int grp, blk64_t blk)
+{
+ return blk >= ext2fs_inode_table_loc(fs, grp) &&
+ blk < (ext2fs_inode_table_loc(fs, grp) +
+ fs->inode_blocks_per_group);
+}
/* Some bigalloc helper macros which are more succint... */
#define B2C(x) EXT2FS_B2C(fs, (x))
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);
goto errout;
print_resource_track(rfs, &rtrack, fs->io);
+ retval = clear_sparse_super2_last_group(rfs);
+ if (retval)
+ goto errout;
+
init_resource_track(&rtrack, "calculate_summary_stats", fs->io);
retval = ext2fs_calculate_summary_stats(rfs->new_fs);
if (retval)
goto errout;
print_resource_track(rfs, &rtrack, fs->io);
- retval = clear_sparse_super2_last_group(rfs);
+ retval = ext2fs_set_gdt_csum(rfs->new_fs);
if (retval)
goto 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 (ext2fs_has_feature_resize_inode(fs->super) &&
+ (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) {
+ ext2fs_clear_feature_64bit(rfs->new_fs->super);
+ rfs->new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE;
+ } else if (rfs->flags & RESIZE_ENABLE_64BIT) {
+ ext2fs_set_feature_64bit(rfs->new_fs->super);
+ 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 = (char *)n + EXT2_DESC_SIZE(rfs->new_fs->super);
+ o = (char *)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 (ext2fs_has_feature_meta_bg(rfs->old_fs->super)) {
+ 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_HURD)
+ 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
*/
{
blk64_t blk, lblk;
dgrp_t g;
- int i;
+ unsigned int i;
- if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)))
+ if (!ext2fs_has_group_desc_csum(fs))
return;
for (g=0; g < fs->group_desc_count; g++) {
ext2fs_mark_block_bitmap2(fs->block_map,
ext2fs_inode_bitmap_loc(fs, g));
for (i = 0, blk = ext2fs_inode_table_loc(fs, g);
- i < (unsigned int) fs->inode_blocks_per_group;
+ i < fs->inode_blocks_per_group;
i++, blk++)
ext2fs_mark_block_bitmap2(fs->block_map, blk);
}
ext2_filsys old_fs,
dgrp_t group)
{
- blk64_t blk;
- int j;
- dgrp_t i;
+ blk64_t blk;
+ unsigned int j;
+ dgrp_t i;
ext2fs_block_bitmap bg_map = NULL;
- errcode_t retval = 0;
- dgrp_t count = old_fs->group_desc_count - fs->group_desc_count;
+ errcode_t retval = 0;
+ dgrp_t count = old_fs->group_desc_count - fs->group_desc_count;
/* If bigalloc, don't free metadata living in the same cluster */
if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
blk64_t old_numblocks, numblocks, adjblocks;
unsigned long i, j, old_desc_blocks;
unsigned int meta_bg, meta_bg_size;
- int has_super, csum_flag;
+ int has_super, csum_flag, has_bg;
unsigned long long new_inodes; /* u64 to check for overflow */
double percent;
*/
overhead = (int) (2 + fs->inode_blocks_per_group);
- if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1))
+ has_bg = 0;
+ if (ext2fs_has_feature_sparse_super2(fs->super)) {
+ /*
+ * We have to do this manually since
+ * super->s_backup_bgs hasn't been set up yet.
+ */
+ if (fs->group_desc_count == 2)
+ has_bg = fs->super->s_backup_bgs[0] != 0;
+ else
+ has_bg = fs->super->s_backup_bgs[1] != 0;
+ } else
+ has_bg = ext2fs_bg_has_super(fs, fs->group_desc_count - 1);
+ if (has_bg)
overhead += 1 + fs->desc_blocks +
fs->super->s_reserved_gdt_blocks;
/*
* 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,
* 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;
-
- 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 (EXT2_DESC_SIZE(old_fs->super) == EXT2_DESC_SIZE(fs->super))
+ adjust_reserved_gdt_blocks(old_fs, fs);
- if ((fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
+ if (ext2fs_has_feature_meta_bg(fs->super) &&
(fs->super->s_first_meta_bg > fs->desc_blocks)) {
- fs->super->s_feature_incompat &=
- ~EXT2_FEATURE_INCOMPAT_META_BG;
+ ext2fs_clear_feature_meta_bg(fs->super);
fs->super->s_first_meta_bg = 0;
}
* Update the location of the backup superblocks if the
* sparse_super2 feature is enabled.
*/
- if (fs->super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2) {
+ if (ext2fs_has_feature_sparse_super2(fs->super)) {
dgrp_t last_bg = fs->group_desc_count - 1;
dgrp_t old_last_bg = old_fs->group_desc_count - 1;
if (last_bg > old_last_bg) {
if (old_fs->group_desc_count == 1)
fs->super->s_backup_bgs[0] = 1;
- if (old_fs->group_desc_count == 1 &&
- fs->super->s_backup_bgs[0])
- fs->super->s_backup_bgs[0] = last_bg;
- else if (fs->super->s_backup_bgs[1])
+ if ((old_fs->group_desc_count < 3 &&
+ fs->group_desc_count > 2) ||
+ fs->super->s_backup_bgs[1])
fs->super->s_backup_bgs[1] = last_bg;
} else if (last_bg < old_last_bg) {
if (fs->super->s_backup_bgs[0] > last_bg)
*/
group_block = ext2fs_group_first_block2(fs,
old_fs->group_desc_count);
- csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
- if (access("/sys/fs/ext4/features/lazy_itable_init", F_OK) == 0)
+ csum_flag = ext2fs_has_group_desc_csum(fs);
+ if (!getenv("RESIZE2FS_FORCE_ITABLE_INIT") &&
+ access("/sys/fs/ext4/features/lazy_itable_init", F_OK) == 0)
lazy_itable_init = 1;
- if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ if (ext2fs_has_feature_meta_bg(fs->super))
old_desc_blocks = fs->super->s_first_meta_bg;
else
old_desc_blocks = fs->desc_blocks +
}
meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super);
meta_bg = i / meta_bg_size;
- if (!(fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_META_BG) ||
+ if (!ext2fs_has_feature_meta_bg(fs->super) ||
(meta_bg < fs->super->s_first_meta_bg)) {
if (has_super) {
for (j=0; j < old_desc_blocks; j++)
* supports lazy inode initialization, we can skip
* initializing the inode table.
*/
- if (lazy_itable_init &&
- EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+ if (lazy_itable_init && ext2fs_has_group_desc_csum(fs)) {
retval = 0;
goto errout;
}
/*
* 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) {
ext2fs_block_bitmap meta_bmap,
int group, blk64_t blk)
{
- ext2_filsys fs = rfs->new_fs;
+ ext2_filsys fs = rfs->new_fs;
ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
ext2fs_block_alloc_stats2(fs, blk, +1);
* or the inode tables. If not, and the block is in use, then
* mark it as a block to be moved.
*/
- if (IS_BLOCK_BM(fs, group, blk)) {
+ if (is_block_bm(fs, group, blk)) {
ext2fs_block_bitmap_loc_set(fs, group, 0);
rfs->needed_blocks++;
return;
}
- if (IS_INODE_BM(fs, group, blk)) {
+ if (is_inode_bm(fs, group, blk)) {
ext2fs_inode_bitmap_loc_set(fs, group, 0);
rfs->needed_blocks++;
return;
}
- if (IS_INODE_TB(fs, group, blk)) {
+ if (is_inode_tb(fs, group, blk)) {
ext2fs_inode_table_loc_set(fs, group, 0);
rfs->needed_blocks++;
return;
}
- if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) {
+ if (ext2fs_has_feature_flex_bg(fs->super)) {
dgrp_t i;
- for (i=0; i < rfs->old_fs->group_desc_count; i++) {
- if (IS_BLOCK_BM(fs, i, blk)) {
+ for (i = 0; i < rfs->old_fs->group_desc_count; i++) {
+ if (is_block_bm(fs, i, blk)) {
ext2fs_block_bitmap_loc_set(fs, i, 0);
rfs->needed_blocks++;
return;
}
- if (IS_INODE_BM(fs, i, blk)) {
+ if (is_inode_bm(fs, i, blk)) {
ext2fs_inode_bitmap_loc_set(fs, i, 0);
rfs->needed_blocks++;
return;
}
- if (IS_INODE_TB(fs, i, blk)) {
+ if (is_inode_tb(fs, i, blk)) {
ext2fs_inode_table_loc_set(fs, i, 0);
rfs->needed_blocks++;
return;
}
}
}
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
- (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) {
+
+ if (ext2fs_has_group_desc_csum(fs) &&
+ (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) {
/*
* If the block bitmap is uninitialized, which means
* nothing other than standard metadata in use.
*/
static errcode_t blocks_to_move(ext2_resize_t rfs)
{
- int j, has_super;
+ unsigned int j;
+ int has_super;
dgrp_t i, max_groups, g;
blk64_t blk, group_blk;
blk64_t old_blocks, new_blocks, group_end, cluster_freed;
for (blk = ext2fs_blocks_count(fs->super);
blk < ext2fs_blocks_count(old_fs->super); blk++) {
g = ext2fs_group_of_blk2(fs, blk);
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+ if (ext2fs_has_group_desc_csum(fs) &&
ext2fs_bg_flags_test(old_fs, g, EXT2_BG_BLOCK_UNINIT)) {
/*
* The block bitmap is uninitialized, so skip
ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
}
- if (old_fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ if (ext2fs_has_feature_meta_bg(old_fs->super))
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)
+ if (ext2fs_has_feature_meta_bg(fs->super))
new_blocks = fs->super->s_first_meta_bg;
else
new_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
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;
}
}
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;
}
* gets interesting....
*/
meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super);
- flex_bg = fs->super->s_feature_incompat &
- EXT4_FEATURE_INCOMPAT_FLEX_BG;
+ flex_bg = ext2fs_has_feature_flex_bg(fs->super);
/* first reserve all of the existing fs meta blocks */
for (i = 0; i < max_groups; i++) {
has_super = ext2fs_bg_has_super(fs, i);
mark_fs_metablock(rfs, meta_bmap, i, group_blk);
meta_bg = i / meta_bg_size;
- if (!(fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_META_BG) ||
+ if (!ext2fs_has_feature_meta_bg(fs->super) ||
(meta_bg < fs->super->s_first_meta_bg)) {
if (has_super) {
for (blk = group_blk+1;
}
}
-static errcode_t resize2fs_get_alloc_block(ext2_filsys fs, blk64_t goal,
+static errcode_t resize2fs_get_alloc_block(ext2_filsys fs,
+ blk64_t goal EXT2FS_ATTR((unused)),
blk64_t *ret)
{
ext2_resize_t rfs = (ext2_resize_t) fs->priv_data;
blk64_t blk;
+ int group;
blk = get_new_block(rfs);
if (!blk)
ext2fs_mark_block_bitmap2(rfs->old_fs->block_map, blk);
ext2fs_mark_block_bitmap2(rfs->new_fs->block_map, blk);
+
+ group = ext2fs_group_of_blk2(rfs->old_fs, blk);
+ ext2fs_clear_block_uninit(rfs->old_fs, group);
+ group = ext2fs_group_of_blk2(rfs->new_fs, blk);
+ ext2fs_clear_block_uninit(rfs->new_fs, group);
+
*ret = (blk64_t) blk;
return 0;
}
ext2_filsys fs = rfs->new_fs;
ext2_filsys old_fs = rfs->old_fs;
errcode_t retval;
- __u64 size;
- int c;
+ __u64 c, size;
int to_move, moved;
ext2_badblocks_list badblock_list = 0;
int bb_modified = 0;
struct process_block_struct {
ext2_resize_t rfs;
ext2_ino_t ino;
+ ext2_ino_t old_ino;
struct ext2_inode * inode;
errcode_t error;
int is_dir;
int changed;
+ int has_extents;
};
static int process_block(ext2_filsys fs, blk64_t *block_nr,
#ifdef RESIZE2FS_DEBUG
if (pb->rfs->flags & RESIZE_DEBUG_BMOVE)
printf("ino=%u, blockcnt=%lld, %llu->%llu\n",
- pb->ino, blockcnt, block, new_block);
+ pb->old_ino, blockcnt, block,
+ new_block);
#endif
block = new_block;
}
}
+
if (pb->is_dir) {
retval = ext2fs_add_dir_block2(fs->dblist, pb->ino,
block, (int) blockcnt);
return 0;
}
+static errcode_t migrate_ea_block(ext2_resize_t rfs, ext2_ino_t ino,
+ struct ext2_inode *inode, int *changed)
+{
+ char *buf = NULL;
+ blk64_t new_block;
+ errcode_t err = 0;
+
+ /* No EA block or no remapping? Quit early. */
+ if (ext2fs_file_acl_block(rfs->old_fs, inode) == 0 || !rfs->bmap)
+ return 0;
+ new_block = extent_translate(rfs->old_fs, rfs->bmap,
+ ext2fs_file_acl_block(rfs->old_fs, inode));
+ if (new_block == 0)
+ return 0;
+
+ /* Set the new ACL block */
+ ext2fs_file_acl_block_set(rfs->old_fs, inode, new_block);
+
+ /* Update checksum */
+ if (ext2fs_has_feature_metadata_csum(rfs->new_fs->super)) {
+ err = ext2fs_get_mem(rfs->old_fs->blocksize, &buf);
+ if (err)
+ return err;
+ rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ err = ext2fs_read_ext_attr3(rfs->old_fs, new_block, buf, ino);
+ rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ if (err)
+ goto out;
+ err = ext2fs_write_ext_attr3(rfs->old_fs, new_block, buf, ino);
+ if (err)
+ goto out;
+ }
+ *changed = 1;
+
+out:
+ ext2fs_free_mem(&buf);
+ 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 EXT2FS_ATTR((unused)),
+ errcode_t code EXT2FS_ATTR((unused)),
+ const char *fmt EXT2FS_ATTR((unused)),
+ va_list args EXT2FS_ATTR((unused)))
+{
+}
+
static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
{
struct process_block_struct pb;
errcode_t retval;
char *block_buf = 0;
ext2_ino_t start_to_move;
- blk64_t orig_size;
- blk64_t new_block;
int inode_size;
if ((rfs->old_fs->group_desc_count <=
!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;
pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
pb.changed = 0;
- if (ext2fs_file_acl_block(rfs->old_fs, inode) && rfs->bmap) {
- new_block = extent_translate(rfs->old_fs, rfs->bmap,
- ext2fs_file_acl_block(rfs->old_fs, inode));
- if (new_block) {
- ext2fs_file_acl_block_set(rfs->old_fs, inode,
- new_block);
- retval = ext2fs_write_inode_full(rfs->old_fs,
- ino, inode, inode_size);
- if (retval) goto errout;
- }
- }
-
- if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) &&
- (rfs->bmap || pb.is_dir)) {
- pb.ino = ino;
- retval = ext2fs_block_iterate3(rfs->old_fs,
- ino, 0, block_buf,
- process_block, &pb);
- if (retval)
- goto errout;
- if (pb.error) {
- retval = pb.error;
- goto errout;
- }
- }
+ /* Remap EA block */
+ retval = migrate_ea_block(rfs, ino, inode, &pb.changed);
+ if (retval)
+ goto errout;
+ new_inode = ino;
if (ino <= start_to_move)
- continue; /* Don't need to move it. */
+ goto remap_blocks; /* Don't need to move inode. */
/*
- * Find a new inode
+ * Find a new inode. Now that extents and directory blocks
+ * are tied to the inode number through the checksum, we must
+ * set up the new inode before we start rewriting blocks.
*/
retval = ext2fs_new_inode(rfs->new_fs, 0, 0, 0, &new_inode);
if (retval)
ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1,
pb.is_dir);
- if (pb.changed) {
- /* Get the new version of the inode */
- retval = ext2fs_read_inode_full(rfs->old_fs, ino,
- inode, inode_size);
- if (retval) goto errout;
- }
inode->i_ctime = time(0);
retval = ext2fs_write_inode_full(rfs->old_fs, new_inode,
inode, inode_size);
- if (retval) goto errout;
+ if (retval)
+ goto errout;
+ pb.changed = 0;
#ifdef RESIZE2FS_DEBUG
if (rfs->flags & RESIZE_DEBUG_INODEMAP)
goto errout;
}
ext2fs_add_extent_entry(rfs->imap, ino, new_inode);
+
+remap_blocks:
+ if (pb.changed)
+ retval = ext2fs_write_inode_full(rfs->old_fs,
+ new_inode,
+ inode, inode_size);
+ if (retval)
+ goto errout;
+
+ /* Rewrite extent block checksums with new inode number */
+ if (ext2fs_has_feature_metadata_csum(rfs->old_fs->super) &&
+ (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
+ * with new inode numbers if we have metadata_csum enabled.
+ */
+ if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) &&
+ (rfs->bmap || pb.is_dir)) {
+ pb.ino = new_inode;
+ pb.old_ino = ino;
+ pb.has_extents = inode->i_flags & EXT4_EXTENTS_FL;
+ rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ retval = ext2fs_block_iterate3(rfs->old_fs,
+ new_inode, 0, block_buf,
+ process_block, &pb);
+ rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ if (retval)
+ goto errout;
+ if (pb.error) {
+ retval = pb.error;
+ goto errout;
+ }
+ } else if ((inode->i_flags & EXT4_INLINE_DATA_FL) &&
+ (rfs->bmap || pb.is_dir)) {
+ /* inline data dir; update it too */
+ retval = ext2fs_add_dir_block2(rfs->old_fs->dblist,
+ new_inode, 0, 0);
+ if (retval)
+ goto errout;
+ }
}
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;
struct ext2_inode inode;
ext2_ino_t new_inode;
errcode_t retval;
+ int ret = 0;
if (is->rfs->progress && offset == 0) {
io_channel_flush(is->rfs->old_fs->io);
return DIRENT_ABORT;
}
+ /*
+ * If we have checksums enabled and the inode wasn't present in the
+ * old fs, then we must rewrite all dir blocks with new checksums.
+ */
+ if (ext2fs_has_feature_metadata_csum(is->rfs->old_fs->super) &&
+ !ext2fs_test_inode_bitmap2(is->rfs->old_fs->inode_map, dir))
+ ret |= DIRENT_CHANGED;
+
if (!dirent->inode)
- return 0;
+ return ret;
new_inode = ext2fs_extent_translate(is->rfs->imap, dirent->inode);
if (!new_inode)
- return 0;
+ return ret;
#ifdef RESIZE2FS_DEBUG
if (is->rfs->flags & RESIZE_DEBUG_INODEMAP)
printf("Inode translate (dir=%u, name=%.*s, %u->%u)\n",
- dir, dirent->name_len&0xFF, dirent->name,
+ dir, ext2fs_dirent_name_len(dirent), dirent->name,
dirent->inode, new_inode);
#endif
inode.i_mtime = inode.i_ctime = time(0);
is->err = ext2fs_write_inode(is->rfs->old_fs, dir, &inode);
if (is->err)
- return DIRENT_ABORT;
+ return ret | DIRENT_ABORT;
}
- return DIRENT_CHANGED;
+ return ret | DIRENT_CHANGED;
}
static errcode_t inode_ref_fix(ext2_resize_t rfs)
goto errout;
}
+ rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist,
DIRENT_FLAG_INCLUDE_EMPTY, 0,
check_and_change_inodes, &is);
+ rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval)
goto errout;
if (is.err) {
char *cp;
blk64_t old_blk, new_blk, blk, cluster_freed;
errcode_t retval;
- int j, to_move, moved;
+ int to_move, moved;
+ unsigned int j;
ext2fs_block_bitmap new_bmap = NULL;
max_groups = fs->group_desc_count;
blk64_t sb, old_desc;
blk_t num;
- if (!(fs->super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2))
+ if (!ext2fs_has_feature_sparse_super2(fs->super))
return 0;
if (last_bg <= old_last_bg)
fs->super->s_backup_bgs[1] == old_last_bg)
return 0;
+ if (old_last_bg == 0)
+ return 0;
+
retval = ext2fs_super_and_bgd_loc2(rfs->old_fs, old_last_bg,
&sb, &old_desc, NULL, &num);
if (retval)
blk_t i, num;
int realloc = 0;
- if (!(fs->super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2))
+ if (!ext2fs_has_feature_sparse_super2(fs->super))
return 0;
if (last_bg >= old_last_bg)
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);
{
struct ext2_inode inode;
errcode_t retval;
- char *block_buf = NULL;
- if (!(fs->super->s_feature_compat &
- EXT2_FEATURE_COMPAT_RESIZE_INODE))
+ if (!ext2fs_has_feature_resize_inode(fs->super))
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;
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;
}
uninit = ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT);
ext2fs_super_and_bgd_loc2(fs, group, &super_blk, &old_desc_blk,
&new_desc_blk, 0);
- if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ if (ext2fs_has_feature_meta_bg(fs->super))
old_desc_blocks = fs->super->s_first_meta_bg;
else
old_desc_blocks = fs->desc_blocks +
ext2fs_super_and_bgd_loc2(fs, group, &super_blk,
&old_desc_blk,
&new_desc_blk, 0);
- if (fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_META_BG)
+ if (ext2fs_has_feature_meta_bg(fs->super))
old_desc_blocks = fs->super->s_first_meta_bg;
else
old_desc_blocks = fs->desc_blocks +
errcode_t retval;
struct ext2_inode inode;
- if (!(fs->super->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+ if (!ext2fs_has_feature_journal(fs->super))
return 0;
/* External journal? Nothing to do. */
/*
* number of old-style block group descriptor blocks
*/
- if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ if (ext2fs_has_feature_meta_bg(fs->super))
old_desc_blocks = fs->super->s_first_meta_bg;
else
old_desc_blocks = fs->desc_blocks +
* guaranteed to finish.
*/
flex_groups = groups;
- if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) {
+ if (ext2fs_has_feature_flex_bg(fs->super)) {
dgrp_t remainder = groups & (flexbg_size - 1);
flex_groups += flexbg_size - remainder;
grp = flex_groups;
groups += extra_grps;
- if (!(fs->super->s_feature_incompat &
- EXT4_FEATURE_INCOMPAT_FLEX_BG))
+ if (!ext2fs_has_feature_flex_bg(fs->super))
flex_groups = groups;
else if (groups > flex_groups) {
dgrp_t r = groups & (flexbg_size - 1);
/* now for the fun voodoo */
grp = groups - 1;
- if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) &&
+ if (ext2fs_has_feature_flex_bg(fs->super) &&
(grp & ~(flexbg_size - 1)) == 0)
grp = grp & ~(flexbg_size - 1);
overhead = 0;
* with each data block needs to be in its own extent, and
* with each inode needing at least one extent block.
*/
- if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) {
+ if (ext2fs_has_feature_extents(fs->super)) {
blk64_t safe_margin = (ext2fs_blocks_count(fs->super) -
blks_needed)/500;
unsigned int exts_per_blk = (fs->blocksize /