diff --git a/Makefile b/Makefile index c2ced1a..18ebed7 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ -all: realloc-inodes +all: realloc-inodes e2patch realloc-inodes: realloc-inodes.c bmove.c ext2fsP.h Makefile patch_io.c patch_io.h gcc -g -Wsign-compare -Wall -o realloc-inodes -lcom_err -lext2fs realloc-inodes.c bmove.c patch_io.c +e2patch: e2patch.c + gcc -g -Wsign-compare -Wall -o e2patch -lcom_err -lext2fs e2patch.c diff --git a/patch_io.c b/patch_io.c index ed4a60f..c3021d5 100644 --- a/patch_io.c +++ b/patch_io.c @@ -3,11 +3,10 @@ * a separate sparse file to apply it later. * * Patch file format: - * 1) sparse data blocks - same size as the filesystem image, but only changed blocks are written - * 2) updated block bitmap - fs_size/block_size/8 + * 1) sparse data blocks - same size as the filesystem, but only changed blocks are written + * 2) updated block bitmap - fs_size/block_size/8 bytes * 3) 4 byte FS block size - * 4) 4 byte FS size in blocks - * FIXME: should be 8 bytes! 4 bytes = sizeof(blk_t), from ext2fs_get_device_size() + * 4) 8 byte FS size in blocks * * Copyright (c) Vitaliy Filippov 2014 * License: GNU GPLv2 or later @@ -35,10 +34,6 @@ #define ATTR(x) #endif -/* - * For checking structure magic numbers... - */ - #define EXT2_CHECK_MAGIC(struct, code) if ((struct)->magic != (code)) return (code) struct patch_private_data { @@ -46,7 +41,7 @@ struct patch_private_data { char *patch_file; int patch_fd; int block_size; - blk_t size; + blk64_t size; ext2fs_generic_bitmap bmap; /* The backing io channel */ io_channel real; @@ -68,7 +63,7 @@ static errcode_t patch_get_stats(io_channel channel, io_stats *stats); static struct struct_io_manager struct_patch_manager = { EXT2_ET_MAGIC_IO_MANAGER, - "Undo I/O Manager", + "Patch I/O Manager", patch_open, patch_close, patch_set_blksize, @@ -98,18 +93,18 @@ errcode_t set_patch_io_patch_file(char *file) return 0; } -static void patch_read_bmap(struct patch_private_data *data) +static void patch_read_bmap(struct patch_private_data *data, int fd) { - int bufsize = 65536, r; - ext2_loff_t i; + int bufsize = 65536; + blk64_t i, r; void *buf = malloc(bufsize); - ext2fs_llseek(data->patch_fd, data->size*data->block_size, SEEK_SET); + ext2fs_llseek(fd, data->size*data->block_size, SEEK_SET); for (i = 0; i < data->size/8; ) { r = bufsize; if (data->size/8 - i < r) r = data->size/8 - i; - r = read(data->patch_fd, buf, r); + r = read(fd, buf, r); if (r < 0) { r = 0; @@ -122,19 +117,19 @@ static void patch_read_bmap(struct patch_private_data *data) free(buf); } -static void patch_write_bmap(struct patch_private_data *data) +static void patch_write_bmap(struct patch_private_data *data, int fd) { - int bufsize = 65536, r; - ext2_loff_t i; + int bufsize = 65536; + blk64_t i, r; void *buf = malloc(bufsize); - ext2fs_llseek(data->patch_fd, data->size*data->block_size, SEEK_SET); + ext2fs_llseek(fd, data->size*data->block_size, SEEK_SET); for (i = 0; i < data->size/8; ) { r = bufsize; if (data->size/8 - i < r) r = data->size/8 - i; ext2fs_get_generic_bmap_range(data->bmap, i*8, r*8, buf); - r = write(data->patch_fd, buf, r); + r = write(fd, buf, r); if (r < 0) { r = 0; @@ -144,8 +139,8 @@ static void patch_write_bmap(struct patch_private_data *data) i += r; } free(buf); - write(data->patch_fd, &data->block_size, sizeof(int)); - write(data->patch_fd, &data->size, sizeof(blk_t)); + write(fd, &data->block_size, sizeof(int)); + write(fd, &data->size, sizeof(blk64_t)); } static errcode_t patch_open(const char *name, int flags, io_channel *channel) @@ -203,16 +198,16 @@ static errcode_t patch_open(const char *name, int flags, io_channel *channel) retval = errno; goto cleanup; } - if (size > sizeof(int)+sizeof(blk_t)) + if (size > sizeof(int)+sizeof(blk64_t)) { - ext2fs_llseek(data->patch_fd, size-sizeof(int)-sizeof(blk_t), 0); + ext2fs_llseek(data->patch_fd, size-sizeof(int)-sizeof(blk64_t), 0); read(data->patch_fd, &data->block_size, sizeof(int)); - read(data->patch_fd, &data->size, sizeof(blk_t)); + read(data->patch_fd, &data->size, sizeof(blk64_t)); retval = ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, NULL, 0, data->size, data->size, "overwritten blocks", 0, &data->bmap); if (retval) goto cleanup; - patch_read_bmap(data); + patch_read_bmap(data, data->patch_fd); } } @@ -224,7 +219,7 @@ cleanup: { if (data->bmap) ext2fs_free_generic_bmap(data->bmap); - if (data->patch_fd) + if (data->patch_fd >= 0) close(data->patch_fd); if (data->patch_file) free(data->patch_file); @@ -255,10 +250,11 @@ static errcode_t patch_close(io_channel channel) if (data->bmap) { - patch_write_bmap(data); + if (data->patch_fd >= 0) + patch_write_bmap(data, data->patch_fd); ext2fs_free_generic_bmap(data->bmap); } - if (data->patch_fd) + if (data->patch_fd >= 0) close(data->patch_fd); if (data->patch_file) free(data->patch_file); @@ -357,13 +353,13 @@ static errcode_t patch_read_blk(io_channel channel, unsigned long block, int cou return patch_read_blk64(channel, block, count, buf); } -static errcode_t patch_check_bmap(struct patch_private_data *data, io_channel channel) +static errcode_t patch_check_bmap_init(struct patch_private_data *data, io_channel channel) { errcode_t retval = 0; if (!data->bmap) { data->block_size = channel->block_size; - retval = ext2fs_get_device_size(channel->name, data->block_size, &data->size); + retval = ext2fs_get_device_size2(channel->name, data->block_size, &data->size); if (retval) return retval; retval = ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, NULL, @@ -384,7 +380,7 @@ static errcode_t patch_write_blk64(io_channel channel, unsigned long long block, if ((unsigned)ext2fs_llseek(data->patch_fd, block*channel->block_size, SEEK_SET) != block*channel->block_size) return errno ? errno : EXT2_ET_LLSEEK_FAILED; - retval = patch_check_bmap(data, channel); + retval = patch_check_bmap_init(data, channel); if (retval) return retval; if (count < 0) @@ -451,10 +447,10 @@ static errcode_t patch_set_option(io_channel channel, const char *option, const * Need to support offset option to work with * Unix I/O manager */ - if (data->real && data->real->manager->set_option) { + if (data->real && data->real->manager->set_option) retval = data->real->manager->set_option(data->real, option, arg); - } - if (!retval && !strcmp(option, "offset")) { + if (!retval && !strcmp(option, "offset")) + { if (!arg) return EXT2_ET_INVALID_ARGUMENT; diff --git a/realloc-inodes.c b/realloc-inodes.c index bf5f84b..df3370f 100644 --- a/realloc-inodes.c +++ b/realloc-inodes.c @@ -63,7 +63,7 @@ typedef struct { ext2_filsys fs; int fs_fd; - char *device_name, *io_options; + char *device_name, *io_options, *patch_file; __u32 ig_old, ig_new; // old and new inodes-per-group count __u32 ibg_old, ibg_new; // old and new inode_blocks-per-group count __u32 new_inode_count; @@ -646,99 +646,54 @@ __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) +static int setup_patch_io(char *name, char *patch_file, io_manager *io_ptr) { set_patch_io_backing_manager(*io_ptr); - set_patch_io_patch_file("./patch"); + set_patch_io_patch_file(patch_file); *io_ptr = patch_io_manager; + printf(_( + "To apply the inode change operation to the real filesystem" + " please run the command\n e2patch apply %s %s\n" + ), name, patch_file); return 0; } -static int setup_tdb_0(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; io_manager io_ptr = unix_io_manager; struct stat st_buf; - if (narg < 3) + for (optind = 1; optind < narg; optind++) { - printf("USAGE: ./realloc-inodes \n"); + if (!strcmp(args[optind], "--patch")) + rd.patch_file = args[++optind]; + else if (args[optind][0] == '-' && args[optind][1] == '-') + { + if (strcmp(args[optind], "--help") != 0) + printf("Unknown option: %s\n", args[optind]); + break; + } + else if (!rd.device_name) + rd.device_name = args[optind]; + else + rd.new_inode_count = atou(args[optind++]); + } + if (!rd.device_name || !rd.new_inode_count) + { + printf( + "Change inode count of an ext2/ext3/ext4 filesystem\n" + "License: GNU GPLv2 or later\nCopyright (c) Vitaliy Filippov, 2013+\n\n" + "USAGE: ./realloc-inodes [--patch ] \n\n" + "If is specified, all modifications are written to it\n" + "instead of directly modifying the filesystem. These modifications\n" + "can then be applied and unapplied to the real filesystem in a safe way.\n" + " should be on a filesystem supporting sparse files.\n" + ); return 0; } - optind = 1; - rd.device_name = args[optind++]; - rd.new_inode_count = atou(args[optind++]); add_error_table(&et_ext2_error_table); // Open FS rd.fs_fd = ext2fs_open_file(rd.device_name, O_RDWR, 0); @@ -763,8 +718,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); + if (rd.patch_file) + { + setup_patch_io(rd.device_name, rd.patch_file, &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, io_ptr, &rd.fs); if (retval)