Inode table growing now works on a basic test case!

master
Vitaliy Filippov 2014-01-04 20:29:42 +00:00
parent 9f543c9ca1
commit 36ac4a8330
4 changed files with 92 additions and 20 deletions

View File

@ -1,3 +1,3 @@
all: realloc-inodes
realloc-inodes: realloc-inodes.c bmove.c ext2fsP.h
gcc -Wall -o realloc-inodes -lcom_err -lext2fs realloc-inodes.c bmove.c
gcc -Wsign-compare -Wall -o realloc-inodes -lcom_err -lext2fs realloc-inodes.c bmove.c

View File

@ -75,6 +75,7 @@ static int process_block(ext2_filsys fs, blk64_t *block_nr,
}
*block_nr = block;
ext2fs_mark_block_bitmap2(pb->alloc_map, block);
ext2fs_unmark_block_bitmap2(pb->alloc_map, orig);
ret = BLOCK_CHANGED;
if (pb->flags & EXT2_BMOVE_DEBUG)
printf("ino=%u, blockcnt=%lld, %llu->%llu\n",

22
mkimage.sh Normal file
View File

@ -0,0 +1,22 @@
#!/bin/sh
# To test shrinking: INODES=8192, then ./realloc-inodes test-clean.img 2048
# To test growing: INODES=2048, then ./realloc-inodes test-clean.img 8192
INODES=2048
dd if=/dev/zero of=test-clean.img bs=1k seek=32767 count=1
/sbin/mkfs.ext4 -F -b 1024 -m 0 -g 2048 -N $INODES test-clean.img
mkdir -p dir
mount -o loop test-clean.img dir
# For block moving test: create a sparse file with many extents
dd if=/dev/urandom of=dir/f_random bs=1k count=1
for i in {1..1200}; do
dd if=dir/f_random of=dir/f_sparse bs=1k count=1 seek=$((2400-i*2)) conv=notrunc 2>/dev/null
done
# For inode moving test: create 1201 1kb sized files
dd if=/dev/zero of=dir/f_zero bs=1k count=1
for i in {1..1200}; do
cp dir/f_zero dir/f$i
done
umount dir

View File

@ -3,6 +3,13 @@
* without recreating it.
*
* TODO bigalloc compatibility
* TODO support undo, but not by undo_io_manager because it is VERY slow
* TODO check the bad block inode before block moving
* TODO check if the block mover does block_alloc_stats
* TODO try to remove duplicate code constructs
* TODO write some tests: for inode moving (image with many files),
* for block moving, including extent blocks (one sparse file with many extents),
* for calculating block group statistics (block moving between different groups)
*
* The theory isn't that hard:
* 1) If shrinking - move inodes away from the end of each block group inode table
@ -25,7 +32,9 @@
* s_free_inodes_count, s_inodes_per_group
*
* 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?
* So we should provide a rollback method. undo_io_manager is very slow, so maybe the
* original idea should be implemented: create the "patch" i/o manager that writes all
* changed blocks to a separate file and only then applies it.
*/
#include <stdio.h>
@ -42,6 +51,7 @@
#include <ext2fs/ext2fs.h>
#define _(a) (a)
#define min(a,b) ((a)<(b)?(a):(b))
errcode_t ext2fs_move_blocks(ext2_filsys fs,
ext2fs_block_bitmap reserve,
@ -54,6 +64,7 @@ typedef struct
ext2_filsys fs;
int fs_fd;
char *device_name, *io_options;
__u32 old_inodes_per_group;
__u32 new_inode_count, new_inodes_per_group, new_inode_blocks_per_group;
// (old->new) inode number map
ext2_ino_t *inode_map;
@ -345,8 +356,7 @@ int extend_move_blocks(realloc_data *rd)
ext2fs_mark_block_bitmap_range2(reserve_map, it_start, rd->new_inode_blocks_per_group-rd->fs->inode_blocks_per_group);
}
}
// TODO Check the bad block inode
retval = ext2fs_move_blocks(rd->fs, reserve_map, rd->fs->block_map, 2);
retval = ext2fs_move_blocks(rd->fs, reserve_map, rd->fs->block_map, 0);
ext2fs_mark_bb_dirty(rd->fs);
ext2fs_flush(rd->fs);
out:
@ -396,10 +406,10 @@ int change_inode_numbers(realloc_data *rd)
int change_super_and_bgd(realloc_data *rd)
{
blk64_t it_start, blk;
dgrp_t grp, n_flex, n_grp, flex_count;
dgrp_t grp, flex_grp, flex_count;
__u32 unus;
__u32 i_per_g_diff = rd->new_inodes_per_group - EXT2_INODES_PER_GROUP(rd->fs->super);
int flexbg_size = 0, i, retval = 0;
int i_per_g_diff = rd->new_inodes_per_group - EXT2_INODES_PER_GROUP(rd->fs->super);
int flexbg_size = 0, n_grp, i, retval = 0;
void *buf = NULL;
ext2fs_flush(rd->fs);
if (!rd->fs->block_map)
@ -419,20 +429,22 @@ int change_super_and_bgd(realloc_data *rd)
{
goto out;
}
for (n_flex = 0; n_flex < flex_count; n_flex++)
for (flex_grp = 0; flex_grp < flex_count; flex_grp++)
{
memset(buf, 0, EXT2_BLOCK_SIZE(rd->fs->super) * rd->new_inode_blocks_per_group * flexbg_size);
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;
}
// Read inode tables
for (grp = n_flex*flexbg_size, i = 0; i < n_grp; grp++, i++)
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))
{
blk = ext2fs_inode_table_loc(rd->fs, grp);
retval = io_channel_read_blk64(rd->fs->io, blk, rd->new_inode_blocks_per_group,
retval = io_channel_read_blk64(rd->fs->io, blk,
min(rd->fs->inode_blocks_per_group, rd->new_inode_blocks_per_group),
buf + i*rd->new_inode_blocks_per_group*EXT2_BLOCK_SIZE(rd->fs->super));
if (retval)
{
@ -441,13 +453,13 @@ int change_super_and_bgd(realloc_data *rd)
}
}
// Write inode table to the new place
it_start = ext2fs_inode_table_loc(rd->fs, n_flex*flexbg_size);
it_start = ext2fs_inode_table_loc(rd->fs, flex_grp*flexbg_size);
blk = rd->new_inode_blocks_per_group * n_grp;
retval = io_channel_write_blk64(rd->fs->io, it_start, blk, buf);
if (retval)
{
// The FS is badly corrupted now :-(
printf("Error moving inode tables for %u groups, starting from %u\n", n_grp, n_flex*flexbg_size);
printf("Error moving inode tables for %u groups, starting from %u\n", n_grp, flex_grp*flexbg_size);
goto out;
}
// Mark/unmark extra inode table blocks
@ -458,21 +470,32 @@ int change_super_and_bgd(realloc_data *rd)
}
else
{
ext2fs_mark_block_bitmap_range2(rd->fs->block_map, it_start+blk,
ext2fs_mark_block_bitmap_range2(rd->fs->block_map, it_start+rd->fs->inode_blocks_per_group*n_grp,
(rd->new_inode_blocks_per_group-rd->fs->inode_blocks_per_group)*n_grp);
}
ext2fs_bg_free_blocks_count_set(rd->fs, n_flex*flexbg_size,
ext2fs_bg_free_blocks_count(rd->fs, n_flex*flexbg_size) -
ext2fs_bg_free_blocks_count_set(rd->fs, flex_grp*flexbg_size,
ext2fs_bg_free_blocks_count(rd->fs, flex_grp*flexbg_size) -
(rd->new_inode_blocks_per_group - rd->fs->inode_blocks_per_group)*flexbg_size);
// Change inode table locations and free inode counts
for (grp = n_flex*flexbg_size, i = 0; i < n_grp; grp++, i++)
for (grp = flex_grp*flexbg_size, i = 0; i < n_grp; grp++, i++)
{
blk = it_start + rd->new_inode_blocks_per_group*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) + i_per_g_diff);
unus = ext2fs_bg_itable_unused(rd->fs, grp);
unus = unus < i_per_g_diff ? 0 : unus - i_per_g_diff;
if (i_per_g_diff > 0)
{
unus += i_per_g_diff;
}
else if (unus < (unsigned)-i_per_g_diff)
{
unus = 0;
}
else
{
unus = unus + i_per_g_diff;
}
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);
@ -500,7 +523,18 @@ int change_super_and_bgd(realloc_data *rd)
ext2fs_bg_free_inodes_count_set(rd->fs, grp, ext2fs_bg_free_inodes_count(rd->fs, grp) +
rd->new_inodes_per_group - EXT2_INODES_PER_GROUP(rd->fs->super));
unus = ext2fs_bg_itable_unused(rd->fs, grp);
unus = unus < i_per_g_diff ? 0 : unus - i_per_g_diff;
if (i_per_g_diff > 0)
{
unus += i_per_g_diff;
}
else if (unus < (unsigned)-i_per_g_diff)
{
unus = 0;
}
else
{
unus = unus + i_per_g_diff;
}
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);
@ -520,6 +554,20 @@ int change_super_and_bgd(realloc_data *rd)
rd->fs->super->s_inodes_per_group = rd->new_inodes_per_group;
rd->fs->super->s_inodes_count = rd->fs->group_desc_count * rd->new_inodes_per_group;
ext2fs_mark_super_dirty(rd->fs);
if (rd->new_inodes_per_group > rd->old_inodes_per_group)
{
// Mark newly allocated inodes as free
__u32 ino;
ext2fs_read_inode_bitmap(rd->fs);
for (grp = 0; grp < rd->fs->group_desc_count; grp++)
{
for (ino = rd->old_inodes_per_group; ino < rd->new_inodes_per_group; ino++)
{
ext2fs_unmark_inode_bitmap2(rd->fs->inode_map, 1 + ino + grp*rd->new_inodes_per_group);
}
}
ext2fs_mark_ib_dirty(rd->fs);
}
out:
if (buf)
{
@ -534,6 +582,7 @@ out:
int do_realloc(realloc_data *rd)
{
int retval;
rd->old_inodes_per_group = EXT2_INODES_PER_GROUP(rd->fs->super);
rd->new_inodes_per_group = rd->new_inode_count / rd->fs->group_desc_count;
rd->new_inodes_per_group &= ~7;
if (rd->new_inodes_per_group < 16)