Whamcloud - gitweb
Many files:
authorTheodore Ts'o <tytso@mit.edu>
Tue, 17 Jun 1997 03:52:12 +0000 (03:52 +0000)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 17 Jun 1997 03:52:12 +0000 (03:52 +0000)
  Checkin of work to date.  (Pretty much completely working now.)

13 files changed:
resize/Makefile.in
resize/NOTES
resize/ext2_block_move.c [new file with mode: 0644]
resize/ext2_inode_move.c [new file with mode: 0644]
resize/extent.c [new file with mode: 0644]
resize/inodemap.c [deleted file]
resize/main.c
resize/resize2fs.8.in
resize/resize2fs.c
resize/resize2fs.h
resize/sim_progress.c [new file with mode: 0644]
resize/test_extent.c [new file with mode: 0644]
resize/test_extent.in [new file with mode: 0644]

index d5991f8..bda475d 100644 (file)
@@ -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
index e04d180..ccbf810 100644 (file)
@@ -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 (file)
index 0000000..ec60edf
--- /dev/null
@@ -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 (file)
index 0000000..5d26b15
--- /dev/null
@@ -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 (file)
index 0000000..0f0ef4c
--- /dev/null
@@ -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 (file)
index 8f419e6..0000000
+++ /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;
-}
-
index 3d3c589..b5e5a7f 100644 (file)
@@ -8,6 +8,13 @@
  * %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"
@@ -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;
index ee2c933..ac2c7fd 100644 (file)
@@ -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 <tytso@mit.edu>.
 .SH SEE ALSO
 .BR dumpe2fs (8),
index b90c075..66e0b20 100644 (file)
@@ -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;
@@ -96,13 +97,20 @@ retry:
                        (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++;
-                       }
-                       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 &&
@@ -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;
 
index 28297f6..32ddaa0 100644 (file)
 #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
@@ -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 (file)
index 0000000..1e30d11
--- /dev/null
@@ -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 (file)
index 0000000..dc3a584
--- /dev/null
@@ -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 (file)
index 0000000..7edcc41
--- /dev/null
@@ -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)