diff --git a/realloc-inodes.c b/realloc-inodes.c index 69818c3..339508f 100644 --- a/realloc-inodes.c +++ b/realloc-inodes.c @@ -40,12 +40,20 @@ #define _(a) (a) +// "local data" for the inode reallocation process typedef struct { - __u32 *inode_map, inode_map_size, inode_map_alloc; + ext2_filsys fs; + int fs_fd; + char *device_name, *io_options; + __u32 new_inode_count, new_inodes_per_group, new_inode_blocks_per_group; + // (old->new) inode number map + ext2_ino_t *inode_map; + __u32 inode_map_size, inode_map_alloc; } realloc_data; -void add_inode_map(realloc_data *rd, __u32 old, __u32 new) +// Utility functions for (old -> new) inode number map +void realloc_add_inode_map(realloc_data *rd, ext2_ino_t old, ext2_ino_t new) { if (!old || !new) { @@ -54,30 +62,31 @@ void add_inode_map(realloc_data *rd, __u32 old, __u32 new) if (2*rd->inode_map_size >= rd->inode_map_alloc) { rd->inode_map_alloc += 1024; - rd->inode_map = realloc(rd->inode_map, sizeof(__u32) * rd->inode_map_alloc); + rd->inode_map = realloc(rd->inode_map, sizeof(ext2_ino_t) * rd->inode_map_alloc); } rd->inode_map[rd->inode_map_size*2] = old; rd->inode_map[rd->inode_map_size*2+1] = new; rd->inode_map_size++; } -int compare_inode_map(const void *a, const void *b) +int realloc_compare_inode_map_callback(const void *a, const void *b) { - return *((__u32*)a) - *((__u32*)b); + return *((ext2_ino_t*)a) - *((ext2_ino_t*)b); } -void sort_inode_map(realloc_data *rd) +void realloc_sort_inode_map(realloc_data *rd) { if (!rd->inode_map) { return; } - qsort(rd->inode_map, rd->inode_map_size, sizeof(__u32)*2, compare_inode_map); + qsort(rd->inode_map, rd->inode_map_size, sizeof(ext2_ino_t)*2, realloc_compare_inode_map_callback); } -__u32 search_inode_map(realloc_data *rd, __u32 old) +ext2_ino_t realloc_search_inode_map(realloc_data *rd, ext2_ino_t old) { - __u32 start = 0, end = rd->inode_map_size, cur, cur_old; + __u32 start = 0, end = rd->inode_map_size, cur; + ext2_ino_t cur_ino; if (!rd->inode_map) { return 0; @@ -85,12 +94,12 @@ __u32 search_inode_map(realloc_data *rd, __u32 old) while (end-start > 1) { cur = (start+end)>>1; - cur_old = rd->inode_map[cur<<1]; - if (cur < old) + cur_ino = rd->inode_map[cur<<1]; + if (cur_ino < old) { start = cur+1; } - else if (cur > old) + else if (cur_ino > old) { end = cur; } @@ -102,106 +111,268 @@ __u32 search_inode_map(realloc_data *rd, __u32 old) return 0; } -const char *program_name = "realloc-inodes"; - -int change_inode_numbers(ext2_ino_t dir, int entry, - struct ext2_dir_entry *dirent, int offset, - int blocksize, char *buf, void *priv_data) +/** + * Move inodes from the end of each block group inode table + * so the tables can be shrinked + */ +int shrink_move_inodes(realloc_data *rd) { - unsigned int *i_per_g = priv_data; - char name[EXT2_NAME_LEN+1]; - strncpy(name, dirent->name, dirent->name_len & 0xff); - name[dirent->name_len & 0xff] = 0; - printf("%s: old %d, new %d\n", name, dirent->inode, 1 + (dirent->inode-1)/i_per_g[0]*i_per_g[1] + (dirent->inode-1)%i_per_g[0]); + int retval; + __u32 group, i, last = rd->inode_map_size; + __u32 new_group; + ext2_ino_t ino, new_ino; + for (group = 0; group < rd->fs->group_desc_count; group++) + { + for (i = rd->new_inodes_per_group; i < rd->fs->super->s_inodes_per_group; i++) + { + ino = 1 + group*rd->fs->super->s_inodes_per_group + i; + if (ext2fs_test_inode_bitmap(rd->fs->inode_map, ino)) + { + // Inode is occupied and should be moved + new_group = group; + do + { + retval = ext2fs_find_first_zero_inode_bitmap2(rd->fs->inode_map, + 1 + new_group*rd->fs->super->s_inodes_per_group, + 1 + new_group*rd->fs->super->s_inodes_per_group+rd->new_inodes_per_group, &new_ino); + if (!retval) + { + break; + } + new_group = (new_group+1) % rd->fs->group_desc_count; + } while (new_group != group); + if (retval) + { + return ENOENT; + } + realloc_add_inode_map(rd, ino, new_ino); + } + } + } + // Move inodes + for (i = last; i < rd->inode_map_size; i++) + { + ino = rd->inode_map[i<<1]; + + } return 0; } -// try to read out all directory inodes? -void test(ext2_filsys fs) +/** + * Move blocks from after the end of each block group inode table + * so the tables can be grown + */ +int extend_move_blocks(realloc_data *rd) +{ + + return 0; +} + +int change_inode_numbers_callback(ext2_ino_t dir, int entry, + struct ext2_dir_entry *dirent, int offset, + int blocksize, char *buf, void *priv_data) +{ + realloc_data *rd = priv_data; + char name[EXT2_NAME_LEN+1]; + ext2_ino_t new_ino = realloc_search_inode_map(rd, dirent->inode); + if (!new_ino) + { + new_ino = dirent->inode; + } + new_ino = 1 + (new_ino-1)/rd->fs->super->s_inodes_per_group*rd->new_inodes_per_group + + (new_ino-1)%rd->fs->super->s_inodes_per_group; + strncpy(name, dirent->name, dirent->name_len & 0xff); + name[dirent->name_len & 0xff] = 0; + printf("%s: old %d, new %d\n", name, dirent->inode, new_ino); + return 0; +} + +/** + * Change inode numbers in all directory entries + */ +int change_inode_numbers(realloc_data *rd) { int i, j, retval; blk64_t blk; void *buf; struct ext2_inode *inode; - unsigned int old_new_i_per_g[2]; - old_new_i_per_g[0] = fs->super->s_inodes_per_group; - old_new_i_per_g[1] = fs->super->s_inodes_per_group/2; - retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group, &buf); - for (i = 0; i < fs->group_desc_count; i++) + retval = ext2fs_get_mem(rd->fs->blocksize * rd->fs->inode_blocks_per_group, &buf); + realloc_sort_inode_map(rd); + for (i = 0; i < rd->fs->group_desc_count; i++) { // read inode table of a group - blk = ext2fs_inode_table_loc(fs, i); - retval = io_channel_read_blk64(fs->io, blk, fs->inode_blocks_per_group, buf); + blk = ext2fs_inode_table_loc(rd->fs, i); + retval = io_channel_read_blk64(rd->fs->io, blk, rd->fs->inode_blocks_per_group, buf); if (retval) { - goto errout; + break; } - for (j = 0; j < fs->super->s_inodes_per_group; j++) + for (j = 0; j < rd->fs->super->s_inodes_per_group; j++) { - inode = (struct ext2_inode*)(buf + EXT2_INODE_SIZE(fs->super) * j); + inode = (struct ext2_inode*)(buf + EXT2_INODE_SIZE(rd->fs->super) * j); if (inode->i_mode & S_IFDIR) { - printf("Directory %d\n", 1+j+i*fs->super->s_inodes_per_group); - ext2fs_dir_iterate2(fs, 1+j+i*fs->super->s_inodes_per_group, 0, 0, change_inode_numbers, old_new_i_per_g); + printf("Directory %d\n", 1+j+i*rd->fs->super->s_inodes_per_group); + ext2fs_dir_iterate2(rd->fs, 1+j+i*rd->fs->super->s_inodes_per_group, 0, 0, change_inode_numbers_callback, rd); printf("\n"); } } } -errout: ext2fs_free_mem(&buf); + return 0; } +/** + * If flex_bg is enabled, move inode tables so they are consecutive again + * Then adjust superblock and block group descriptors + */ +int change_super_and_bgd(realloc_data *rd) +{ + + return 0; +} + +/** + * Main function: change inode number of a filesystem! + */ +int do_realloc(realloc_data *rd) +{ + int retval; + rd->new_inodes_per_group = rd->new_inode_count / rd->fs->group_desc_count; + if (rd->new_inodes_per_group < 16) + { + printf("Too small number of inodes requested, min inodes per group = 16\n"); + return ENOENT; + } + rd->new_inode_blocks_per_group = + (rd->new_inodes_per_group * EXT2_INODE_SIZE(rd->fs->super) + + EXT2_BLOCK_SIZE(rd->fs->super) - 1) / EXT2_BLOCK_SIZE(rd->fs->super); + rd->new_inode_count = rd->new_inodes_per_group * rd->fs->group_desc_count; + if (rd->new_inodes_per_group < rd->fs->super->s_inodes_per_group) + { + if (rd->new_inode_count < rd->fs->super->s_inodes_count - rd->fs->super->s_free_inodes_count) + { + printf("Too small number of inodes requested, existing inodes (%u) won't fit\n", + rd->fs->super->s_inodes_count - rd->fs->super->s_free_inodes_count); + return ENOENT; + } + retval = shrink_move_inodes(rd); + if (retval) + { + return retval; + } + } + else if (rd->new_inodes_per_group > rd->fs->super->s_inodes_per_group) + { + blk64_t required_blocks = (rd->new_inode_blocks_per_group - rd->fs->inode_blocks_per_group) * rd->fs->group_desc_count; + if (required_blocks > ext2fs_free_blocks_count(rd->fs->super)) + { + printf("Requested number of inodes is too big, it requires %llu free blocks, " + "and there are only %llu free blocks available\n", + required_blocks, ext2fs_free_blocks_count(rd->fs->super)); + return ENOENT; + } + retval = extend_move_blocks(rd); + if (retval) + { + return retval; + } + } + else + { + printf("The requested number of inodes is equal to current\n"); + return 0; + } + retval = change_inode_numbers(rd); + if (retval) + { + return retval; + } + retval = change_super_and_bgd(rd); + if (retval) + { + return retval; + } + return 0; +} + +__u32 atou(char *s) +{ + __u32 x = 0; + if (s[0] == '0') + { + if (s[1] == 'x' || s[1] == 'X') + { + sscanf(s+2, "%x", &x); + } + else + { + sscanf(s+1, "%o", &x); + } + } + else + { + sscanf(s, "%u", &x); + } + return x; +} + +const char *program_name = "realloc-inodes"; + int main(int narg, char **args) { - ext2_filsys fs; - io_manager io_ptr = unix_io_manager; + realloc_data rd = { 0 }; int optind, fd, retval, io_flags = 0, force = 0; ext2fs_struct_stat st_buf; char *device_name, *io_options; - optind = 1; - device_name = args[optind++]; add_error_table(&et_ext2_error_table); - fd = ext2fs_open_file(device_name, O_RDWR, 0); - if (fd < 0) + + // USAGE: ./realloc-inodes + optind = 1; + rd.device_name = args[optind++]; + rd.new_inode_count = atou(args[optind++]); + + rd.fs_fd = ext2fs_open_file(rd.device_name, O_RDWR, 0); + if (rd.fs_fd < 0) { - com_err("open", errno, _("while opening %s"), device_name); + com_err("open", errno, _("while opening %s"), rd.device_name); exit(1); } - retval = ext2fs_fstat(fd, &st_buf); + retval = ext2fs_fstat(rd.fs_fd, &st_buf); if (retval < 0) { - com_err("open", errno, _("while getting stat information for %s"), device_name); + com_err("open", errno, _("while getting stat information for %s"), rd.device_name); exit(1); } if (!S_ISREG(st_buf.st_mode)) { - close(fd); - fd = -1; + close(rd.fs_fd); + rd.fs_fd = -1; } - io_options = strchr(device_name, '?'); - if (io_options) + rd.io_options = strchr(rd.device_name, '?'); + if (rd.io_options) { - *io_options++ = 0; + *rd.io_options++ = 0; } //io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE; io_flags |= EXT2_FLAG_64BITS; - retval = ext2fs_open2(device_name, io_options, io_flags, 0, 0, io_ptr, &fs); + retval = ext2fs_open2(rd.device_name, rd.io_options, io_flags, 0, 0, unix_io_manager, &rd.fs); if (retval) { - com_err(program_name, retval, _("while trying to open %s"), device_name); + com_err(program_name, retval, _("while trying to open %s"), rd.device_name); printf(_("Couldn't find valid filesystem superblock.\n")); exit(1); } - if (!force && ((fs->super->s_state & EXT2_ERROR_FS) || ((fs->super->s_state & EXT2_VALID_FS) == 0))) + if (!force && ((rd.fs->super->s_state & EXT2_ERROR_FS) || ((rd.fs->super->s_state & EXT2_VALID_FS) == 0))) { - fprintf(stderr, _("Please run 'e2fsck -f %s' first.\n\n"), device_name); + fprintf(stderr, _("Please run 'e2fsck -f %s' first.\n\n"), rd.device_name); exit(1); } - test(fs); - ext2fs_close(fs); - if (fd > 0) + do_realloc(&rd); + ext2fs_close(rd.fs); + if (rd.fs_fd > 0) { - close(fd); + close(rd.fs_fd); } }