Remap blocks before moving inode tables (fixes big flex_bg extend),

check blocks to move even when shrinking inode tables (fixes big flex_bg shrink),
mark inode tables during allocation (fixes bigalloc shrink)
master
Vitaliy Filippov 2016-09-25 20:22:30 +03:00
parent 152890aa45
commit f58c260dc4
1 changed files with 67 additions and 30 deletions

View File

@ -367,9 +367,11 @@ static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size)
* 4) block_mover(): move blocks out of the way as usual
* 5) inodes_to_move(): when shrinking inode tables, move inodes from the end
* of each group's inode table (still operating on old fs inode tables).
* rewrite blocks used by inodes.
* it would be possible to rewrite blocks on step 7, but we need to read
* extents, and they may be overwritten by inode tables on step 6.
* so, rewrite blocks used by inodes here!
* 6) move_inode_tables(): move inode tables
* 7) inode_scan_and_fix(): rewrite all extent checksums, extattr and ACL checksums
* 7) inode_scan_and_fix(): rewrite all inode, extent, extattr and ACL checksums
* 8) inode_ref_fix(): translate inode numbers (operating on new fs inode tables)
*/
static int set_inode_count(ext2_filsys fs, __u32 new_inodes)
@ -1366,17 +1368,17 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
if (retval) {
return retval;
}
// If we're extending inode tables, we need to move some blocks away
if (rfs->old_fs->inode_blocks_per_group < fs->inode_blocks_per_group) {
group_blk = ext2fs_inode_table_loc(fs, g)+fs->inode_blocks_per_group;
for (blk = ext2fs_inode_table_loc(fs, g); blk < group_blk; blk++) {
if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
!ext2fs_test_block_bitmap2(meta_bmap, blk)) {
ext2fs_mark_block_bitmap2(rfs->move_blocks, blk);
rfs->needed_blocks++;
}
ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
ext2fs_mark_block_bitmap_range2(empty_bmap,
ext2fs_inode_table_loc(fs, g), fs->inode_blocks_per_group);
// We may need to move some blocks away (mainly if extending inode tables)
group_blk = ext2fs_inode_table_loc(fs, g)+fs->inode_blocks_per_group;
for (blk = ext2fs_inode_table_loc(fs, g); blk < group_blk; blk++) {
if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
!ext2fs_test_block_bitmap2(meta_bmap, blk)) {
ext2fs_mark_block_bitmap2(rfs->move_blocks, blk);
rfs->needed_blocks++;
}
ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
}
}
ext2fs_free_block_bitmap(empty_bmap);
@ -1941,7 +1943,7 @@ static int process_block(ext2_filsys fs, blk64_t *block_nr,
}
}
if (pb->is_dir) {
if (pb->is_dir && fs->dblist) {
retval = ext2fs_add_dir_block2(fs->dblist, pb->ino,
block, (int) blockcnt);
if (retval) {
@ -2084,18 +2086,22 @@ static void quiet_com_err_proc(const char *whoami EXT2FS_ATTR((unused)),
static errcode_t inodes_to_move(ext2_resize_t rfs)
{
struct process_block_struct pb;
ext2_ino_t ino, new_inode, tr_ino;
struct ext2_inode *inode = NULL;
ext2_inode_scan scan = NULL;
errcode_t retval;
dgrp_t g, old_g;
char *block_buf = 0;
ext2_ino_t start_to_move;
int inode_size;
int shrink_inodes = rfs->old_fs->inode_blocks_per_group > rfs->new_fs->inode_blocks_per_group;
int change_inodes = rfs->old_fs->inode_blocks_per_group != rfs->new_fs->inode_blocks_per_group;
if ((rfs->old_fs->group_desc_count <=
rfs->new_fs->group_desc_count) &&
!shrink_inodes)
!rfs->bmap &&
!change_inodes)
return 0;
retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan);
@ -2115,12 +2121,18 @@ static errcode_t inodes_to_move(ext2_resize_t rfs)
}
}
retval = ext2fs_get_array(rfs->new_fs->blocksize, 3, &block_buf);
if (retval) goto errout;
inode_size = EXT2_INODE_SIZE(rfs->new_fs->super);
inode = malloc(inode_size);
if (!inode) {
retval = ENOMEM;
goto errout;
}
pb.rfs = rfs;
pb.inode = inode;
pb.error = 0;
/*
* First, copy all of the inodes that need to be moved
@ -2179,12 +2191,43 @@ static errcode_t inodes_to_move(ext2_resize_t rfs)
goto errout;
}
ext2fs_add_extent_entry(rfs->imap, ino, new_inode);
// continue with rewritten inode number
ino = new_inode;
}
/*
* Update inodes to point to new blocks.
*/
pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) && rfs->bmap) {
pb.changed = 0;
pb.ino = ino;
pb.old_ino = ino; // FIXME debug output will show translated inodes
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,
ino, 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;
}
}
}
errout:
if (rfs->bmap) {
ext2fs_free_extent_table(rfs->bmap);
rfs->bmap = 0;
}
if (scan)
ext2fs_close_inode_scan(scan);
if (block_buf)
ext2fs_free_mem(&block_buf);
free(inode);
return retval;
}
@ -2198,13 +2241,11 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
errcode_t retval;
dgrp_t g;
char *block_buf = 0;
ext2_ino_t start_to_move;
int inode_size;
int change_inodes = rfs->old_fs->inode_blocks_per_group != rfs->new_fs->inode_blocks_per_group;
if ((rfs->old_fs->group_desc_count <=
rfs->new_fs->group_desc_count) &&
!rfs->bmap &&
!change_inodes)
return 0;
@ -2225,15 +2266,15 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
goto errout;
}
ext2fs_set_inode_callback(scan, progress_callback, (void *) rfs);
pb.rfs = rfs;
pb.inode = inode;
pb.error = 0;
inode_size = EXT2_INODE_SIZE(rfs->new_fs->super);
inode = malloc(inode_size);
if (!inode) {
retval = ENOMEM;
goto errout;
}
pb.rfs = rfs;
pb.inode = inode;
pb.error = 0;
/*
* First, copy all of the inodes that need to be moved
* elsewhere in the inode table
@ -2276,12 +2317,10 @@ remap_blocks:
}
/*
* Update inodes to point to new blocks; schedule directory
* blocks for inode remapping. Need to write out dir 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->new_fs, inode) &&
(rfs->bmap || pb.is_dir)) {
if (ext2fs_inode_has_valid_blocks2(rfs->new_fs, inode) && pb.is_dir) {
pb.ino = ino;
pb.old_ino = ino; // FIXME: Debug output will show translated inodes
pb.has_extents = inode->i_flags & EXT4_EXTENTS_FL;
@ -2296,8 +2335,7 @@ remap_blocks:
retval = pb.error;
goto errout;
}
} else if ((inode->i_flags & EXT4_INLINE_DATA_FL) &&
(rfs->bmap || pb.is_dir)) {
} else if ((inode->i_flags & EXT4_INLINE_DATA_FL) && pb.is_dir) {
/* inline data dir; update it too */
retval = ext2fs_add_dir_block2(rfs->new_fs->dblist,
tr_ino, 0, 0);
@ -2309,10 +2347,6 @@ remap_blocks:
errout:
reset_com_err_hook();
if (rfs->bmap) {
ext2fs_free_extent_table(rfs->bmap);
rfs->bmap = 0;
}
if (scan)
ext2fs_close_inode_scan(scan);
if (block_buf)
@ -2360,10 +2394,13 @@ static errcode_t move_inode_tables(ext2_resize_t rfs)
ext2fs_bg_free_inodes_count(rfs->new_fs, g) + new_ipg - old_ipg);
ext2fs_group_desc_csum_set(rfs->new_fs, g);
}
printf("g%u = %u->%u free=%u unused=%u\n", g,
printf("g%u = [%u?] %u->%u free=%u->%u unused=%u->%u\n", g,
g > 0 ? ext2fs_inode_table_loc(rfs->old_fs, g-1)+old_ibg : 0,
ext2fs_inode_table_loc(rfs->old_fs, g),
ext2fs_inode_table_loc(rfs->new_fs, g),
ext2fs_bg_free_inodes_count(rfs->old_fs, g),
ext2fs_bg_free_inodes_count(rfs->new_fs, g),
ext2fs_bg_itable_unused(rfs->old_fs, g),
ext2fs_bg_itable_unused(rfs->new_fs, g));
if ((change_inodes || ext2fs_inode_table_loc(rfs->new_fs, g) != ext2fs_inode_table_loc(rfs->old_fs, g)) &&
(!ext2fs_has_group_desc_csum(rfs->new_fs) ||