From 7f03acc6196f77daec73a0ada56d3e16991e2aa8 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sun, 5 Jan 2014 19:20:44 +0000 Subject: [PATCH] Fix for the case when inode block count doesn't change; Warn about inode number rounding; Respect unused_inodes during inode table moving. --- realloc-inodes.c | 163 +++++++++++++++++++++++++++-------------------- 1 file changed, 95 insertions(+), 68 deletions(-) diff --git a/realloc-inodes.c b/realloc-inodes.c index 5cb9854..1816bc2 100644 --- a/realloc-inodes.c +++ b/realloc-inodes.c @@ -234,9 +234,9 @@ int shrink_move_inodes(realloc_data *rd) } for (group = 0; group < rd->fs->group_desc_count; group++) { - for (i = rd->ig_new; i < EXT2_INODES_PER_GROUP(rd->fs->super); i++) + for (i = rd->ig_new; i < rd->ig_old; i++) { - ino = 1 + group*EXT2_INODES_PER_GROUP(rd->fs->super) + i; + ino = 1 + group*rd->ig_old + i; if (ext2fs_test_inode_bitmap2(rd->fs->inode_map, ino)) { // Inode is occupied and should be moved @@ -244,8 +244,8 @@ int shrink_move_inodes(realloc_data *rd) do { retval = ext2fs_find_first_zero_inode_bitmap2(rd->fs->inode_map, - 1 + new_group*EXT2_INODES_PER_GROUP(rd->fs->super), - new_group*EXT2_INODES_PER_GROUP(rd->fs->super)+rd->ig_new, &new_ino); + 1 + new_group*rd->ig_old, + new_group*rd->ig_old+rd->ig_new, &new_ino); if (!retval) { break; @@ -296,7 +296,7 @@ int extend_move_blocks(realloc_data *rd) { ext2fs_block_bitmap reserve_map; blk64_t it_start, blk_diff, b_per_g; - dgrp_t n_flex, n_grp, flex_count; + dgrp_t flex_grp, n_grp, flex_count; int retval, flexbg_size; if (rd->ibg_new == rd->ibg_old) { @@ -324,15 +324,15 @@ int extend_move_blocks(realloc_data *rd) flexbg_size = 1; } flex_count = (rd->fs->group_desc_count + flexbg_size - 1) / flexbg_size; - for (n_flex = 0; n_flex < flex_count; n_flex++) + for (flex_grp = 0; flex_grp < flex_count; flex_grp++) { n_grp = flexbg_size; - if (n_flex*flexbg_size+n_grp > rd->fs->group_desc_count) + if (flex_grp*flexbg_size+n_grp > rd->fs->group_desc_count) { - n_grp = rd->fs->group_desc_count-n_flex*flexbg_size; + n_grp = rd->fs->group_desc_count-flex_grp*flexbg_size; } - it_start = ext2fs_inode_table_loc(rd->fs, n_flex*flexbg_size); - // Check group boundaries + it_start = ext2fs_inode_table_loc(rd->fs, flex_grp*flexbg_size); + // Check group boundaries (just in case) if ((it_start + rd->ibg_new*n_grp - 1) / b_per_g != (it_start + rd->ibg_old*n_grp - 1) / b_per_g) { @@ -360,8 +360,8 @@ static int change_inode_numbers_callback(ext2_ino_t dir, int entry, { new_ino = dirent->inode; } - new_ino = 1 + (new_ino-1)/EXT2_INODES_PER_GROUP(rd->fs->super)*rd->ig_new + - (new_ino-1)%EXT2_INODES_PER_GROUP(rd->fs->super); + new_ino = 1 + (new_ino-1)/rd->ig_old*rd->ig_new + + (new_ino-1)%rd->ig_old; if (new_ino != dirent->inode) { dirent->inode = new_ino; @@ -393,47 +393,62 @@ int change_super_and_bgd(realloc_data *rd) { blk64_t it_start, blk; dgrp_t grp, flex_grp, flex_count; - __u32 unus; + __u32 unus, used_ibg; int flexbg_size, n_grp, i, retval = 0; + int has_gdt_csum = EXT2_HAS_RO_COMPAT_FEATURE(rd->fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM); void *buf = NULL; ext2fs_flush(rd->fs); if (!rd->fs->block_map) { ext2fs_read_block_bitmap(rd->fs); } - if (rd->ibg_new != rd->ibg_old) + if (EXT2_HAS_INCOMPAT_FEATURE(rd->fs->super, EXT4_FEATURE_INCOMPAT_FLEX_BG) + && rd->fs->super->s_log_groups_per_flex) { - if (EXT2_HAS_INCOMPAT_FEATURE(rd->fs->super, EXT4_FEATURE_INCOMPAT_FLEX_BG) - && rd->fs->super->s_log_groups_per_flex) + flexbg_size = 1 << rd->fs->super->s_log_groups_per_flex; + } + else + { + flexbg_size = 1; + } + flex_count = (rd->fs->group_desc_count + flexbg_size - 1) / flexbg_size; + retval = ext2fs_get_mem(EXT2_BLOCK_SIZE(rd->fs->super) * rd->ibg_new * flexbg_size, &buf); + if (retval) + { + goto out; + } + for (flex_grp = 0; flex_grp < flex_count; flex_grp++) + { + n_grp = flexbg_size; + if (flex_grp*flexbg_size+n_grp > rd->fs->group_desc_count) { - flexbg_size = 1 << rd->fs->super->s_log_groups_per_flex; + n_grp = rd->fs->group_desc_count-flex_grp*flexbg_size; } - else + it_start = ext2fs_inode_table_loc(rd->fs, flex_grp*flexbg_size); + if (rd->ibg_new != rd->ibg_old) { - flexbg_size = 1; - } - flex_count = (rd->fs->group_desc_count + flexbg_size - 1) / flexbg_size; - retval = ext2fs_get_mem(EXT2_BLOCK_SIZE(rd->fs->super) * rd->ibg_new * flexbg_size, &buf); - if (retval) - { - goto out; - } - for (flex_grp = 0; flex_grp < flex_count; flex_grp++) - { - memset(buf, 0, EXT2_BLOCK_SIZE(rd->fs->super) * rd->ibg_new * flexbg_size); - n_grp = flexbg_size; - if (flex_grp*flexbg_size+n_grp > rd->fs->group_desc_count) - { - n_grp = rd->fs->group_desc_count-flex_grp*flexbg_size; - } - // Read inode table(s) + memset(buf, 0, EXT2_BLOCK_SIZE(rd->fs->super) * rd->ibg_new * n_grp); + // Read inode table(s) while skipping unitialized inode table parts for (grp = flex_grp*flexbg_size, i = 0; i < n_grp; grp++, i++) { - if (!ext2fs_bg_flags_test(rd->fs, grp, EXT2_BG_INODE_UNINIT)) + used_ibg = rd->ibg_old; + if (has_gdt_csum) + { + if (ext2fs_bg_flags_test(rd->fs, grp, EXT2_BG_INODE_UNINIT)) + { + used_ibg = 0; + } + else + { + used_ibg = (rd->ig_old - ext2fs_bg_itable_unused(rd->fs, grp)); + used_ibg = (used_ibg * EXT2_INODE_SIZE(rd->fs->super)+EXT2_BLOCK_SIZE(rd->fs->super)-1)/EXT2_BLOCK_SIZE(rd->fs->super); + } + } + if (used_ibg > 0) { blk = ext2fs_inode_table_loc(rd->fs, grp); retval = io_channel_read_blk64(rd->fs->io, blk, - min(rd->ibg_old, rd->ibg_new), + min(used_ibg, rd->ibg_new), buf + i*rd->ibg_new*EXT2_BLOCK_SIZE(rd->fs->super)); if (retval) { @@ -442,9 +457,7 @@ int change_super_and_bgd(realloc_data *rd) } } // Write inode table(s) to the new place - it_start = ext2fs_inode_table_loc(rd->fs, flex_grp*flexbg_size); - blk = rd->ibg_new * n_grp; - retval = io_channel_write_blk64(rd->fs->io, it_start, blk, buf); + retval = io_channel_write_blk64(rd->fs->io, it_start, rd->ibg_new * n_grp, buf); if (retval) { // Exiting with badly corrupted filesystem :-( @@ -454,43 +467,43 @@ int change_super_and_bgd(realloc_data *rd) // Mark/unmark extra inode table blocks if (rd->ibg_new < rd->ibg_old) { - ext2fs_unmark_block_bitmap_range2(rd->fs->block_map, it_start+blk, + ext2fs_unmark_block_bitmap_range2(rd->fs->block_map, it_start + rd->ibg_new*n_grp, (rd->ibg_old-rd->ibg_new)*n_grp); } else { - ext2fs_mark_block_bitmap_range2(rd->fs->block_map, it_start+rd->ibg_old*n_grp, + ext2fs_mark_block_bitmap_range2(rd->fs->block_map, it_start + rd->ibg_old*n_grp, (rd->ibg_new-rd->ibg_old)*n_grp); } - ext2fs_bg_free_blocks_count_set(rd->fs, flex_grp*flexbg_size, - ext2fs_bg_free_blocks_count(rd->fs, flex_grp*flexbg_size) - - (rd->ibg_new - rd->ibg_old)*flexbg_size); - // Change inode table locations and free inode counts - for (grp = flex_grp*flexbg_size, i = 0; i < n_grp; grp++, i++) + } + ext2fs_bg_free_blocks_count_set(rd->fs, flex_grp*flexbg_size, + ext2fs_bg_free_blocks_count(rd->fs, flex_grp*flexbg_size) - + (rd->ibg_new - rd->ibg_old)*n_grp); + // Change inode table locations and free inode counts + for (grp = flex_grp*flexbg_size, i = 0; i < n_grp; grp++, i++) + { + blk = it_start + rd->ibg_new*i; + ext2fs_inode_table_loc_set(rd->fs, grp, blk); + ext2fs_bg_free_inodes_count_set(rd->fs, grp, + ext2fs_bg_free_inodes_count(rd->fs, grp) + rd->ig_new - rd->ig_old); + if (has_gdt_csum) { - blk = it_start + rd->ibg_new*i; - ext2fs_inode_table_loc_set(rd->fs, grp, blk); - ext2fs_bg_free_inodes_count_set(rd->fs, grp, - ext2fs_bg_free_inodes_count(rd->fs, grp) + rd->ig_new - rd->ig_old); - if (EXT2_HAS_RO_COMPAT_FEATURE(rd->fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + unus = ext2fs_bg_itable_unused(rd->fs, grp); + if (rd->ig_new > rd->ig_old || unus >= rd->ig_old - rd->ig_new) { - unus = ext2fs_bg_itable_unused(rd->fs, grp); - if (rd->ig_new > rd->ig_old || unus >= rd->ig_old - rd->ig_new) - { - unus += rd->ig_new - rd->ig_old; - } - else - { - unus = 0; - } - ext2fs_bg_itable_unused_set(rd->fs, grp, unus); - ext2fs_bg_flags_clear(rd->fs, grp, EXT2_BG_BLOCK_UNINIT); - ext2fs_group_desc_csum_set(rd->fs, grp); + unus += rd->ig_new - rd->ig_old; } + else + { + unus = 0; + } + ext2fs_bg_itable_unused_set(rd->fs, grp, unus); + ext2fs_bg_flags_clear(rd->fs, grp, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(rd->fs, grp); } } } - // Bitmaps don't need to be moved because a single bitmap is always a single FS block + // Bitmaps never need to be moved because a single bitmap is always a single FS block ext2fs_mark_bb_dirty(rd->fs); retval = rd->fs->write_bitmaps(rd->fs); if (retval) @@ -501,7 +514,7 @@ int change_super_and_bgd(realloc_data *rd) // Explicitly set 'overwrite backup superblocks' flag rd->fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; ext2fs_free_blocks_count_add(rd->fs->super, rd->fs->group_desc_count * (rd->ibg_old - rd->ibg_new)); - rd->fs->super->s_free_inodes_count += rd->fs->group_desc_count * (rd->ig_new - EXT2_INODES_PER_GROUP(rd->fs->super)); + rd->fs->super->s_free_inodes_count += rd->fs->group_desc_count * (rd->ig_new - rd->ig_old); rd->fs->super->s_inodes_per_group = rd->ig_new; rd->fs->super->s_inodes_count = rd->fs->group_desc_count * rd->ig_new; ext2fs_mark_super_dirty(rd->fs); @@ -532,9 +545,11 @@ out: */ int do_realloc(realloc_data *rd) { + __u32 ig_round; int retval; rd->ig_old = EXT2_INODES_PER_GROUP(rd->fs->super); rd->ig_new = rd->new_inode_count / rd->fs->group_desc_count; + // inodes-per-group must be a multiple of 8 so each byte of inode bitmap is filled rd->ig_new &= ~7; if (rd->ig_new < 16) { @@ -544,8 +559,20 @@ int do_realloc(realloc_data *rd) rd->ibg_old = rd->fs->inode_blocks_per_group; rd->ibg_new = (rd->ig_new * EXT2_INODE_SIZE(rd->fs->super) + EXT2_BLOCK_SIZE(rd->fs->super) - 1) / EXT2_BLOCK_SIZE(rd->fs->super); + if (rd->new_inode_count != rd->ig_new * rd->fs->group_desc_count) + { + printf("Inode count %u rounded down to %u = (%u inodes per group) * (%u block groups)\n", + rd->new_inode_count, rd->ig_new * rd->fs->group_desc_count, rd->ig_new, rd->fs->group_desc_count); + } rd->new_inode_count = rd->ig_new * rd->fs->group_desc_count; - if (rd->ig_new < EXT2_INODES_PER_GROUP(rd->fs->super)) + ig_round = rd->ibg_new * EXT2_BLOCK_SIZE(rd->fs->super) / EXT2_INODE_SIZE(rd->fs->super); + if (rd->ig_new != ig_round) + { + printf("Inode count %u is not optimal because %u inodes per group is not a multiple of %u" + " - there will be wasted space in inode tables. Optimal inode count would be %u.\n", + rd->new_inode_count, rd->ig_new, EXT2_BLOCK_SIZE(rd->fs->super) / EXT2_INODE_SIZE(rd->fs->super), ig_round); + } + if (rd->ig_new < rd->ig_old) { if (rd->new_inode_count < rd->fs->super->s_inodes_count - rd->fs->super->s_free_inodes_count) { @@ -560,7 +587,7 @@ int do_realloc(realloc_data *rd) return retval; } } - else if (rd->ig_new > EXT2_INODES_PER_GROUP(rd->fs->super)) + else if (rd->ig_new > rd->ig_old) { blk64_t required_blocks = (rd->ibg_new - rd->ibg_old) * rd->fs->group_desc_count; if (required_blocks > ext2fs_free_blocks_count(rd->fs->super))