Use ext2fs_inode_alloc_stats2, add block moving algorithm details, add (very slow) undo_io_manager support
parent
8bb97a550a
commit
7ba9912c52
119
realloc-inodes.c
119
realloc-inodes.c
|
@ -2,18 +2,23 @@
|
||||||
* A tool for ext2/ext3/ext4 filesystems that allows to change inode count
|
* A tool for ext2/ext3/ext4 filesystems that allows to change inode count
|
||||||
* without recreating it.
|
* without recreating it.
|
||||||
*
|
*
|
||||||
|
* TODO bigalloc compatibility
|
||||||
|
*
|
||||||
* The theory isn't that hard:
|
* The theory isn't that hard:
|
||||||
* 1) If shrinking - move inodes away from the end of each block group inode table
|
* 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.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
|
* 1.2) remember the old->new inode number mapping
|
||||||
* 2) If growing - move data away from extra blocks needed by growing inode tables
|
* 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.
|
* 2.1) Iterate through all block groups and remember extra blocks needed for
|
||||||
* If yes, we should either abort the reallocation process, or move inode
|
* inode tables that are occupied.
|
||||||
* tables to another location in a block group, possibly first defragmenting it.
|
* 2.2) Iterate through all inodes and move remembered blocks.
|
||||||
* 2.1) Copy data somewhere, remember block number mapping, mark/unmark new/old.
|
|
||||||
* It involves overwriting the whole file extent tree or block mapping...
|
* 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),
|
* 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
|
* 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
|
* 5) Mark/unmark extra blocks for inode tables
|
||||||
* 6) Change block group descriptors: bg_inode_table, bg_free_inodes_count,
|
* 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;
|
goto out;
|
||||||
}
|
}
|
||||||
ext2fs_mark_inode_bitmap2(rd->fs->inode_map, new_ino);
|
ext2fs_inode_alloc_stats2(rd->fs, new_ino, 1, inode->i_mode & S_IFDIR);
|
||||||
ext2fs_bg_free_inodes_count_set(rd->fs, group, ext2fs_bg_free_inodes_count(rd->fs, group) + 1);
|
ext2fs_inode_alloc_stats2(rd->fs, ino, -1, inode->i_mode & S_IFDIR);
|
||||||
ext2fs_bg_free_inodes_count_set(rd->fs, new_group, ext2fs_bg_free_inodes_count(rd->fs, new_group) - 1);
|
|
||||||
// Remember mapping
|
// Remember mapping
|
||||||
realloc_add_inode_map(rd, ino, new_ino);
|
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)
|
if (rd->inode_map_size)
|
||||||
{
|
{
|
||||||
ext2fs_mark_ib_dirty(rd->fs);
|
ext2fs_mark_ib_dirty(rd->fs);
|
||||||
|
@ -281,6 +280,7 @@ out:
|
||||||
*/
|
*/
|
||||||
int extend_move_blocks(realloc_data *rd)
|
int extend_move_blocks(realloc_data *rd)
|
||||||
{
|
{
|
||||||
|
// we'll probably need ext2fs_block_alloc_stats2(fs, new_blk, +1);
|
||||||
|
|
||||||
return ENOSYS;
|
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,
|
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);
|
(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) -
|
||||||
ext2fs_bg_free_blocks_count_set(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(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
|
// Change inode table locations and free inode counts
|
||||||
for (grp = n_flex*flexbg_size, i = 0; i < n_grp; grp++, i++)
|
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 = ext2fs_bg_itable_unused(rd->fs, grp);
|
||||||
unus = unus < i_per_g_diff ? 0 : unus - i_per_g_diff;
|
unus = unus < i_per_g_diff ? 0 : unus - i_per_g_diff;
|
||||||
ext2fs_bg_itable_unused_set(rd->fs, grp, unus);
|
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);
|
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 = ext2fs_bg_itable_unused(rd->fs, grp);
|
||||||
unus = unus < i_per_g_diff ? 0 : unus - i_per_g_diff;
|
unus = unus < i_per_g_diff ? 0 : unus - i_per_g_diff;
|
||||||
ext2fs_bg_itable_unused_set(rd->fs, grp, unus);
|
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);
|
ext2fs_group_desc_csum_set(rd->fs, grp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,13 +545,83 @@ __u32 atou(char *s)
|
||||||
return x;
|
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";
|
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)
|
int main(int narg, char **args)
|
||||||
{
|
{
|
||||||
realloc_data rd = { 0 };
|
realloc_data rd = { 0 };
|
||||||
int optind, retval, io_flags = 0, force = 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)
|
if (narg < 3)
|
||||||
{
|
{
|
||||||
printf("USAGE: ./realloc-inodes <device> <new_inode_count>\n");
|
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);
|
rd.fs_fd = ext2fs_open_file(rd.device_name, O_RDWR, 0);
|
||||||
if (rd.fs_fd < 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);
|
exit(1);
|
||||||
}
|
}
|
||||||
retval = ext2fs_fstat(rd.fs_fd, &st_buf);
|
retval = fstat(rd.fs_fd, &st_buf);
|
||||||
if (retval < 0)
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (!S_ISREG(st_buf.st_mode))
|
if (!S_ISREG(st_buf.st_mode))
|
||||||
|
@ -585,8 +654,10 @@ int main(int narg, char **args)
|
||||||
{
|
{
|
||||||
*rd.io_options++ = 0;
|
*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;
|
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)
|
if (retval)
|
||||||
{
|
{
|
||||||
com_err(program_name, retval, _("while trying to open %s"), rd.device_name);
|
com_err(program_name, retval, _("while trying to open %s"), rd.device_name);
|
||||||
|
|
Loading…
Reference in New Issue