Fix for the case when inode block count doesn't change;

Warn about inode number rounding;
Respect unused_inodes during inode table moving.
master
Vitaliy Filippov 2014-01-05 19:20:44 +00:00
parent 9dfbea8e54
commit 7f03acc619
1 changed files with 95 additions and 68 deletions

View File

@ -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))