2013-12-25 14:15:02 +04:00
|
|
|
/**
|
|
|
|
* An attempt to write a tool for ext4 that will allow to change inode count of a filesystem.
|
|
|
|
*
|
|
|
|
* In theory it shouldn't be that hard:
|
|
|
|
* 1) If shrinking:
|
2013-12-26 17:00:03 +04:00
|
|
|
* 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
|
2013-12-25 14:15:02 +04:00
|
|
|
* 2) If growing:
|
2013-12-26 17:00:03 +04:00
|
|
|
* 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
|
2013-12-25 14:15:02 +04:00
|
|
|
* 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)
|
2013-12-26 17:00:03 +04:00
|
|
|
* also translate old->new inode numbers remembered at (1.2.2)
|
2013-12-25 14:15:02 +04:00
|
|
|
* 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)
|
2013-12-26 17:00:03 +04:00
|
|
|
* 6) If flex_bg is enabled, move parts of old inode tables so they are consecutive again
|
2013-12-25 14:15:02 +04:00
|
|
|
*
|
|
|
|
* 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?)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2013-12-26 17:00:03 +04:00
|
|
|
#include <ext2fs/ext2_fs.h>
|
|
|
|
#include <ext2fs/ext2fs.h>
|
2013-12-25 14:15:02 +04:00
|
|
|
|
|
|
|
#define _(a) (a)
|
|
|
|
|
2013-12-27 16:47:10 +04:00
|
|
|
// "local data" for the inode reallocation process
|
2013-12-26 17:00:03 +04:00
|
|
|
typedef struct
|
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
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;
|
2013-12-26 17:00:03 +04:00
|
|
|
} realloc_data;
|
|
|
|
|
2013-12-27 16:47:10 +04:00
|
|
|
// Utility functions for (old -> new) inode number map
|
|
|
|
void realloc_add_inode_map(realloc_data *rd, ext2_ino_t old, ext2_ino_t new)
|
2013-12-26 17:00:03 +04:00
|
|
|
{
|
|
|
|
if (!old || !new)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (2*rd->inode_map_size >= rd->inode_map_alloc)
|
|
|
|
{
|
|
|
|
rd->inode_map_alloc += 1024;
|
2013-12-27 16:47:10 +04:00
|
|
|
rd->inode_map = realloc(rd->inode_map, sizeof(ext2_ino_t) * rd->inode_map_alloc);
|
2013-12-26 17:00:03 +04:00
|
|
|
}
|
|
|
|
rd->inode_map[rd->inode_map_size*2] = old;
|
|
|
|
rd->inode_map[rd->inode_map_size*2+1] = new;
|
|
|
|
rd->inode_map_size++;
|
|
|
|
}
|
|
|
|
|
2013-12-27 16:47:10 +04:00
|
|
|
int realloc_compare_inode_map_callback(const void *a, const void *b)
|
2013-12-26 17:00:03 +04:00
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
return *((ext2_ino_t*)a) - *((ext2_ino_t*)b);
|
2013-12-26 17:00:03 +04:00
|
|
|
}
|
|
|
|
|
2013-12-27 16:47:10 +04:00
|
|
|
void realloc_sort_inode_map(realloc_data *rd)
|
2013-12-26 17:00:03 +04:00
|
|
|
{
|
|
|
|
if (!rd->inode_map)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2013-12-27 16:47:10 +04:00
|
|
|
qsort(rd->inode_map, rd->inode_map_size, sizeof(ext2_ino_t)*2, realloc_compare_inode_map_callback);
|
2013-12-26 17:00:03 +04:00
|
|
|
}
|
|
|
|
|
2013-12-27 16:47:10 +04:00
|
|
|
ext2_ino_t realloc_search_inode_map(realloc_data *rd, ext2_ino_t old)
|
2013-12-26 17:00:03 +04:00
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
__u32 start = 0, end = rd->inode_map_size, cur;
|
|
|
|
ext2_ino_t cur_ino;
|
2013-12-26 17:00:03 +04:00
|
|
|
if (!rd->inode_map)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
while (end-start > 1)
|
|
|
|
{
|
|
|
|
cur = (start+end)>>1;
|
2013-12-27 16:47:10 +04:00
|
|
|
cur_ino = rd->inode_map[cur<<1];
|
|
|
|
if (cur_ino < old)
|
2013-12-26 17:00:03 +04:00
|
|
|
{
|
|
|
|
start = cur+1;
|
|
|
|
}
|
2013-12-27 16:47:10 +04:00
|
|
|
else if (cur_ino > old)
|
2013-12-26 17:00:03 +04:00
|
|
|
{
|
|
|
|
end = cur;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return rd->inode_map[(cur<<1)+1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-27 16:47:10 +04:00
|
|
|
/**
|
|
|
|
* Move inodes from the end of each block group inode table
|
|
|
|
* so the tables can be shrinked
|
|
|
|
*/
|
|
|
|
int shrink_move_inodes(realloc_data *rd)
|
|
|
|
{
|
2013-12-27 21:13:24 +04:00
|
|
|
int retval = 0, inode_size = EXT2_INODE_SIZE(rd->fs->super);
|
2013-12-27 16:47:10 +04:00
|
|
|
__u32 group, i, last = rd->inode_map_size;
|
|
|
|
__u32 new_group;
|
|
|
|
ext2_ino_t ino, new_ino;
|
2013-12-27 21:13:24 +04:00
|
|
|
struct ext2_inode *inode = NULL;
|
|
|
|
retval = ext2fs_get_mem(inode_size, &inode);
|
|
|
|
if (retval)
|
|
|
|
{
|
|
|
|
return retval;
|
|
|
|
}
|
2013-12-27 16:47:10 +04:00
|
|
|
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)
|
|
|
|
{
|
2013-12-27 21:13:24 +04:00
|
|
|
// No space to move this inode
|
|
|
|
goto out;
|
2013-12-27 16:47:10 +04:00
|
|
|
}
|
2013-12-27 21:13:24 +04:00
|
|
|
// Copy inode to the new place
|
|
|
|
retval = ext2fs_read_inode_full(rd->fs, ino, inode, inode_size);
|
|
|
|
if (retval)
|
|
|
|
{
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
retval = ext2fs_write_inode_full(rd->fs, new_ino, inode, inode_size);
|
|
|
|
if (retval)
|
|
|
|
{
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
retval = ext2fs_mark_inode_bitmap2(rd->fs->inode_map, new_ino);
|
|
|
|
if (retval)
|
|
|
|
{
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
// Remember mapping
|
2013-12-27 16:47:10 +04:00
|
|
|
realloc_add_inode_map(rd, ino, new_ino);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-27 21:13:24 +04:00
|
|
|
out:
|
|
|
|
if (inode)
|
2013-12-27 16:47:10 +04:00
|
|
|
{
|
2013-12-27 21:13:24 +04:00
|
|
|
ext2fs_free_mem(&inode);
|
2013-12-27 16:47:10 +04:00
|
|
|
}
|
2013-12-27 21:13:24 +04:00
|
|
|
return retval;
|
2013-12-27 16:47:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-12-27 21:13:24 +04:00
|
|
|
* Move data blocks from after the end of each block group inode table
|
2013-12-27 16:47:10 +04:00
|
|
|
* so the tables can be grown
|
|
|
|
*/
|
|
|
|
int extend_move_blocks(realloc_data *rd)
|
|
|
|
{
|
|
|
|
|
2013-12-27 21:13:24 +04:00
|
|
|
return ENOSYS;
|
2013-12-27 16:47:10 +04:00
|
|
|
}
|
2013-12-25 14:15:02 +04:00
|
|
|
|
2013-12-27 21:13:24 +04:00
|
|
|
static int change_inode_numbers_callback(ext2_ino_t dir, int entry,
|
2013-12-26 03:18:14 +04:00
|
|
|
struct ext2_dir_entry *dirent, int offset,
|
|
|
|
int blocksize, char *buf, void *priv_data)
|
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
realloc_data *rd = priv_data;
|
2013-12-26 03:18:14 +04:00
|
|
|
char name[EXT2_NAME_LEN+1];
|
2013-12-27 16:47:10 +04:00
|
|
|
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;
|
2013-12-26 03:18:14 +04:00
|
|
|
strncpy(name, dirent->name, dirent->name_len & 0xff);
|
|
|
|
name[dirent->name_len & 0xff] = 0;
|
2013-12-27 16:47:10 +04:00
|
|
|
printf("%s: old %d, new %d\n", name, dirent->inode, new_ino);
|
2013-12-27 21:13:24 +04:00
|
|
|
if (new_ino != dirent->inode)
|
|
|
|
{
|
|
|
|
dirent->inode = new_ino;
|
|
|
|
return DIRENT_CHANGED;
|
|
|
|
}
|
2013-12-26 03:18:14 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-27 16:47:10 +04:00
|
|
|
/**
|
|
|
|
* Change inode numbers in all directory entries
|
|
|
|
*/
|
|
|
|
int change_inode_numbers(realloc_data *rd)
|
2013-12-25 14:15:02 +04:00
|
|
|
{
|
2013-12-27 21:13:24 +04:00
|
|
|
int j, retval;
|
|
|
|
dgrp_t grp;
|
2013-12-25 14:15:02 +04:00
|
|
|
blk64_t blk;
|
|
|
|
void *buf;
|
|
|
|
struct ext2_inode *inode;
|
2013-12-27 21:13:24 +04:00
|
|
|
retval = ext2fs_get_mem(EXT2_BLOCK_SIZE(rd->fs->super) * rd->fs->inode_blocks_per_group, &buf);
|
2013-12-27 16:47:10 +04:00
|
|
|
realloc_sort_inode_map(rd);
|
2013-12-27 21:13:24 +04:00
|
|
|
for (grp = 0; grp < rd->fs->group_desc_count; grp++)
|
2013-12-25 14:15:02 +04:00
|
|
|
{
|
|
|
|
// read inode table of a group
|
2013-12-27 21:13:24 +04:00
|
|
|
if (!ext2fs_bg_flags_test(rd->fs, grp, EXT2_BG_INODE_UNINIT))
|
2013-12-25 14:15:02 +04:00
|
|
|
{
|
2013-12-27 21:13:24 +04:00
|
|
|
blk = ext2fs_inode_table_loc(rd->fs, grp);
|
|
|
|
retval = io_channel_read_blk64(rd->fs->io, blk, rd->fs->inode_blocks_per_group, buf);
|
|
|
|
if (retval)
|
2013-12-25 14:15:02 +04:00
|
|
|
{
|
2013-12-27 21:13:24 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (j = 0; j < rd->fs->super->s_inodes_per_group; 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+grp*rd->fs->super->s_inodes_per_group);
|
|
|
|
ext2fs_dir_iterate2(rd->fs, 1+j+grp*rd->fs->super->s_inodes_per_group, 0, 0, change_inode_numbers_callback, rd);
|
|
|
|
printf("\n");
|
|
|
|
}
|
2013-12-25 14:15:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ext2fs_free_mem(&buf);
|
2013-12-27 16:47:10 +04:00
|
|
|
return 0;
|
2013-12-25 14:15:02 +04:00
|
|
|
}
|
|
|
|
|
2013-12-27 16:47:10 +04:00
|
|
|
/**
|
|
|
|
* If flex_bg is enabled, move inode tables so they are consecutive again
|
2013-12-27 21:13:24 +04:00
|
|
|
*/
|
|
|
|
int flex_bg_move_itables(realloc_data *rd)
|
|
|
|
{
|
|
|
|
blk64_t it_start, blk;
|
|
|
|
dgrp_t grp, n_flex, last_grp;
|
|
|
|
int flexbg_size = 0, i, retval = 0;
|
|
|
|
void *buf = NULL;
|
|
|
|
ext2fs_flush(rd->fs);
|
|
|
|
if (rd->new_inode_blocks_per_group != rd->fs->inode_blocks_per_group)
|
|
|
|
{
|
|
|
|
// Move inode tables, if flex_bg is active
|
|
|
|
if (EXT2_HAS_INCOMPAT_FEATURE(rd->fs->super, EXT4_FEATURE_INCOMPAT_FLEX_BG)
|
|
|
|
&& rd->fs->super->s_log_groups_per_flex)
|
|
|
|
{
|
|
|
|
flexbg_size = 1 << rd->fs->super->s_log_groups_per_flex;
|
|
|
|
retval = ext2fs_get_mem(EXT2_BLOCK_SIZE(rd->fs->super) * rd->new_inode_blocks_per_group * flexbg_size, &buf);
|
|
|
|
if (retval)
|
|
|
|
{
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
for (n_flex = 0; n_flex < rd->fs->group_desc_count/flexbg_size; n_flex++)
|
|
|
|
{
|
|
|
|
last_grp = (n_flex+1)*flexbg_size;
|
|
|
|
if (last_grp > rd->fs->group_desc_count)
|
|
|
|
{
|
|
|
|
last_grp = rd->fs->group_desc_count;
|
|
|
|
}
|
|
|
|
for (grp = n_flex*flexbg_size, i = 0; grp < last_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,
|
|
|
|
buf + i*rd->new_inode_blocks_per_group*EXT2_BLOCK_SIZE(rd->fs->super));
|
|
|
|
if (retval)
|
|
|
|
{
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
it_start = ext2fs_inode_table_loc(rd->fs, n_flex*flexbg_size);
|
|
|
|
blk = rd->new_inode_blocks_per_group * (last_grp-n_flex*flexbg_size);
|
|
|
|
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", last_grp-n_flex*flexbg_size, n_flex*flexbg_size);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
// Mark/unmark extra inode table blocks
|
|
|
|
if (rd->new_inode_blocks_per_group < rd->fs->inode_blocks_per_group)
|
|
|
|
{
|
|
|
|
ext2fs_unmark_block_bitmap_range(rd->fs->block_map, it_start+blk, (rd->fs->inode_blocks_per_group-rd->new_inode_blocks_per_group)*flexbg_size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ext2fs_mark_block_bitmap_range(rd->fs->block_map, it_start+blk, (rd->new_inode_blocks_per_group-rd->fs->inode_blocks_per_group)*flexbg_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Mark/unmark extra inode table blocks (without flex_bg)
|
|
|
|
for (grp = 0; grp < rd->fs->group_desc_count; grp++)
|
|
|
|
{
|
|
|
|
if (!ext2fs_bg_flags_test(rd->fs, grp, EXT2_BG_INODE_UNINIT))
|
|
|
|
{
|
|
|
|
it_start = ext2fs_inode_table_loc(rd->fs, grp);
|
|
|
|
if (rd->new_inode_blocks_per_group < rd->fs->inode_blocks_per_group)
|
|
|
|
{
|
|
|
|
ext2fs_unmark_block_bitmap_range(rd->fs->block_map, it_start, rd->fs->inode_blocks_per_group-rd->new_inode_blocks_per_group);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ext2fs_mark_block_bitmap_range(rd->fs->block_map, it_start, rd->new_inode_blocks_per_group-rd->fs->inode_blocks_per_group);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (buf)
|
|
|
|
{
|
|
|
|
ext2fs_free_mem(&buf);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adjust superblock and block group descriptors
|
2013-12-27 16:47:10 +04:00
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
2013-12-27 21:13:24 +04:00
|
|
|
retval = flex_bg_move_itables(rd);
|
|
|
|
if (retval)
|
|
|
|
{
|
|
|
|
return retval;
|
|
|
|
}
|
2013-12-27 16:47:10 +04:00
|
|
|
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";
|
|
|
|
|
2013-12-25 14:15:02 +04:00
|
|
|
int main(int narg, char **args)
|
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
realloc_data rd = { 0 };
|
2013-12-25 14:15:02 +04:00
|
|
|
int optind, fd, retval, io_flags = 0, force = 0;
|
|
|
|
ext2fs_struct_stat st_buf;
|
|
|
|
char *device_name, *io_options;
|
|
|
|
|
|
|
|
add_error_table(&et_ext2_error_table);
|
2013-12-27 16:47:10 +04:00
|
|
|
|
|
|
|
// USAGE: ./realloc-inodes <device> <new_count>
|
|
|
|
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)
|
2013-12-25 14:15:02 +04:00
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
com_err("open", errno, _("while opening %s"), rd.device_name);
|
2013-12-25 14:15:02 +04:00
|
|
|
exit(1);
|
|
|
|
}
|
2013-12-27 16:47:10 +04:00
|
|
|
retval = ext2fs_fstat(rd.fs_fd, &st_buf);
|
2013-12-25 14:15:02 +04:00
|
|
|
if (retval < 0)
|
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
com_err("open", errno, _("while getting stat information for %s"), rd.device_name);
|
2013-12-25 14:15:02 +04:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (!S_ISREG(st_buf.st_mode))
|
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
close(rd.fs_fd);
|
|
|
|
rd.fs_fd = -1;
|
2013-12-25 14:15:02 +04:00
|
|
|
}
|
2013-12-27 16:47:10 +04:00
|
|
|
rd.io_options = strchr(rd.device_name, '?');
|
|
|
|
if (rd.io_options)
|
2013-12-25 14:15:02 +04:00
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
*rd.io_options++ = 0;
|
2013-12-25 14:15:02 +04:00
|
|
|
}
|
|
|
|
//io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE;
|
|
|
|
io_flags |= EXT2_FLAG_64BITS;
|
2013-12-27 16:47:10 +04:00
|
|
|
retval = ext2fs_open2(rd.device_name, rd.io_options, io_flags, 0, 0, unix_io_manager, &rd.fs);
|
2013-12-25 14:15:02 +04:00
|
|
|
if (retval)
|
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
com_err(program_name, retval, _("while trying to open %s"), rd.device_name);
|
2013-12-25 14:15:02 +04:00
|
|
|
printf(_("Couldn't find valid filesystem superblock.\n"));
|
|
|
|
exit(1);
|
|
|
|
}
|
2013-12-27 16:47:10 +04:00
|
|
|
if (!force && ((rd.fs->super->s_state & EXT2_ERROR_FS) || ((rd.fs->super->s_state & EXT2_VALID_FS) == 0)))
|
2013-12-25 14:15:02 +04:00
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
fprintf(stderr, _("Please run 'e2fsck -f %s' first.\n\n"), rd.device_name);
|
2013-12-25 14:15:02 +04:00
|
|
|
exit(1);
|
|
|
|
}
|
2013-12-27 16:47:10 +04:00
|
|
|
do_realloc(&rd);
|
|
|
|
ext2fs_close(rd.fs);
|
|
|
|
if (rd.fs_fd > 0)
|
2013-12-25 14:15:02 +04:00
|
|
|
{
|
2013-12-27 16:47:10 +04:00
|
|
|
close(rd.fs_fd);
|
2013-12-25 14:15:02 +04:00
|
|
|
}
|
|
|
|
}
|