@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)
-$(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)
$(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
TODO
-*) Inode table relocation
-
-*) Inode relocation
-
-*) Summary information collection
-
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+
+
+++ /dev/null
-/*
- * 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;
-}
-
* %End-Header%
*/
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+
#include "resize2fs.h"
#define E2FSPROGS_VERSION "1.10"
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);
}
ext2_filsys fs;
int c;
int flags = 0;
+ int flush = 0;
+ int fd;
blk_t new_size;
io_manager io_ptr;
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;
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;
.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 <tytso@mit.edu>.
.SH SEE ALSO
.BR dumpe2fs (8),
{
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;
(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 +
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.
/*
* 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;
/*
* 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,
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
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
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
/*
* 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++;
- }
- if (blk == fs->group_desc[i].bg_inode_bitmap) {
- fs->group_desc[i].bg_inode_bitmap = 0;
+ } 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++;
}
- /*
- * 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 &&
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,
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
* 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.
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;
}
* 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)
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;
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)
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;
{
ext2_resize_t rfs;
errcode_t retval;
- int bmove_flags;
retval = ext2fs_read_bitmaps(fs);
if (retval)
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;
#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
*/
#define RESIZE_DEBUG_IO 0x0001
#define RESIZE_DEBUG_ITABLEMOVE 0x0008
#define RESIZE_PERCENT_COMPLETE 0x0100
+#define RESIZE_VERBOSE 0x0200
/*
* The core state structure for the ext2 resizer
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;
/* 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);
+
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+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)