Use ext2fs_inode_alloc_stats2, add block moving algorithm details, add (very slow) undo_io_manager support

master
Vitaliy Filippov 2013-12-30 21:09:00 +00:00
parent 8bb97a550a
commit 7ba9912c52
1 changed files with 95 additions and 24 deletions

View File

@ -2,18 +2,23 @@
* A tool for ext2/ext3/ext4 filesystems that allows to change inode count
* without recreating it.
*
* TODO bigalloc compatibility
*
* 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.
* 2.1) Iterate through all block groups and remember extra blocks needed for
* inode tables that are occupied.
* 2.2) Iterate through all inodes and move remembered blocks.
* It involves overwriting the whole file extent tree or block mapping...
* If some of these blocks are in the bad block inode, we should either
* abort the reallocation process, or move inode tables to another location
* in a block group, possibly first defragmenting it... :-(
* 2.3) While copying, remember block number mapping and mark/unmark new/old blocks.
* 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)
* and then using a formula: new_num = 1 + ((old_num-1)/old_i_per_g)*new_i_per_g + ((old_num-1) % old_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,
@ -250,19 +255,13 @@ int shrink_move_inodes(realloc_data *rd)
{
goto out;
}
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);
ext2fs_inode_alloc_stats2(rd->fs, new_ino, 1, inode->i_mode & S_IFDIR);
ext2fs_inode_alloc_stats2(rd->fs, ino, -1, inode->i_mode & S_IFDIR);
// 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);
@ -281,6 +280,7 @@ out:
*/
int extend_move_blocks(realloc_data *rd)
{
// we'll probably need ext2fs_block_alloc_stats2(fs, new_blk, +1);
return ENOSYS;
}
@ -388,12 +388,9 @@ int change_super_and_bgd(realloc_data *rd)
ext2fs_mark_block_bitmap_range2(rd->fs->block_map, it_start+blk,
(rd->new_inode_blocks_per_group-rd->fs->inode_blocks_per_group)*n_grp);
}
if (!ext2fs_bg_flags_test(rd->fs, n_flex*flexbg_size, EXT2_BG_INODE_UNINIT))
{
ext2fs_bg_free_blocks_count_set(rd->fs, n_flex*flexbg_size,
ext2fs_bg_free_blocks_count(rd->fs, n_flex*flexbg_size) -
(rd->new_inode_blocks_per_group - rd->fs->inode_blocks_per_group)*flexbg_size);
}
ext2fs_bg_free_blocks_count_set(rd->fs, n_flex*flexbg_size,
ext2fs_bg_free_blocks_count(rd->fs, n_flex*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++)
{
@ -404,6 +401,7 @@ int change_super_and_bgd(realloc_data *rd)
unus = ext2fs_bg_itable_unused(rd->fs, grp);
unus = unus < i_per_g_diff ? 0 : 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);
}
}
@ -431,6 +429,7 @@ int change_super_and_bgd(realloc_data *rd)
unus = ext2fs_bg_itable_unused(rd->fs, grp);
unus = unus < i_per_g_diff ? 0 : 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);
}
}
@ -546,13 +545,83 @@ __u32 atou(char *s)
return x;
}
static char *basename(char *name)
{
char *n1 = rindex(name, '/'), *n2 = rindex(name, '\\');
if (!n1 && !n2)
{
return name;
}
else if (n1 < n2)
{
return n1+1;
}
else
{
return n2+1;
}
}
const char *program_name = "realloc-inodes";
static int setup_tdb(char *name, io_manager *io_ptr)
{
errcode_t retval = 0;
const char *tdb_dir;
char *tdb_file;
char *dev_name;
dev_name = basename(name);
tdb_dir = getenv("E2FSPROGS_UNDO_DIR");
if (!tdb_dir)
{
tdb_dir = "/var/lib/e2fsprogs";
}
if (!strcmp(tdb_dir, "none") || (tdb_dir[0] == 0))
{
fprintf(stderr, "Can't save undo file to %s; specify different undo directory with E2FSPROGS_UNDO_DIR env variable\n", tdb_dir);
return ENOENT;
}
if ((retval = access(tdb_dir, W_OK)))
{
fprintf(stderr, "Can't save undo file to %s; specify different undo directory with E2FSPROGS_UNDO_DIR env variable\n", tdb_dir);
return EACCES;
}
tdb_file = malloc(strlen(tdb_dir) + 2 + 14 + strlen(dev_name) + 7 + 1);
if (!tdb_file)
{
fprintf(stderr, "Bad alloc\n");
return ENOMEM;
}
sprintf(tdb_file, "%s/realloc-inodes-%s.e2undo", tdb_dir, dev_name);
if (!access(tdb_file, F_OK) && (unlink(tdb_file) < 0))
{
retval = errno;
com_err(program_name, retval,
_("while trying to delete %s"),
tdb_file);
free(tdb_file);
return retval;
}
set_undo_io_backing_manager(*io_ptr);
*io_ptr = undo_io_manager;
set_undo_io_backup_file(tdb_file);
printf(_("To undo the %s operation please run the command\n e2undo %s %s\n\n"), program_name, tdb_file, name);
free(tdb_file);
return retval;
}
int main(int narg, char **args)
{
realloc_data rd = { 0 };
int optind, retval, io_flags = 0, force = 0;
ext2fs_struct_stat st_buf;
io_manager io_ptr = unix_io_manager;
struct stat st_buf;
if (narg < 3)
{
printf("USAGE: ./realloc-inodes <device> <new_inode_count>\n");
@ -566,13 +635,13 @@ int main(int narg, char **args)
rd.fs_fd = ext2fs_open_file(rd.device_name, O_RDWR, 0);
if (rd.fs_fd < 0)
{
com_err("open", errno, _("while opening %s"), rd.device_name);
com_err(program_name, errno, _("while opening %s"), rd.device_name);
exit(1);
}
retval = ext2fs_fstat(rd.fs_fd, &st_buf);
retval = fstat(rd.fs_fd, &st_buf);
if (retval < 0)
{
com_err("open", errno, _("while getting stat information for %s"), rd.device_name);
com_err(program_name, errno, _("while getting stat information for %s"), rd.device_name);
exit(1);
}
if (!S_ISREG(st_buf.st_mode))
@ -585,8 +654,10 @@ int main(int narg, char **args)
{
*rd.io_options++ = 0;
}
// undo_io_manager is very slow by now -- safe, but slow: it commits every transaction...
setup_tdb(rd.device_name, &io_ptr);
io_flags = EXT2_FLAG_64BITS | EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE;
retval = ext2fs_open2(rd.device_name, rd.io_options, io_flags, 0, 0, unix_io_manager, &rd.fs);
retval = ext2fs_open2(rd.device_name, rd.io_options, io_flags, 0, 0, io_ptr, &rd.fs);
if (retval)
{
com_err(program_name, retval, _("while trying to open %s"), rd.device_name);