OK, realloc now works with inode moving!
parent
ee80425d93
commit
8bb97a550a
149
realloc-inodes.c
149
realloc-inodes.c
|
@ -1,28 +1,28 @@
|
|||
/**
|
||||
* An attempt to write a tool for ext4 that will allow to change inode count of a filesystem.
|
||||
* A tool for ext2/ext3/ext4 filesystems that allows to change inode count
|
||||
* without recreating it.
|
||||
*
|
||||
* In theory it shouldn't be that hard:
|
||||
* 1) If shrinking:
|
||||
* 1.1) mark the end of each inode table as reserved (to make sure the inode allocator won't allocate it)
|
||||
* 1.2) if there were some inodes, move them away:
|
||||
* 1.2.1) allocate a new place for each inode, copy it there
|
||||
* 1.2.2) remember the old->new inode number mapping
|
||||
* 1.3) mark some blocks that belonged to inode table as free
|
||||
* 2) If growing:
|
||||
* 2.1) check all extra blocks that will be occupied by the growing inode tables,
|
||||
* mark them as occupied if there are free ()
|
||||
* 2.2) move data away from 'extra blocks' that were occupied
|
||||
* 3) Change all inode numbers in directory entries so that
|
||||
* new_num = (old_num/old_inodes_per_group)*new_inodes_per_group + (old_num%inodes_per_group)
|
||||
* also translate old->new inode numbers remembered at (1.2.2)
|
||||
* 4) Change superblock: s_inodes_count, s_free_inodes_count, s_inodes_per_group
|
||||
* 5) Change block group descriptors: bg_inode_table_(lo,hi), bg_free_inodes_count_(lo,hi),
|
||||
* bg_inode_bitmap_csum_(lo,hi), bg_itable_unused_(lo,hi)
|
||||
* 6) If flex_bg is enabled, move parts of old inode tables so they are consecutive again
|
||||
* The theory isn't that hard:
|
||||
* 1) If shrinking - move inodes away from the end of each block group inode table
|
||||
* 1.1) move each inode to the new place, mark new place as occupied, unmark old one
|
||||
* 1.2) remember the old->new inode number mapping
|
||||
* 2) If growing - move data away from extra blocks needed by growing inode tables
|
||||
* 2.1) First check if some extra blocks are occupied by the bad block inode.
|
||||
* If yes, we should either abort the reallocation process, or move inode
|
||||
* tables to another location in a block group, possibly first defragmenting it.
|
||||
* 2.1) Copy data somewhere, remember block number mapping, mark/unmark new/old.
|
||||
* It involves overwriting the whole file extent tree or block mapping...
|
||||
* 3) Change all inode numbers in directory entries according to mappings from (1.2),
|
||||
* and then so new_num = (old_num/old_i_per_g)*new_i_per_g + (old_num % i_per_g)
|
||||
* 4) Move parts of inode tables so they are consecutive again if flex_bg feature is active
|
||||
* 5) Mark/unmark extra blocks for inode tables
|
||||
* 6) Change block group descriptors: bg_inode_table, bg_free_inodes_count,
|
||||
* bg_free_blocks_count, bg_inode_bitmap_csum, bg_itable_unused
|
||||
* 7) Change superblock: s_inodes_count, s_free_blocks_count,
|
||||
* s_free_inodes_count, s_inodes_per_group
|
||||
*
|
||||
* This is a destructive process involving metadata change so it would be good if
|
||||
* we could first write a file containing all necessary changes, then backup all
|
||||
* changed blocks, and then write new blocks to the disk. (maybe just use undo_io_manager?)
|
||||
* This is a highly destructive process and WILL leave a corrupted FS if interrupted.
|
||||
* So we should provide a rollback method... maybe use undo_io_manager?
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
@ -108,9 +108,90 @@ ext2_ino_t realloc_search_inode_map(realloc_data *rd, ext2_ino_t old)
|
|||
return rd->inode_map[(cur<<1)+1];
|
||||
}
|
||||
}
|
||||
if (rd->inode_map[start<<1] == old)
|
||||
{
|
||||
return rd->inode_map[(start<<1)+1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy-paste from e2fsprogs: lib/ext2fs/alloc.c
|
||||
|
||||
/*
|
||||
* Check for uninit block bitmaps and deal with them appropriately
|
||||
*/
|
||||
static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map,
|
||||
dgrp_t group)
|
||||
{
|
||||
blk_t i;
|
||||
blk64_t blk, super_blk, old_desc_blk, new_desc_blk;
|
||||
int old_desc_blocks;
|
||||
|
||||
if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
|
||||
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
|
||||
!(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)))
|
||||
return;
|
||||
|
||||
blk = (group * fs->super->s_blocks_per_group) +
|
||||
fs->super->s_first_data_block;
|
||||
|
||||
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)
|
||||
old_desc_blocks = fs->super->s_first_meta_bg;
|
||||
else
|
||||
old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
|
||||
|
||||
for (i=0; i < fs->super->s_blocks_per_group; i++, blk++)
|
||||
ext2fs_fast_unmark_block_bitmap2(map, blk);
|
||||
|
||||
blk = (group * fs->super->s_blocks_per_group) +
|
||||
fs->super->s_first_data_block;
|
||||
for (i=0; i < fs->super->s_blocks_per_group; i++, blk++) {
|
||||
if ((blk == super_blk) ||
|
||||
(old_desc_blk && old_desc_blocks &&
|
||||
(blk >= old_desc_blk) &&
|
||||
(blk < old_desc_blk + old_desc_blocks)) ||
|
||||
(new_desc_blk && (blk == new_desc_blk)) ||
|
||||
(blk == ext2fs_block_bitmap_loc(fs, group)) ||
|
||||
(blk == ext2fs_inode_bitmap_loc(fs, group)) ||
|
||||
(blk >= ext2fs_inode_table_loc(fs, group) &&
|
||||
(blk < ext2fs_inode_table_loc(fs, group)
|
||||
+ fs->inode_blocks_per_group)))
|
||||
ext2fs_fast_mark_block_bitmap2(map, blk);
|
||||
}
|
||||
ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
|
||||
ext2fs_group_desc_csum_set(fs, group);
|
||||
ext2fs_mark_super_dirty(fs);
|
||||
ext2fs_mark_bb_dirty(fs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for uninit inode bitmaps and deal with them appropriately
|
||||
*/
|
||||
static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map,
|
||||
dgrp_t group)
|
||||
{
|
||||
ext2_ino_t i, ino;
|
||||
|
||||
if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
|
||||
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
|
||||
!(ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)))
|
||||
return;
|
||||
|
||||
ino = (group * fs->super->s_inodes_per_group) + 1;
|
||||
for (i=0; i < fs->super->s_inodes_per_group; i++, ino++)
|
||||
ext2fs_fast_unmark_inode_bitmap2(map, ino);
|
||||
|
||||
ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT);
|
||||
ext2fs_group_desc_csum_set(fs, group);
|
||||
ext2fs_mark_ib_dirty(fs);
|
||||
ext2fs_mark_super_dirty(fs);
|
||||
check_block_uninit(fs, fs->block_map, group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move inodes from the end of each block group inode table
|
||||
* so the tables can be shrinked
|
||||
|
@ -145,7 +226,7 @@ int shrink_move_inodes(realloc_data *rd)
|
|||
{
|
||||
retval = ext2fs_find_first_zero_inode_bitmap2(rd->fs->inode_map,
|
||||
1 + new_group*EXT2_INODES_PER_GROUP(rd->fs->super),
|
||||
1 + new_group*EXT2_INODES_PER_GROUP(rd->fs->super)+rd->new_inodes_per_group, &new_ino);
|
||||
new_group*EXT2_INODES_PER_GROUP(rd->fs->super)+rd->new_inodes_per_group, &new_ino);
|
||||
if (!retval)
|
||||
{
|
||||
break;
|
||||
|
@ -158,6 +239,7 @@ int shrink_move_inodes(realloc_data *rd)
|
|||
goto out;
|
||||
}
|
||||
// Copy inode to the new place
|
||||
check_inode_uninit(rd->fs, rd->fs->inode_map, new_group);
|
||||
retval = ext2fs_read_inode_full(rd->fs, ino, inode, inode_size);
|
||||
if (retval)
|
||||
{
|
||||
|
@ -168,17 +250,23 @@ int shrink_move_inodes(realloc_data *rd)
|
|||
{
|
||||
goto out;
|
||||
}
|
||||
retval = ext2fs_mark_inode_bitmap2(rd->fs->inode_map, new_ino);
|
||||
if (retval)
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
ext2fs_mark_ib_dirty(rd->fs);
|
||||
ext2fs_mark_inode_bitmap2(rd->fs->inode_map, new_ino);
|
||||
ext2fs_bg_free_inodes_count_set(rd->fs, group, ext2fs_bg_free_inodes_count(rd->fs, group) + 1);
|
||||
ext2fs_bg_free_inodes_count_set(rd->fs, new_group, ext2fs_bg_free_inodes_count(rd->fs, new_group) - 1);
|
||||
// Remember mapping
|
||||
realloc_add_inode_map(rd, ino, new_ino);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < rd->inode_map_size; i++)
|
||||
{
|
||||
ino = rd->inode_map[i<<1];
|
||||
ext2fs_unmark_inode_bitmap2(rd->fs->inode_map, ino);
|
||||
}
|
||||
if (rd->inode_map_size)
|
||||
{
|
||||
ext2fs_mark_ib_dirty(rd->fs);
|
||||
}
|
||||
out:
|
||||
if (inode)
|
||||
{
|
||||
|
@ -241,7 +329,7 @@ int change_super_and_bgd(realloc_data *rd)
|
|||
blk64_t it_start, blk;
|
||||
dgrp_t grp, n_flex, n_grp;
|
||||
__u32 unus;
|
||||
int i_per_g_diff = 0;
|
||||
__u32 i_per_g_diff = rd->new_inodes_per_group - EXT2_INODES_PER_GROUP(rd->fs->super);
|
||||
int flexbg_size = 0, i, retval = 0;
|
||||
void *buf = NULL;
|
||||
ext2fs_flush(rd->fs);
|
||||
|
@ -258,7 +346,6 @@ int change_super_and_bgd(realloc_data *rd)
|
|||
{
|
||||
goto out;
|
||||
}
|
||||
i_per_g_diff = rd->new_inodes_per_group - EXT2_INODES_PER_GROUP(rd->fs->super);
|
||||
for (n_flex = 0; n_flex < rd->fs->group_desc_count/flexbg_size; n_flex++)
|
||||
{
|
||||
n_grp = flexbg_size;
|
||||
|
|
Loading…
Reference in New Issue