diff --git a/resize/Makefile.in b/resize/Makefile.in index d5991f81..bda475d2 100644 --- a/resize/Makefile.in +++ b/resize/Makefile.in @@ -12,21 +12,28 @@ INSTALL = @INSTALL@ @MCONFIG@ PROGS= resize2fs +TEST_PROGS= test_extent MANPAGES= resize2fs.8 -RESIZE_OBJS= inodemap.o resize2fs.o main.o +RESIZE_OBJS= extent.o ext2_block_move.o ext2_inode_move.o resize2fs.o \ + main.o sim_progress.o -SRCS= $(srcdir)/inodemap.c \ +TEST_EXTENT_OBJS= extent.o test_extent.o + +SRCS= $(srcdir)/extent.c \ + $(srcdir)/ext2_block_move.c \ + $(srcdir)/ext2_inode_move.c \ $(srcdir)/resize2fs.c \ - $(srcdir)/main.c + $(srcdir)/main.c \ + $(srcdir)/sim_progress.c -LIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBCOM_ERR) $(LIBUUID) -DEPLIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBCOM_ERR) $(LIBUUID) +LIBS= $(LIBEXT2FS) $(LIBCOM_ERR) $(LIBUUID) +DEPLIBS= $(LIBEXT2FS) $(LIBCOM_ERR) $(LIBUUID) .c.o: $(CC) -c $(ALL_CFLAGS) $< -o $@ -all:: $(PROGS) $(MANPAGES) +all:: $(PROGS) $(TEST_PROGS) $(MANPAGES) resize2fs: $(RESIZE_OBJS) $(DEPLIBS) $(CC) $(ALL_LDFLAGS) -o resize2fs $(RESIZE_OBJS) $(LIBS) @@ -35,6 +42,9 @@ resize2fs.8: $(SUBSTITUTE) $(srcdir)/resize2fs.8.in -$(CHMOD) +x $(SUBSTITUTE) $(SUBSTITUTE) $(srcdir)/resize2fs.8.in resize2fs.8 +test_extent: $(TEST_EXTENT_OBJS) + $(CC) $(ALL_LDFLAGS) -o test_extent $(TEST_EXTENT_OBJS) $(LIBS) + installdirs: $(top_srcdir)/mkinstalldirs $(DESTDIR)$(usbindir) \ $(DESTDIR)$(man8dir) $(DESTDIR)$(cat8dir) @@ -56,13 +66,52 @@ uninstall: $(RM) -f $(man8dir)/$$i; \ done +test_extent.out: test_extent $(srcdir)/test_extent.in + ./test_extent < $(srcdir)/test_extent.in > test_extent.out + +check: test_extent.out + @if cmp -s test_extent.out $(srcdir)/test_extent.in ; then \ + echo "Test succeeded." ; \ + else \ + echo "Test failed!" ; \ + diff test_extent.out $(srcdir)/test_extent.in ; \ + exit 1 ; \ + fi + clean: - $(RM) -f $(PROGS) $(MANPAGES) \#* *.s *.o *.a *~ core + $(RM) -f $(PROGS) $(TEST_PROGS) $(MANPAGES) \#* *.s *.o *.a *~ core \ + test_extent.out mostlyclean: clean distclean: clean $(RM) -f .depend Makefile +# +# Kludge to create a "special" e2fsprogs distribution file. +# + +SRCROOT = `echo e2fsprogs-@E2FSPROGS_VERSION@ | sed -e 's/-WIP//' \ + -e 's/pre-//' -e 's/-PLUS//'` +TAR=tar + +$(top_srcdir)/.exclude-file: + (cd $(top_srcdir)/.. ; find e2fsprogs \( -name \*~ -o -name \*.orig \ + -o -name CVS -o -name \*.rej \) -print \ + > .exclude-file) + echo "$(SRCROOT)/build" >> $(top_srcdir)/.exclude-file + echo "$(SRCROOT)/rpm.log" >> $(top_srcdir)/.exclude-file + echo "$(SRCROOT)/.exclude-file" >> $(top_srcdir)/.exclude-file + echo $(SRCROOT)/e2fsprogs-@E2FSPROGS_VERSION@.tar.gz \ + >> $(top_srcdir)/.exclude-file + +source_tar_file: $(top_srcdir)/.exclude-file + (cd $(top_srcdir)/..; a=$(SRCROOT); rm -f $$a ; ln -sf e2fsprogs $$a ; \ + $(TAR) -c -h -v -f - \ + -X $$a/.exclude-file $$a | \ + gzip -9 > e2fsprogs-@E2FSPROGS_VERSION@.tar.gz) + rm -f $(top_srcdir)/.exclude-file + + # +++ Dependency line eater +++ # # Makefile dependencies follow. This must be the last section in diff --git a/resize/NOTES b/resize/NOTES index e04d1803..ccbf8103 100644 --- a/resize/NOTES +++ b/resize/NOTES @@ -1,8 +1,2 @@ TODO -*) Inode table relocation - -*) Inode relocation - -*) Summary information collection - diff --git a/resize/ext2_block_move.c b/resize/ext2_block_move.c new file mode 100644 index 00000000..ec60edf7 --- /dev/null +++ b/resize/ext2_block_move.c @@ -0,0 +1,230 @@ +/* + * ext2_block_move.c --- ext2resizer block mover + * + * Copyright (C) 1997 Theodore Ts'o + * + * %Begin-Header% + * All rights reserved. + * %End-Header% + */ + +#include "resize2fs.h" + +struct process_block_struct { + ino_t ino; + struct ext2_inode * inode; + ext2_extent bmap; + errcode_t error; + int is_dir; + int flags; +}; + +static int process_block(ext2_filsys fs, blk_t *block_nr, + int blockcnt, blk_t ref_block, + int ref_offset, void *private) +{ + struct process_block_struct *pb = private; + errcode_t retval; + blk_t block, new; + int ret = 0; + + block = *block_nr; + + new = ext2fs_extent_translate(pb->bmap, block); + if (new) { + *block_nr = new; + ret |= BLOCK_CHANGED; + if (pb->flags & RESIZE_DEBUG_BMOVE) + printf("ino=%ld, blockcnt=%d, %u->%u\n", pb->ino, + blockcnt, block, new); + } + + if (pb->is_dir) { + retval = ext2fs_add_dir_block(fs->dblist, pb->ino, + *block_nr, blockcnt); + if (retval) { + pb->error = retval; + ret |= BLOCK_ABORT; + } + } + + return ret; +} + +errcode_t ext2fs_block_move(ext2_resize_t rfs) +{ + ext2_extent bmap; + blk_t blk, old, new; + ext2_filsys fs = rfs->new_fs; + ext2_filsys old_fs = rfs->old_fs; + ino_t ino; + struct ext2_inode inode; + errcode_t retval; + struct process_block_struct pb; + ext2_inode_scan scan = 0; + char *block_buf; + int size, c; + int to_move, moved; + ext2_sim_progmeter progress = 0; + + new = fs->super->s_first_data_block; + if (!rfs->itable_buf) { + rfs->itable_buf = malloc(fs->blocksize * + fs->inode_blocks_per_group); + if (!rfs->itable_buf) + return ENOMEM; + } + retval = ext2fs_create_extent_table(&bmap, 0); + if (retval) + return retval; + + /* + * The first step is to figure out where all of the blocks + * will go. + */ + to_move = moved = 0; + for (blk = old_fs->super->s_first_data_block; + blk < old_fs->super->s_blocks_count; blk++) { + if (!ext2fs_test_block_bitmap(old_fs->block_map, blk)) + continue; + if (!ext2fs_test_block_bitmap(rfs->move_blocks, blk)) + continue; + + while (1) { + if (new >= fs->super->s_blocks_count) { + retval = ENOSPC; + goto errout; + } + if (!ext2fs_test_block_bitmap(fs->block_map, new) && + !ext2fs_test_block_bitmap(rfs->reserve_blocks, + new)) + break; + new++; + } + ext2fs_mark_block_bitmap(fs->block_map, new); + ext2fs_add_extent_entry(bmap, blk, new); + to_move++; + } + if (to_move == 0) + return 0; + /* + * Step two is to actually move the blocks + */ + retval = ext2fs_iterate_extent(bmap, 0, 0, 0); + if (retval) goto errout; + + if (rfs->flags & RESIZE_PERCENT_COMPLETE) { + retval = ext2fs_progress_init(&progress, + "Relocating blocks", 30, 40, to_move, 0); + if (retval) + return retval; + } + + while (1) { + retval = ext2fs_iterate_extent(bmap, &old, &new, &size); + if (retval) goto errout; + if (!size) + break; + if (rfs->flags & RESIZE_DEBUG_BMOVE) + printf("Moving %d blocks %u->%u\n", size, + old, new); + do { + c = size; + if (c > fs->inode_blocks_per_group) + c = fs->inode_blocks_per_group; + retval = io_channel_read_blk(fs->io, old, c, + rfs->itable_buf); + if (retval) goto errout; + retval = io_channel_write_blk(fs->io, new, c, + rfs->itable_buf); + if (retval) goto errout; + size -= c; + new += c; + old += c; + moved += c; + io_channel_flush(fs->io); + if (progress) + ext2fs_progress_update(progress, moved); + } while (size > 0); + io_channel_flush(fs->io); + } + if (progress) { + ext2fs_progress_close(progress); + progress = 0; + } + + /* + * Step 3 is where we update the block pointers + */ + retval = ext2fs_open_inode_scan(old_fs, 0, &scan); + if (retval) goto errout; + + pb.error = 0; + pb.bmap = bmap; + pb.flags = rfs->flags; + + block_buf = malloc(old_fs->blocksize * 3); + if (!block_buf) { + retval = ENOMEM; + goto errout; + } + + /* + * We're going to initialize the dblist while we're at it. + */ + if (old_fs->dblist) { + ext2fs_free_dblist(old_fs->dblist); + old_fs->dblist = NULL; + } + retval = ext2fs_init_dblist(old_fs, 0); + if (retval) + return retval; + + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval) goto errout; + + if (rfs->flags & RESIZE_PERCENT_COMPLETE) { + retval = ext2fs_progress_init(&progress, + "Updating block references", 30, 40, + old_fs->super->s_inodes_count, 0); + if (retval) + return retval; + } + + while (ino) { + if ((inode.i_links_count == 0) || + !ext2fs_inode_has_valid_blocks(&inode)) + goto next; + + pb.ino = ino; + pb.inode = &inode; + + pb.is_dir = LINUX_S_ISDIR(inode.i_mode); + + retval = ext2fs_block_iterate2(old_fs, ino, 0, block_buf, + process_block, &pb); + if (retval) + goto errout; + if (pb.error) { + retval = pb.error; + goto errout; + } + + next: + if (progress) + ext2fs_progress_update(progress, ino); + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) + goto next; + } + retval = 0; +errout: + if (progress) + ext2fs_progress_close(progress); + + ext2fs_free_extent_table(bmap); + if (scan) + ext2fs_close_inode_scan(scan); + return retval; +} + diff --git a/resize/ext2_inode_move.c b/resize/ext2_inode_move.c new file mode 100644 index 00000000..5d26b153 --- /dev/null +++ b/resize/ext2_inode_move.c @@ -0,0 +1,300 @@ +/* + * ext2_inode_move.c --- ext2resizer inode mover + * + * Copyright (C) 1997 Theodore Ts'o + * + * %Begin-Header% + * All rights reserved. + * %End-Header% + */ + +#include "resize2fs.h" + +/* + * Progress callback + */ +struct callback_info { + ext2_sim_progmeter progress; + int offset; +}; + +static errcode_t progress_callback(ext2_filsys fs, ext2_inode_scan scan, + dgrp_t group, void * private) +{ + struct callback_info *cb = private; + + if (!cb->progress) + return 0; + + ext2fs_progress_update(cb->progress, group - cb->offset + 1); + return 0; +} + + +struct istruct { + ext2_sim_progmeter progress; + ext2_extent imap; + int flags; + int num; +}; + +static int check_and_change_inodes(ino_t dir, int entry, + struct ext2_dir_entry *dirent, int offset, + int blocksize, char *buf, void *private) +{ + struct istruct *is = private; + ino_t new; + + if (is->progress && offset == 0) { + ext2fs_progress_update(is->progress, ++is->num); + } + + if (!dirent->inode) + return 0; + + new = ext2fs_extent_translate(is->imap, dirent->inode); + + if (!new) + return 0; + if (is->flags & RESIZE_DEBUG_INODEMAP) + printf("Inode translate (dir=%ld, name=%.*s, %u->%ld)\n", + dir, dirent->name_len, dirent->name, + dirent->inode, new); + + dirent->inode = new; + + return DIRENT_CHANGED; +} + +/* + * Function to obtain the dblist information (if we didn't get it + * earlier) + */ +struct process_block_struct { + ino_t ino; + struct ext2_inode * inode; + errcode_t error; +}; + +static int process_block(ext2_filsys fs, blk_t *block_nr, + int blockcnt, blk_t ref_block, + int ref_offset, void *private) +{ + struct process_block_struct *pb = private; + errcode_t retval; + int ret = 0; + + retval = ext2fs_add_dir_block(fs->dblist, pb->ino, + *block_nr, blockcnt); + if (retval) { + pb->error = retval; + ret |= BLOCK_ABORT; + } + return ret; +} + +static errcode_t get_dblist(ext2_filsys fs, int flags) +{ + ext2_inode_scan scan = 0; + errcode_t retval; + char *block_buf; + struct process_block_struct pb; + ext2_sim_progmeter progress = 0; + ino_t ino; + struct ext2_inode inode; + + retval = ext2fs_open_inode_scan(fs, 0, &scan); + if (retval) goto errout; + + pb.error = 0; + + block_buf = malloc(fs->blocksize * 3); + if (!block_buf) { + retval = ENOMEM; + goto errout; + } + + /* + * We're going to initialize the dblist while we're at it. + */ + if (fs->dblist) { + ext2fs_free_dblist(fs->dblist); + fs->dblist = NULL; + } + retval = ext2fs_init_dblist(fs, 0); + if (retval) + return retval; + + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval) goto errout; + + if (flags & RESIZE_PERCENT_COMPLETE) { + retval = ext2fs_progress_init(&progress, + "Finding directories", 30, 40, + fs->super->s_inodes_count, 0); + if (retval) + return retval; + } + + while (ino) { + if ((inode.i_links_count == 0) || + !ext2fs_inode_has_valid_blocks(&inode) || + !LINUX_S_ISDIR(inode.i_mode)) + goto next; + + pb.ino = ino; + pb.inode = &inode; + + retval = ext2fs_block_iterate2(fs, ino, 0, block_buf, + process_block, &pb); + if (retval) + goto errout; + if (pb.error) { + retval = pb.error; + goto errout; + } + + next: + if (progress) + ext2fs_progress_update(progress, ino); + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) + goto next; + } + retval = 0; + +errout: + if (progress) + ext2fs_progress_close(progress); + if (scan) + ext2fs_close_inode_scan(scan); + return retval; +} + + +errcode_t ext2fs_inode_move(ext2_resize_t rfs) +{ + ino_t ino, new; + struct ext2_inode inode; + ext2_inode_scan scan = NULL; + ext2_extent imap; + errcode_t retval; + int group; + struct istruct is; + struct callback_info callback_info; + ext2_sim_progmeter progress = 0; + + if (rfs->old_fs->group_desc_count <= + rfs->new_fs->group_desc_count) + return 0; + + retval = ext2fs_create_extent_table(&imap, 0); + if (retval) + return retval; + + retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan); + if (retval) goto errout; + + retval = ext2fs_inode_scan_goto_blockgroup(scan, + rfs->new_fs->group_desc_count); + if (retval) goto errout; + + + if (rfs->flags & RESIZE_PERCENT_COMPLETE) { + callback_info.offset = rfs->new_fs->group_desc_count; + + group = (rfs->old_fs->group_desc_count - + rfs->new_fs->group_desc_count); + + retval = ext2fs_progress_init(&progress, + "Moving inodes", 30, 40, group, 0); + if (retval) + return retval; + ext2fs_set_inode_callback(scan, progress_callback, + &callback_info); + } + callback_info.progress = progress; + + new = EXT2_FIRST_INODE(rfs->new_fs->super); + /* + * First, copy all of the inodes that need to be moved + * elsewhere in the inode table + */ + while (1) { + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval) goto errout; + + if (!ino) + break; + + if (!ext2fs_test_inode_bitmap(rfs->old_fs->inode_map, ino)) + continue; + + /* + * Find a new inode + */ + while (1) { + if (!ext2fs_test_inode_bitmap(rfs->new_fs->inode_map, + new)) + break; + new++; + if (new > rfs->new_fs->super->s_inodes_count) { + retval = ENOSPC; + goto errout; + } + } + ext2fs_mark_inode_bitmap(rfs->new_fs->inode_map, new); + retval = ext2fs_write_inode(rfs->old_fs, new, &inode); + if (retval) goto errout; + + group = (new-1) / EXT2_INODES_PER_GROUP(rfs->new_fs->super); + if (LINUX_S_ISDIR(inode.i_mode)) + rfs->new_fs->group_desc[group].bg_used_dirs_count++; + + if (rfs->flags & RESIZE_DEBUG_INODEMAP) + printf("Inode moved %ld->%ld\n", ino, new); + + ext2fs_add_extent_entry(imap, ino, new); + } + io_channel_flush(rfs->new_fs->io); + if (progress) { + ext2fs_progress_close(progress); + progress = 0; + } + /* + * Get the list of directory blocks, if necessary + */ + if (!rfs->old_fs->dblist) { + retval = get_dblist(rfs->old_fs, rfs->flags); + if (retval) goto errout; + } + /* + * Now, we iterate over all of the directories to update the + * inode references + */ + if (rfs->flags & RESIZE_PERCENT_COMPLETE) { + retval = ext2fs_progress_init(&progress, + "Updating inode references", 30, 40, + ext2fs_dblist_count(rfs->old_fs->dblist), 0); + if (retval) + return retval; + } + is.imap = imap; + is.flags = rfs->flags; + is.num = 0; + is.progress = progress; + + retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist, + DIRENT_FLAG_INCLUDE_EMPTY, 0, + check_and_change_inodes, &is); + /* if (retval) goto errout; */ + +errout: + if (progress) + ext2fs_progress_close(progress); + ext2fs_free_extent_table(imap); + if (scan) + ext2fs_close_inode_scan(scan); + return retval; +} + diff --git a/resize/extent.c b/resize/extent.c new file mode 100644 index 00000000..0f0ef4cd --- /dev/null +++ b/resize/extent.c @@ -0,0 +1,224 @@ +/* + * extent.c --- ext2 extent abstraction + * + * This abstraction is used to provide a compact way of representing a + * translation table, for moving multiple contiguous ranges (extents) + * of blocks or inodes. + * + * Copyright (C) 1997 Theodore Ts'o + * + * %Begin-Header% + * All rights reserved. + * %End-Header% + */ + +#include "resize2fs.h" + +struct ext2_extent_entry { + __u32 old, new; + int size; +}; + +struct _ext2_extent { + struct ext2_extent_entry *list; + int cursor; + int size; + int num; + int sorted; +}; + +/* + * Create an extent table + */ +errcode_t ext2fs_create_extent_table(ext2_extent *ret_extent, int size) +{ + ext2_extent new; + + new = malloc(sizeof(struct _ext2_extent)); + if (!new) + return ENOMEM; + memset(new, 0, sizeof(struct _ext2_extent)); + + new->size = size ? size : 50; + new->cursor = 0; + new->num = 0; + new->sorted = 1; + + new->list = malloc(sizeof(struct ext2_extent_entry) * new->size); + if (!new->list) { + free(new); + return ENOMEM; + } + memset(new->list, 0, sizeof(struct ext2_extent_entry) * new->size); + *ret_extent = new; + return 0; +} + +/* + * Free an extent table + */ +void ext2fs_free_extent_table(ext2_extent extent) +{ + if (extent->list) + free(extent->list); + extent->list = 0; + extent->size = 0; + extent->num = 0; + free(extent); +} + +/* + * Add an entry to the extent table + */ +errcode_t ext2fs_add_extent_entry(ext2_extent extent, __u32 old, __u32 new) +{ + struct ext2_extent_entry *p; + int newsize; + int curr; + struct ext2_extent_entry *ent; + + if (extent->num >= extent->size) { + newsize = extent->size + 100; + p = realloc(extent->list, + sizeof(struct ext2_extent_entry) * newsize); + if (!p) + return ENOMEM; + extent->list = p; + extent->size = newsize; + } + curr = extent->num; + ent = extent->list + curr; + if (curr) { + /* + * Check to see if this can be coalesced with the last + * extent + */ + ent--; + if ((ent->old + ent->size == old) && + (ent->new + ent->size == new)) { + ent->size++; + return 0; + } + /* + * Now see if we're going to ruin the sorting + */ + if (ent->old + ent->size > old) + extent->sorted = 0; + ent++; + } + ent->old = old; + ent->new = new; + ent->size = 1; + extent->num++; + return 0; +} + +/* + * Helper function for qsort + */ +static int extent_cmp(const void *a, const void *b) +{ + const struct ext2_extent_entry *db_a = a; + const struct ext2_extent_entry *db_b = b; + + return (db_a->old - db_b->old); +} + +/* + * Given an inode map and inode number, look up the old inode number + * and return the new inode number + */ +__u32 ext2fs_extent_translate(ext2_extent extent, __u32 old) +{ + int low, high, mid; + ino_t lowval, highval; + float range; + + if (!extent->sorted) { + qsort(extent->list, extent->num, + sizeof(struct ext2_extent_entry), extent_cmp); + extent->sorted = 1; + } + low = 0; + high = extent->num-1; + while (low <= high) { +#if 0 + mid = (low+high)/2; +#else + if (low == high) + mid = low; + else { + /* Interpolate for efficiency */ + lowval = extent->list[low].old; + highval = extent->list[high].old; + + if (old < lowval) + range = 0; + else if (old > highval) + range = 1; + else + range = ((float) (old - lowval)) / + (highval - lowval); + mid = low + ((int) (range * (high-low))); + } +#endif + if ((old >= extent->list[mid].old) && + (old < extent->list[mid].old + extent->list[mid].size)) + return (extent->list[mid].new + + (old - extent->list[mid].old)); + if (old < extent->list[mid].old) + high = mid-1; + else + low = mid+1; + } + return 0; +} + +/* + * For debugging only + */ +void ext2fs_extent_dump(ext2_extent extent, FILE *out) +{ + int i; + struct ext2_extent_entry *ent; + + fputs("# Extent dump:\n", out); + fprintf(out, "#\tNum=%d, Size=%d, Cursor=%d, Sorted=%d\n", + extent->num, extent->size, extent->cursor, extent->sorted); + for (i=0, ent=extent->list; i < extent->num; i++, ent++) { + fprintf(out, "#\t\t %u -> %u (%d)\n", ent->old, + ent->new, ent->size); + } +} + +/* + * Iterate over the contents of the extent table + */ +errcode_t ext2fs_iterate_extent(ext2_extent extent, __u32 *old, + __u32 *new, int *size) +{ + struct ext2_extent_entry *ent; + + if (!old) { + extent->cursor = 0; + return 0; + } + + if (extent->cursor >= extent->num) { + *old = 0; + *new = 0; + *size = 0; + return 0; + } + + ent = extent->list + extent->cursor++; + + *old = ent->old; + *new = ent->new; + *size = ent->size; + return 0; +} + + + + diff --git a/resize/inodemap.c b/resize/inodemap.c deleted file mode 100644 index 8f419e6c..00000000 --- a/resize/inodemap.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * inodemap.c --- ext2resizer indoe mapper - * - * Copyright (C) 1997 Theodore Ts'o - * - * %Begin-Header% - * All rights reserved. - * %End-Header% - */ - -#include "resize2fs.h" - -struct inode_map_entry { - ino_t old, new; -}; - -struct inode_map_struct { - struct inode_map_entry *list; - int size; - int num; - int sorted; -}; - -typedef struct inode_map_struct *inode_map; - -/* - * Create inode map table - */ -static errcode_t create_inode_map(inode_map *imap, int size) -{ - inode_map new; - - new = malloc(sizeof(struct inode_map_struct)); - if (!new) - return ENOMEM; - memset(new, 0, sizeof(struct inode_map_struct)); - - new->size = size ? size : 50; - new->num = 0; - new->sorted = 1; - - new->list = malloc(sizeof(struct inode_map_struct) * new->size); - if (!new->list) { - free(new); - return ENOMEM; - } - *imap = new; - return 0; -} - -/* - * Add an entry to the inode table map - */ -static errcode_t add_inode_map_entry(inode_map imap, ino_t old, ino_t new) -{ - struct inode_map_entry *p; - int newsize; - - if (imap->num >= imap->size) { - newsize = imap->size + 100; - p = realloc(imap->list, - sizeof(struct inode_map_struct) * newsize); - if (!p) - return ENOMEM; - imap->list = p; - imap->size = newsize; - } - if (imap->num) { - if (imap->list[imap->num-1].old > old) - imap->sorted = 0; - } - imap->list[imap->num].old = old; - imap->list[imap->num].new = new; - imap->num++; - return 0; -} - -/* - * Helper function for qsort - */ -static int inode_map_cmp(const void *a, const void *b) -{ - const struct inode_map_entry *db_a = - (const struct inode_map_entry *) a; - const struct inode_map_entry *db_b = - (const struct inode_map_entry *) b; - - return (db_a->old - db_b->old); -} - -/* - * Given an inode map and inode number, look up the old inode number - * and return the new inode number - */ -static ino_t inode_map_translate(inode_map imap, ino_t old) -{ - int low, high, mid; - ino_t lowval, highval; - float range; - - if (!imap->sorted) { - qsort(imap->list, imap->num, - sizeof(struct inode_map_entry), inode_map_cmp); - imap->sorted = 1; - } - low = 0; - high = imap->num-1; - while (low <= high) { -#if 0 - mid = (low+high)/2; -#else - if (low == high) - mid = low; - else { - /* Interpolate for efficiency */ - lowval = imap->list[low].old; - highval = imap->list[high].old; - - if (old < lowval) - range = 0; - else if (old > highval) - range = 1; - else - range = ((float) (old - lowval)) / - (highval - lowval); - mid = low + ((int) (range * (high-low))); - } -#endif - if (old == imap->list[mid].old) - return imap->list[mid].new; - if (old < imap->list[mid].old) - high = mid-1; - else - low = mid+1; - } - return 0; -} - -struct istruct { - inode_map imap; - int flags; -}; - -int check_and_change_inodes(ino_t dir, int entry, - struct ext2_dir_entry *dirent, int offset, - int blocksize, char *buf, void *private) -{ - struct istruct *is = private; - ino_t new; - - if (!dirent->inode) - return 0; - - new = inode_map_translate(is->imap, dirent->inode); - - if (!new) - return 0; - if (is->flags & RESIZE_DEBUG_INODEMAP) - printf("Inode translate (dir=%ld, name=%.*s, %ld->%ld)\n", - dir, dirent->name_len, dirent->name, - dirent->inode, new); - - dirent->inode = new; - - return DIRENT_CHANGED; -} - -errcode_t ext2fs_inode_move(ext2_resize_t rfs) -{ - ino_t ino, start, end, new; - struct ext2_inode inode; - ext2_inode_scan scan; - inode_map imap; - errcode_t retval; - int group; - struct istruct is; - - if (rfs->old_fs->group_desc_count <= - rfs->new_fs->group_desc_count) - return 0; - - retval = create_inode_map(&imap, 0); - if (retval) - return retval; - - retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan); - if (retval) - return retval; - - retval = ext2fs_inode_scan_goto_blockgroup(scan, - rfs->new_fs->group_desc_count); - if (retval) { - ext2fs_close_inode_scan(scan); - return retval; - } - - new = EXT2_FIRST_INODE(rfs->new_fs->super); - - /* - * First, copy all of the inodes that need to be moved - * elsewhere in the inode table - */ - while (1) { - retval = ext2fs_get_next_inode(scan, &ino, &inode); - if (retval) - return retval; - if (!ino) - break; - - if (!ext2fs_test_inode_bitmap(rfs->old_fs->inode_map, ino)) - continue; - - /* - * Find a new inode - */ - while (1) { - if (!ext2fs_test_inode_bitmap(rfs->new_fs->inode_map, - new)) - break; - new++; - if (new > rfs->new_fs->super->s_inodes_count) - return ENOSPC; - } - ext2fs_mark_inode_bitmap(rfs->new_fs->inode_map, new); - retval = ext2fs_write_inode(rfs->old_fs, new, &inode); - if (retval) - return retval; - group = (new-1) / EXT2_INODES_PER_GROUP(rfs->new_fs->super); - if (LINUX_S_ISDIR(inode.i_mode)) - rfs->new_fs->group_desc[group].bg_used_dirs_count++; - - if (rfs->flags & RESIZE_DEBUG_INODEMAP) - printf("Inode moved %ld->%ld\n", ino, new); - - add_inode_map_entry(imap, ino, new); - } - /* - * Now, we iterate over all of the directories to update the - * inode references - */ - is.imap = imap; - is.flags = rfs->flags; - retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist, 0, 0, - check_and_change_inodes, &is); - if (retval) - return retval; - - return 0; -} - diff --git a/resize/main.c b/resize/main.c index 3d3c589f..b5e5a7fc 100644 --- a/resize/main.c +++ b/resize/main.c @@ -8,6 +8,13 @@ * %End-Header% */ +#ifdef HAVE_GETOPT_H +#include +#endif +#include +#include + + #include "resize2fs.h" #define E2FSPROGS_VERSION "1.10" @@ -15,9 +22,9 @@ char *program_name, *device_name; -static volatile void usage (char *program_name) +static volatile void usage (char *prog) { - fprintf (stderr, "usage: %s device new-size\n", program_name); + fprintf (stderr, "usage: %s [-d debug_flags] [-p] [-F] device new-size\n", prog); exit (1); } @@ -27,6 +34,8 @@ void main (int argc, char ** argv) ext2_filsys fs; int c; int flags = 0; + int flush = 0; + int fd; blk_t new_size; io_manager io_ptr; @@ -36,11 +45,14 @@ void main (int argc, char ** argv) if (argc && *argv) program_name = *argv; - while ((c = getopt (argc, argv, "d:hp")) != EOF) { + while ((c = getopt (argc, argv, "d:Fhp")) != EOF) { switch (c) { case 'h': usage(program_name); break; + case 'F': + flush = 1; + break; case 'd': flags |= atoi(optarg); break; @@ -57,6 +69,27 @@ void main (int argc, char ** argv) new_size = atoi(argv[optind++]); initialize_ext2_error_table(); + if (flush) { +#ifdef BLKFLSBUF + fd = open(device_name, O_RDONLY, 0); + + if (fd < 0) { + com_err("open", errno, "while opening %s for flushing", + device_name); + exit(1); + } + if (ioctl(fd, BLKFLSBUF, 0) < 0) { + com_err("BLKFLSBUF", errno, "while trying to flush %s", + device_name); + exit(1); + } + close(fd); +#else + fprintf(stderr, "BLKFLSBUF not supported"); + exit(1); +#endif /* BLKFLSBUF */ + } + if (flags & RESIZE_DEBUG_IO) { io_ptr = test_io_manager; test_io_backing_manager = unix_io_manager; diff --git a/resize/resize2fs.8.in b/resize/resize2fs.8.in index ee2c9336..ac2c7fd9 100644 --- a/resize/resize2fs.8.in +++ b/resize/resize2fs.8.in @@ -7,23 +7,56 @@ resize2fs \- ext2 file system resizer .SH SYNOPSIS .B resize2fs [ -device +.B \-d +.I debug-flags ] +[ +.B \-p +] +[ +.B \-F +] +.I device +.I size .SH DESCRIPTION The -.B resize2fs +.B resize2fs program will resize ext2 file systems. It can be used to enlarge or -shrink an ext2 file system. -.br +shrink an ext2 file system so that it will have +.I size +blocks. +The device containing the ext2 filesystem is specified by the .I device -is the special file corresponding to the device containing the ext2 -file system (e.g /dev/hdXX). +parameter. .SH OPTIONS .TP -.I -h -Displays a help message. +.I \-d debug-flags +Turns on various resize2fs debugging features. +.I debug-flags +should be computed by adding the numbers of the desired features +from the following list: +.br +\ 1\ \-\ Print out all disk I/O +.br +\ 2\ \-\ Debug block relocations +.br +\ 8\ \-\ Debug inode relocations +.br +\ 16\ \-\ Debug moving the inode table +.TP +.I \-p +Prints out a percentage completion bars for each +.B resize2fs +operation, so that the user can keep track of what +the program is doing. +.TP +.I \-F +Flush the filesystem device's buffer caches before beginning. Only +really useful for doing +.B resize2fs +time trials. .SH AUTHOR -.B debugfs +.B resize2fs was written by Theodore Ts'o . .SH SEE ALSO .BR dumpe2fs (8), diff --git a/resize/resize2fs.c b/resize/resize2fs.c index b90c0759..66e0b20e 100644 --- a/resize/resize2fs.c +++ b/resize/resize2fs.c @@ -29,13 +29,14 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk_t new_size) { ext2_filsys fs; int overhead = 0; - int rem; + int rem, adj = 0; errcode_t retval; ino_t real_end; blk_t blk, group_block; unsigned long i, j; struct ext2_group_desc *new; int old_numblocks, numblocks, adjblocks; + ext2_sim_progmeter progress = 0; fs = rfs->new_fs; fs->super->s_blocks_count = new_size; @@ -95,14 +96,21 @@ retry: fs->super->s_free_blocks_count += (fs->super->s_blocks_count - blk); + /* + * Adjust the number of reserved blocks + */ + blk = rfs->old_fs->super->s_r_blocks_count * 100 / + rfs->old_fs->super->s_blocks_count; + fs->super->s_r_blocks_count = ((fs->super->s_blocks_count * blk) + / 100); + /* * Adjust the bitmaps for size */ retval = ext2fs_resize_inode_bitmap(fs->super->s_inodes_count, fs->super->s_inodes_count, fs->inode_map); - if (retval) - return retval; + if (retval) goto errout; real_end = ((EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count)) - 1 + @@ -110,8 +118,7 @@ retry: retval = ext2fs_resize_block_bitmap(fs->super->s_blocks_count-1, real_end, fs->block_map); - if (retval) - return retval; + if (retval) goto errout; /* * Reallocate the group descriptors as necessary. @@ -127,8 +134,10 @@ retry: /* * Fix the count of the last (old) block group */ - if (rfs->old_fs->group_desc_count > fs->group_desc_count) - return 0; + if (rfs->old_fs->group_desc_count > fs->group_desc_count) { + retval = 0; + goto errout; + } old_numblocks = (rfs->old_fs->super->s_blocks_count - rfs->old_fs->super->s_first_data_block) % rfs->old_fs->super->s_blocks_per_group; @@ -148,14 +157,26 @@ retry: /* * Initialize the new block group descriptors */ - if (rfs->old_fs->group_desc_count >= fs->group_desc_count) - return 0; + if (rfs->old_fs->group_desc_count >= fs->group_desc_count) { + retval = 0; + goto errout; + } rfs->itable_buf = malloc(fs->blocksize * fs->inode_blocks_per_group); - if (!rfs->itable_buf) - return ENOMEM; + if (!rfs->itable_buf) { + retval = ENOMEM; + goto errout; + } memset(rfs->itable_buf, 0, fs->blocksize * fs->inode_blocks_per_group); group_block = fs->super->s_first_data_block + rfs->old_fs->group_desc_count * fs->super->s_blocks_per_group; + + if (rfs->flags & RESIZE_PERCENT_COMPLETE) { + adj = rfs->old_fs->group_desc_count; + retval = ext2fs_progress_init(&progress, + "Initializing inode table", 30, 40, + fs->group_desc_count - adj, 0); + if (retval) goto errout; + } for (i = rfs->old_fs->group_desc_count; i < fs->group_desc_count; i++) { memset(&fs->group_desc[i], 0, @@ -189,8 +210,7 @@ retry: fs->group_desc[i].bg_used_dirs_count = 0; retval = ext2fs_allocate_group_table(fs, i, 0); - if (retval) - return retval; + if (retval) goto errout; /* * Write out the new inode table @@ -199,54 +219,155 @@ retry: fs->group_desc[i].bg_inode_table, fs->inode_blocks_per_group, rfs->itable_buf); - if (retval) - return retval; + if (retval) goto errout; + + /* io_channel_flush(fs->io); */ + if (progress) + ext2fs_progress_update(progress, i - adj + 1); group_block += fs->super->s_blocks_per_group; } + io_channel_flush(fs->io); + retval = 0; + +errout: + if (progress) + ext2fs_progress_close(progress); + return retval; +} + +/* + * This helper function creates a block bitmap with all of the + * filesystem meta-data blocks. + */ +static errcode_t mark_table_blocks(ext2_filsys fs, + ext2fs_block_bitmap *ret_bmap) +{ + blk_t block, b; + int i,j; + ext2fs_block_bitmap bmap; + errcode_t retval; + + retval = ext2fs_allocate_block_bitmap(fs, "meta-data blocks", &bmap); + if (retval) + return retval; + + block = fs->super->s_first_data_block; + for (i = 0; i < fs->group_desc_count; i++) { + if (ext2fs_bg_has_super(fs, i)) { + /* + * Mark this group's copy of the superblock + */ + ext2fs_mark_block_bitmap(bmap, block); + + /* + * Mark this group's copy of the descriptors + */ + for (j = 0; j < fs->desc_blocks; j++) + ext2fs_mark_block_bitmap(bmap, block + j + 1); + } + + /* + * Mark the blocks used for the inode table + */ + for (j = 0, b = fs->group_desc[i].bg_inode_table; + j < fs->inode_blocks_per_group; + j++, b++) + ext2fs_mark_block_bitmap(bmap, b); + + /* + * Mark block used for the block bitmap + */ + ext2fs_mark_block_bitmap(bmap, + fs->group_desc[i].bg_block_bitmap); + /* + * Mark block used for the inode bitmap + */ + ext2fs_mark_block_bitmap(bmap, + fs->group_desc[i].bg_inode_bitmap); + block += fs->super->s_blocks_per_group; + } + *ret_bmap = bmap; return 0; } + + +/* + * Some helper CPP macros + */ +#define FS_BLOCK_BM(fs, i) ((fs)->group_desc[(i)].bg_block_bitmap) +#define FS_INODE_BM(fs, i) ((fs)->group_desc[(i)].bg_inode_bitmap) +#define FS_INODE_TB(fs, i) ((fs)->group_desc[(i)].bg_inode_table) + +#define IS_BLOCK_BM(fs, i, blk) ((blk) == FS_BLOCK_BM((fs),(i))) +#define IS_INODE_BM(fs, i, blk) ((blk) == FS_INODE_BM((fs),(i))) + +#define IS_INODE_TB(fs, i, blk) (((blk) >= FS_INODE_TB((fs), (i))) && \ + ((blk) < (FS_INODE_TB((fs), (i)) + \ + (fs)->inode_blocks_per_group))) + /* * This routine marks and unmarks reserved blocks in the new block * bitmap. It also determines which blocks need to be moved and * places this information into the move_blocks bitmap. */ -static errcode_t determine_relocations(ext2_resize_t rfs) +static errcode_t blocks_to_move(ext2_resize_t rfs) { - int i, j, max, adj; + int i, j, max; blk_t blk, group_blk; unsigned long old_blocks, new_blocks; errcode_t retval; - ext2_filsys fs = rfs->new_fs; + ext2_filsys fs, old_fs; + ext2fs_block_bitmap meta_bmap; - retval = ext2fs_allocate_block_bitmap(rfs->old_fs, - "blocks to be moved", + fs = rfs->new_fs; + old_fs = rfs->old_fs; + if (old_fs->super->s_blocks_count > fs->super->s_blocks_count) + fs = rfs->old_fs; + + retval = ext2fs_allocate_block_bitmap(fs, "reserved blocks", &rfs->reserve_blocks); if (retval) return retval; + retval = ext2fs_allocate_block_bitmap(fs, "blocks to be moved", + &rfs->move_blocks); + if (retval) + return retval; + + retval = mark_table_blocks(fs, &meta_bmap); + if (retval) + return retval; + + fs = rfs->new_fs; + /* * If we're shrinking the filesystem, we need to move all of * the blocks that don't fit any more */ for (blk = fs->super->s_blocks_count; - blk < rfs->old_fs->super->s_blocks_count; blk++) { - if (ext2fs_test_block_bitmap(rfs->old_fs->block_map, blk)) + blk < old_fs->super->s_blocks_count; blk++) { + if (ext2fs_test_block_bitmap(old_fs->block_map, blk) && + !ext2fs_test_block_bitmap(meta_bmap, blk)) { + ext2fs_mark_block_bitmap(rfs->move_blocks, blk); rfs->needed_blocks++; + } ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk); } - old_blocks = rfs->old_fs->desc_blocks; + old_blocks = old_fs->desc_blocks; new_blocks = fs->desc_blocks; - if (old_blocks == new_blocks) - return 0; + if (old_blocks == new_blocks) { + retval = 0; + goto errout; + } max = fs->group_desc_count; - if (max > rfs->old_fs->group_desc_count) - max = rfs->old_fs->group_desc_count; - group_blk = rfs->old_fs->super->s_first_data_block; + if (max > old_fs->group_desc_count) + max = old_fs->group_desc_count; + group_blk = old_fs->super->s_first_data_block; /* * If we're reducing the number of descriptor blocks, this * makes life easy. :-) We just have to mark some extra @@ -258,15 +379,16 @@ static errcode_t determine_relocations(ext2_resize_t rfs) group_blk += fs->super->s_blocks_per_group; continue; } - for (blk = group_blk+1+old_blocks; - blk < group_blk+1+new_blocks; blk++) { + for (blk = group_blk+1+new_blocks; + blk < group_blk+1+old_blocks; blk++) { ext2fs_unmark_block_bitmap(fs->block_map, blk); rfs->needed_blocks--; } group_blk += fs->super->s_blocks_per_group; } - return 0; + retval = 0; + goto errout; } /* * If we're increasing the number of descriptor blocks, life @@ -283,28 +405,26 @@ static errcode_t determine_relocations(ext2_resize_t rfs) /* * Check to see if we overlap with the inode - * or block bitmap + * or block bitmap, or the inode tables. If + * not, and the block is in use, then mark it + * as a block to be moved. */ - if (blk == fs->group_desc[i].bg_block_bitmap) { - fs->group_desc[i].bg_block_bitmap = 0; + if (IS_BLOCK_BM(fs, i, blk)) { + FS_BLOCK_BM(fs, i) = 0; + rfs->needed_blocks++; + } else if (IS_INODE_BM(fs, i, blk)) { + FS_INODE_BM(fs, i) = 0; + rfs->needed_blocks++; + } else if (IS_INODE_TB(fs, i, blk)) { + FS_INODE_TB(fs, i) = 0; + rfs->needed_blocks++; + } else if (ext2fs_test_block_bitmap(old_fs->block_map, + blk) && + !ext2fs_test_block_bitmap(meta_bmap, blk)) { + ext2fs_mark_block_bitmap(rfs->move_blocks, + blk); rfs->needed_blocks++; } - if (blk == fs->group_desc[i].bg_inode_bitmap) { - fs->group_desc[i].bg_inode_bitmap = 0; - rfs->needed_blocks++; - } - /* - * Check to see if we overlap with the inode - * table - */ - if (blk < fs->group_desc[i].bg_inode_table) - continue; - if (blk >= (fs->group_desc[i].bg_inode_table + - fs->inode_blocks_per_group)) - continue; - blk = fs->group_desc[i].bg_inode_table + - fs->inode_blocks_per_group - 1; - fs->group_desc[i].bg_inode_table = 0; } if (fs->group_desc[i].bg_inode_table && fs->group_desc[i].bg_inode_bitmap && @@ -312,9 +432,8 @@ static errcode_t determine_relocations(ext2_resize_t rfs) goto next_group; /* - * Allocate the missing bitmap and inode table - * structures, passing in rfs->reserve_blocks to - * prevent a conflict. + * Reserve the existing meta blocks that we know + * aren't to be moved. */ if (fs->group_desc[i].bg_block_bitmap) ext2fs_mark_block_bitmap(rfs->reserve_blocks, @@ -328,19 +447,34 @@ static errcode_t determine_relocations(ext2_resize_t rfs) ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk); + /* + * Allocate the missing data structures + */ retval = ext2fs_allocate_group_table(fs, i, rfs->reserve_blocks); if (retval) - return retval; + goto errout; /* - * Now make sure these blocks are reserved in the new - * block bitmap + * For those structures that have changed, we need to + * do bookkeepping. */ - ext2fs_mark_block_bitmap(fs->block_map, - fs->group_desc[i].bg_block_bitmap); - ext2fs_mark_block_bitmap(fs->block_map, - fs->group_desc[i].bg_inode_bitmap); + if (FS_BLOCK_BM(old_fs, i) != + (blk = FS_BLOCK_BM(fs, i))) { + ext2fs_mark_block_bitmap(fs->block_map, blk); + if (ext2fs_test_block_bitmap(old_fs->block_map, blk) && + !ext2fs_test_block_bitmap(meta_bmap, blk)) + ext2fs_mark_block_bitmap(rfs->move_blocks, + blk); + } + if (FS_INODE_BM(old_fs, i) != + (blk = FS_INODE_BM(fs, i))) { + ext2fs_mark_block_bitmap(fs->block_map, blk); + if (ext2fs_test_block_bitmap(old_fs->block_map, blk) && + !ext2fs_test_block_bitmap(meta_bmap, blk)) + ext2fs_mark_block_bitmap(rfs->move_blocks, + blk); + } /* * The inode table, if we need to relocate it, is @@ -349,29 +483,25 @@ static errcode_t determine_relocations(ext2_resize_t rfs) * can't have the inode table be destroyed during the * block relocation phase. */ - adj = fs->group_desc[i].bg_inode_table - - rfs->old_fs->group_desc[i].bg_inode_table; - if (!adj) + if (FS_INODE_TB(fs, i) == FS_INODE_TB(old_fs, i)) goto next_group; /* inode table not moved */ - /* - * Figure out how many blocks we need to have free. - * This takes into account that we need to reserve - * both inode tables, which may be overallping. - */ - if (adj < 0) - adj = -adj; - if (adj > fs->inode_blocks_per_group) - adj = fs->inode_blocks_per_group; - rfs->needed_blocks += fs->inode_blocks_per_group + adj; + rfs->needed_blocks += fs->inode_blocks_per_group; /* * Mark the new inode table as in use in the new block - * allocation bitmap. + * allocation bitmap, and move any blocks that might + * be necessary. */ for (blk = fs->group_desc[i].bg_inode_table, j=0; - j < fs->inode_blocks_per_group ; j++, blk++) + j < fs->inode_blocks_per_group ; j++, blk++) { ext2fs_mark_block_bitmap(fs->block_map, blk); + if (ext2fs_test_block_bitmap(old_fs->block_map, blk) && + !ext2fs_test_block_bitmap(meta_bmap, blk)) + ext2fs_mark_block_bitmap(rfs->move_blocks, + blk); + } + /* * Make sure the old inode table is reserved in the * block reservation bitmap. @@ -383,7 +513,13 @@ static errcode_t determine_relocations(ext2_resize_t rfs) next_group: group_blk += rfs->new_fs->super->s_blocks_per_group; } - return 0; + retval = 0; + +errout: + if (meta_bmap) + ext2fs_free_block_bitmap(meta_bmap); + + return retval; } @@ -393,13 +529,15 @@ static errcode_t determine_relocations(ext2_resize_t rfs) * After this you have to use the rfs->new_fs file handle to read and * write inodes. */ -errcode_t move_itables(ext2_resize_t rfs) +static errcode_t move_itables(ext2_resize_t rfs) { int i, n, num, max, size, diff; ext2_filsys fs = rfs->new_fs; char *cp; blk_t old, new; errcode_t retval, err; + ext2_sim_progmeter progress = 0; + int to_move, moved; max = fs->group_desc_count; if (max > rfs->old_fs->group_desc_count) @@ -411,6 +549,25 @@ errcode_t move_itables(ext2_resize_t rfs) if (!rfs->itable_buf) return ENOMEM; } + + /* + * Figure out how many inode tables we need to move + */ + to_move = moved = 0; + for (i=0; i < max; i++) + if (rfs->old_fs->group_desc[i].bg_inode_table != + fs->group_desc[i].bg_inode_table) + to_move++; + + if (to_move == 0) + return 0; + + if (rfs->flags & RESIZE_PERCENT_COMPLETE) { + retval = ext2fs_progress_init(&progress, + "Moving inode table", 30, 40, to_move, 0); + if (retval) + return retval; + } for (i=0; i < max; i++) { old = rfs->old_fs->group_desc[i].bg_inode_table; @@ -419,7 +576,7 @@ errcode_t move_itables(ext2_resize_t rfs) if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) printf("Itable move group %d block " - "%ld->%ld (diff %d)\n", + "%u->%u (diff %d)\n", i, old, new, diff); if (!diff) @@ -458,20 +615,27 @@ errcode_t move_itables(ext2_resize_t rfs) diff, rfs->itable_buf - fs->blocksize * diff); if (retval) goto backout; - } + } io_channel_flush(fs->io); + if (progress) + ext2fs_progress_update(progress, ++moved); } ext2fs_flush(rfs->new_fs); + io_channel_flush(fs->io); if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) printf("Inode table move finished.\n"); + if (progress) + ext2fs_progress_close(progress); return 0; backout: + if (progress) + ext2fs_progress_close(progress); if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) printf("Error: %s; now backing out!\n", error_message(retval)); while (--i >= 0) { if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) - printf("Group %d block %ld->%ld\n", i, new, old); + printf("Group %d block %u->%u\n", i, new, old); old = rfs->old_fs->group_desc[i].bg_inode_table; new = fs->group_desc[i].bg_inode_table; @@ -554,7 +718,6 @@ errcode_t resize_fs(ext2_filsys fs, blk_t new_size, int flags) { ext2_resize_t rfs; errcode_t retval; - int bmove_flags; retval = ext2fs_read_bitmaps(fs); if (retval) @@ -579,24 +742,17 @@ errcode_t resize_fs(ext2_filsys fs, blk_t new_size, int flags) if (retval) goto errout; - retval = determine_relocations(rfs); + retval = blocks_to_move(rfs); if (retval) goto errout; if (rfs->flags & RESIZE_DEBUG_BMOVE) - printf("Number of free blocks: %d, Needed: %d\n", - fs->super->s_free_blocks_count, rfs->needed_blocks); + printf("Number of free blocks: %d/%d, Needed: %d\n", + rfs->old_fs->super->s_free_blocks_count, + rfs->new_fs->super->s_free_blocks_count, + rfs->needed_blocks); - if (rfs->needed_blocks > fs->super->s_free_blocks_count) { - retval = ENOSPC; - goto errout; - } - - bmove_flags = EXT2_BMOVE_GET_DBLIST; - if (rfs->flags & RESIZE_DEBUG_BMOVE) - bmove_flags |= EXT2_BMOVE_DEBUG; - retval = ext2fs_move_blocks(rfs->old_fs, rfs->reserve_blocks, - rfs->new_fs->block_map, bmove_flags); + retval = ext2fs_block_move(rfs); if (retval) goto errout; diff --git a/resize/resize2fs.h b/resize/resize2fs.h index 28297f69..32ddaa02 100644 --- a/resize/resize2fs.h +++ b/resize/resize2fs.h @@ -31,6 +31,16 @@ #define const #endif +/* + * For the extent map + */ +typedef struct _ext2_extent *ext2_extent; + +/* + * For the simple progress meter + */ +typedef struct ext2_sim_progress *ext2_sim_progmeter; + /* * Flags for the resizer; most are debugging flags only */ @@ -40,6 +50,7 @@ #define RESIZE_DEBUG_ITABLEMOVE 0x0008 #define RESIZE_PERCENT_COMPLETE 0x0100 +#define RESIZE_VERBOSE 0x0200 /* * The core state structure for the ext2 resizer @@ -50,6 +61,7 @@ struct ext2_resize_struct { ext2_filsys new_fs; ext2_brel block_relocate; ext2fs_block_bitmap reserve_blocks; + ext2fs_block_bitmap move_blocks; int needed_blocks; int flags; char *itable_buf; @@ -59,3 +71,27 @@ typedef struct ext2_resize_struct *ext2_resize_t; /* prototypes */ extern errcode_t resize_fs(ext2_filsys fs, blk_t new_size, int flags); +extern errcode_t ext2fs_inode_move(ext2_resize_t rfs); +extern errcode_t ext2fs_block_move(ext2_resize_t rfs); + +/* extent.c */ +extern errcode_t ext2fs_create_extent_table(ext2_extent *ret_extent, + int size); +extern void ext2fs_free_extent_table(ext2_extent extent); +extern errcode_t ext2fs_add_extent_entry(ext2_extent extent, + __u32 old, __u32 new); +extern __u32 ext2fs_extent_translate(ext2_extent extent, __u32 old); +extern void ext2fs_extent_dump(ext2_extent extent, FILE *out); +extern errcode_t ext2fs_iterate_extent(ext2_extent extent, __u32 *old, + __u32 *new, int *size); + +/* sim_progress.c */ +extern errcode_t ext2fs_progress_init(ext2_sim_progmeter *ret_prog, + const char *label, + int labelwidth, int barwidth, + __u32 maxdone, int flags); +extern void ext2fs_progress_update(ext2_sim_progmeter prog, + __u32 current); +extern void ext2fs_progress_close(ext2_sim_progmeter prog); + + diff --git a/resize/sim_progress.c b/resize/sim_progress.c new file mode 100644 index 00000000..1e30d116 --- /dev/null +++ b/resize/sim_progress.c @@ -0,0 +1,106 @@ +/* + * sim_progress.c --- simple progress meter + */ + +#include "resize2fs.h" + +struct ext2_sim_progress { + FILE *f; + char *label; + int labelwidth; + int barwidth; + __u32 maxdone; + __u32 current; + int shown; + int flags; +}; + +static errcode_t ext2fs_progress_display(ext2_sim_progmeter prog) +{ + int i, width; + + fputs(prog->label, prog->f); + width = prog->labelwidth - strlen(prog->label); + while (width-- > 0) + putc(' ', prog->f); + if (prog->labelwidth + prog->barwidth > 80) { + fputs("\n", prog->f); + for (width = prog->labelwidth; width > 0; width--) + putc(' ', prog->f); + } + for (i=0; i < prog->barwidth; i++) + putc('-', prog->f); + for (i=0; i < prog->barwidth; i++) + putc('\b', prog->f); + fflush(prog->f); + return 0; +} + + +void ext2fs_progress_update(ext2_sim_progmeter prog, __u32 current) +{ + int old_level, level, num, i; + + level = prog->barwidth * current / prog->maxdone; + old_level = prog->barwidth * prog->current / prog->maxdone; + prog->current = current; + + num = level - old_level; + if (num == 0) + return; + + if (num > 0) { + for (i=0; i < num; i++) + putc('X', prog->f); + } else { + num = -num; + for (i=0; i < num; i++) + putc('\b', prog->f); + for (i=0; i < num; i++) + putc('-', prog->f); + for (i=0; i < num; i++) + putc('\b', prog->f); + } + fflush(prog->f); +} + +errcode_t ext2fs_progress_init(ext2_sim_progmeter *ret_prog, + const char *label, + int labelwidth, int barwidth, + __u32 maxdone, int flags) +{ + ext2_sim_progmeter prog; + + prog = malloc(sizeof(struct ext2_sim_progress)); + if (!prog) + return ENOMEM; + memset(prog, 0, sizeof(struct ext2_sim_progress)); + + prog->label = malloc(strlen(label)+1); + if (!prog->label) { + free(prog); + return ENOMEM; + } + strcpy(prog->label, label); + prog->labelwidth = labelwidth; + prog->barwidth = barwidth; + prog->flags = flags; + prog->maxdone = maxdone; + prog->current = 0; + prog->shown = 0; + prog->f = stdout; + + *ret_prog = prog; + + return ext2fs_progress_display(prog); +} + +void ext2fs_progress_close(ext2_sim_progmeter prog) +{ + + if (prog->label) + free(prog->label); + free(prog); + printf("\n"); + return; +} diff --git a/resize/test_extent.c b/resize/test_extent.c new file mode 100644 index 00000000..dc3a584e --- /dev/null +++ b/resize/test_extent.c @@ -0,0 +1,113 @@ +/* + * test_extent.c --- tester for the extent abstraction + * + * Copyright (C) 1997 Theodore Ts'o + * + * %Begin-Header% + * All rights reserved. + * %End-Header% + */ + +#include "resize2fs.h" + +void do_test(FILE *in, FILE *out); + +void do_test(FILE *in, FILE *out) +{ + char buf[128]; + char *cp, *cmd, *arg1, *arg2; + __u32 num1, num2; + int size; + errcode_t retval; + ext2_extent extent = 0; + const char *no_table = "# No extent table\n"; + + while (!feof(in)) { + if (!fgets(buf, sizeof(buf), in)) + break; + /* + * Ignore comments + */ + if (buf[0] =='#') + continue; + + /* + * Echo command + */ + fputs(buf, out); + + cp = strchr(buf, '\n'); + if (cp) + *cp = '\0'; + + /* + * Parse command line; simple, at most two arguments + */ + cmd = buf; + num1 = num2 = 0; + arg1 = arg2 = 0; + cp = strchr(buf, ' '); + if (cp) { + *cp++ = '\0'; + arg1 = cp; + num1 = strtoul(arg1, 0, 0); + + cp = strchr(cp, ' '); + } + if (cp) { + *cp++ = '\0'; + arg2 = cp; + num2 = strtoul(arg2, 0, 0); + } + + if (!strcmp(cmd, "create")) { + retval = ext2fs_create_extent_table(&extent, num1); + if (retval) { + handle_error: + fprintf(out, "# Error: %s\n", + error_message(retval)); + continue; + } + continue; + } + if (!extent) { + fputs(no_table, out); + continue; + } + if (!strcmp(cmd, "free")) { + ext2fs_free_extent_table(extent); + extent = 0; + } else if (!strcmp(cmd, "add")) { + retval = ext2fs_add_extent_entry(extent, num1, num2); + if (retval) + goto handle_error; + } else if (!strcmp(cmd, "lookup")) { + num2 = ext2fs_extent_translate(extent, num1); + fprintf(out, "# Answer: %u%s\n", num2, + num2 ? "" : " (not found)"); + } else if (!strcmp(cmd, "dump")) { + ext2fs_extent_dump(extent, out); + } else if (!strcmp(cmd, "iter_test")) { + retval = ext2fs_iterate_extent(extent, 0, 0, 0); + if (retval) + goto handle_error; + while (1) { + retval = ext2fs_iterate_extent(extent, + &num1, &num2, &size); + if (retval) + goto handle_error; + if (!size) + break; + fprintf(out, "# %u -> %u (%d)\n", + num1, num2, size); + } + } else + fputs("# Syntax error\n", out); + } +} + +int main(int argc, char **argv) +{ + do_test(stdin, stdout); + exit(0); +} diff --git a/resize/test_extent.in b/resize/test_extent.in new file mode 100644 index 00000000..7edcc418 --- /dev/null +++ b/resize/test_extent.in @@ -0,0 +1,64 @@ +create 10 +add 10 20 +add 11 21 +add 12 22 +add 14 45 +add 16 50 +add 17 51 +dump +# Extent dump: +# Num=3, Size=10, Cursor=0, Sorted=1 +# 10 -> 20 (3) +# 14 -> 45 (1) +# 16 -> 50 (2) +add 18 52 +dump +# Extent dump: +# Num=3, Size=10, Cursor=0, Sorted=1 +# 10 -> 20 (3) +# 14 -> 45 (1) +# 16 -> 50 (3) +lookup 10 +# Answer: 20 +lookup 11 +# Answer: 21 +lookup 12 +# Answer: 22 +lookup 13 +# Answer: 0 (not found) +lookup 14 +# Answer: 45 +lookup 15 +# Answer: 0 (not found) +lookup 16 +# Answer: 50 +lookup 1 +# Answer: 0 (not found) +lookup 50 +# Answer: 0 (not found) +add 19 100 +add 13 5 +lookup 18 +# Answer: 52 +lookup 19 +# Answer: 100 +lookup 20 +# Answer: 0 (not found) +lookup 12 +# Answer: 22 +lookup 13 +# Answer: 5 +dump +# Extent dump: +# Num=5, Size=10, Cursor=0, Sorted=1 +# 10 -> 20 (3) +# 13 -> 5 (1) +# 14 -> 45 (1) +# 16 -> 50 (3) +# 19 -> 100 (1) +iter_test +# 10 -> 20 (3) +# 13 -> 5 (1) +# 14 -> 45 (1) +# 16 -> 50 (3) +# 19 -> 100 (1)