]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck: rebuild sparse extent trees & convert non-extent ext3 files
authorDarrick J. Wong <darrick.wong@oracle.com>
Thu, 2 Apr 2015 02:34:40 +0000 (19:34 -0700)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 21 Apr 2015 20:22:59 +0000 (16:22 -0400)
Teach e2fsck to (re)construct extent trees.  This enables us to do
either of the following: compress a highly sparse extent tree into
fewer ETB blocks; or convert a ext3-style block mapped file to an
extent file.  The reconstruction is performed during pass 1E or 3A,
as detailed below.

For files that are already extent based, this algorithm will
automatically run (pending user approval) if pass1 determines either
(1) that a whole level of extent tree will fit into a higher level of
the tree; (2) that the size of any level can be reduced by at least
one ETB block; or (3) the extent tree is unnecessarily deep.  It will
not run at all if errors are found and the user declines to fix the
errors.

The option "-E bmap2extent" can be used to force e2fsck to convert all
block map files to extent trees, and to rebuild all extent files'
extent trees.  After conversion, files larger than 12 blocks should be
defragmented to eliminate empty holes where a block lives.

The extent tree constructor is pretty dumb -- it creates a list of
leaf extents (adjacent extents are collapsed), marks all indirect
blocks / ETB blocks free, installs a new extent tree root in the
inode, then loads the leaf extents into the tree.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
18 files changed:
e2fsck/Makefile.in
e2fsck/e2fsck.8.in
e2fsck/e2fsck.c
e2fsck/e2fsck.h
e2fsck/extents.c [new file with mode: 0644]
e2fsck/pass1.c
e2fsck/problem.c
e2fsck/problem.h
e2fsck/rehash.c
e2fsck/super.c
e2fsck/unix.c
tests/f_extent_bad_node/expect.1
tests/f_extent_bad_node/expect.2
tests/f_extent_int_bad_magic/expect.1
tests/f_extent_leaf_bad_magic/expect.1
tests/f_extent_oobounds/expect.1
tests/f_extent_oobounds/expect.2
tests/f_extents/expect.1

index d80ae4b5b4d4eeeae8af4ddcaec4d6c61450daa1..8e555ff8729eb78f373df227ac90d56838a0d0ee 100644 (file)
@@ -62,7 +62,8 @@ OBJS= dict.o unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o \
        pass3.o pass4.o pass5.o journal.o badblocks.o util.o dirinfo.o \
        dx_dirinfo.o ehandler.o problem.o message.o quota.o recovery.o \
        region.o revoke.o ea_refcount.o rehash.o profile.o prof_err.o \
-       logfile.o sigcatcher.o $(MTRACE_OBJ) plausible.o readahead.o
+       logfile.o sigcatcher.o $(MTRACE_OBJ) plausible.o readahead.o \
+       extents.o
 
 PROFILED_OBJS= profiled/dict.o profiled/unix.o profiled/e2fsck.o \
        profiled/super.o profiled/pass1.o profiled/pass1b.o \
@@ -73,7 +74,7 @@ PROFILED_OBJS= profiled/dict.o profiled/unix.o profiled/e2fsck.o \
        profiled/recovery.o profiled/region.o profiled/revoke.o \
        profiled/ea_refcount.o profiled/rehash.o profiled/profile.o \
        profiled/prof_err.o profiled/logfile.o profiled/sigcatcher.o \
-       profiled/plausible.o profiled/readahead.o
+       profiled/plausible.o profiled/readahead.o profiled/extents.o
 
 SRCS= $(srcdir)/e2fsck.c \
        $(srcdir)/dict.c \
@@ -105,6 +106,7 @@ SRCS= $(srcdir)/e2fsck.c \
        prof_err.c \
        $(srcdir)/quota.c \
        $(srcdir)/../misc/plausible.c \
+       $(srcdir)/extents.c \
        $(MTRACE_SRC)
 
 all:: profiled $(PROGS) e2fsck $(MANPAGES) $(FMANPAGES)
@@ -490,6 +492,16 @@ rehash.o: $(srcdir)/rehash.c $(top_builddir)/lib/config.h \
  $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/quota/quotaio.h \
  $(top_srcdir)/lib/quota/dqblk_v2.h $(top_srcdir)/lib/quota/quotaio_tree.h \
  $(top_srcdir)/lib/../e2fsck/dict.h $(srcdir)/problem.h
+readahead.o: $(srcdir)/readahead.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2fsck.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
+ $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/quota/quotaio.h \
+ $(top_srcdir)/lib/quota/dqblk_v2.h $(top_srcdir)/lib/quota/quotaio_tree.h \
+ $(top_srcdir)/lib/../e2fsck/dict.h
 region.o: $(srcdir)/region.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/e2fsck.h \
  $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
@@ -535,13 +547,20 @@ quota.o: $(srcdir)/quota.c $(top_builddir)/lib/config.h \
  $(top_srcdir)/lib/quota/dqblk_v2.h $(top_srcdir)/lib/quota/quotaio_tree.h \
  $(top_srcdir)/lib/../e2fsck/dict.h $(srcdir)/problem.h
 plausible.o: $(srcdir)/../misc/plausible.c $(top_builddir)/lib/config.h \
- $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/et/com_err.h \
- $(top_srcdir)/lib/e2p/e2p.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
- $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
- $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/../misc/plausible.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
  $(top_builddir)/lib/ext2fs/ext2_err.h \
  $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
- $(srcdir)/../misc/nls-enable.h $(srcdir)/../misc/plausible.h
-readahead.o: $(srcdir)/readahead.c $(top_builddir)/lib/config.h \
- $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
- $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/e2fsck.h prof_err.h
+ $(srcdir)/../misc/nls-enable.h
+extents.o: $(srcdir)/extents.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2fsck.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
+ $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/quota/quotaio.h \
+ $(top_srcdir)/lib/quota/dqblk_v2.h $(top_srcdir)/lib/quota/quotaio_tree.h \
+ $(top_srcdir)/lib/../e2fsck/dict.h $(srcdir)/problem.h
index 270727a556cc6831099cc3d912d057ae26d132a6..e1bbd275d2f83ffa71e69c9b7813e7e1c0f4c902 100644 (file)
@@ -227,6 +227,14 @@ e2fsck runtime.  By default, this is set to the size of two block groups' inode
 tables (typically 4MiB on a regular ext4 filesystem); if this amount is more
 than 1/50th of total physical memory, readahead is disabled.  Set this to zero
 to disable readahead entirely.
+.TP
+.BI bmap2extent
+Convert block-mapped files to extent-mapped files.
+.TP
+.BI fixes_only
+Only fix damaged metadata; do not optimize htree directories or compress
+extent trees.  This option is incompatible with the -D and -E bmap2extent
+options.
 .RE
 .TP
 .B \-f
index cf43a8c10c27773e8e16d4f56a1e560884ce3ffe..cab90b87a20ef62b3edbcbaf186085bdd6d85b2b 100644 (file)
@@ -208,8 +208,8 @@ void e2fsck_free_context(e2fsck_t ctx)
 typedef void (*pass_t)(e2fsck_t ctx);
 
 static pass_t e2fsck_passes[] = {
-       e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
-       e2fsck_pass5, 0 };
+       e2fsck_pass1, e2fsck_pass1e, e2fsck_pass2, e2fsck_pass3,
+       e2fsck_pass4, e2fsck_pass5, 0 };
 
 #define E2F_FLAG_RUN_RETURN    (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
 
index a0e03e38c71802a7b062cdc76490c37bde2e356f..8e0e30263f6e6890ef913f65848f00abf21164eb 100644 (file)
@@ -167,6 +167,8 @@ struct resource_track {
 #define E2F_OPT_FRAGCHECK      0x0800
 #define E2F_OPT_JOURNAL_ONLY   0x1000 /* only replay the journal */
 #define E2F_OPT_DISCARD                0x2000
+#define E2F_OPT_CONVERT_BMAP   0x4000 /* convert blockmap to extent */
+#define E2F_OPT_FIXES_ONLY     0x8000 /* skip all optimizations */
 
 /*
  * E2fsck flags
@@ -190,6 +192,7 @@ struct resource_track {
 #define E2F_FLAG_EXITING       0x1000 /* E2fsck exiting due to errors */
 #define E2F_FLAG_TIME_INSANE   0x2000 /* Time is insane */
 #define E2F_FLAG_PROBLEMS_FIXED        0x4000 /* At least one problem was fixed */
+#define E2F_FLAG_ALLOC_OK      0x8000 /* Can we allocate blocks? */
 
 #define E2F_RESET_FLAGS (E2F_FLAG_TIME_INSANE | E2F_FLAG_PROBLEMS_FIXED)
 
@@ -382,6 +385,23 @@ struct e2fsck_struct {
 
        /* How much are we allowed to readahead? */
        unsigned long long readahead_kb;
+
+       /*
+        * Inodes to rebuild extent trees
+        */
+       ext2fs_inode_bitmap inodes_to_rebuild;
+};
+
+/* Data structures to evaluate whether an extent tree needs rebuilding. */
+struct extent_tree_level {
+       unsigned int    num_extents;
+       unsigned int    max_extents;
+};
+
+struct extent_tree_info {
+       ext2_ino_t ino;
+       int force_rebuild;
+       struct extent_tree_level        ext_info[MAX_EXTENT_DEPTH_COUNT];
 };
 
 /* Used by the region allocation code */
@@ -457,6 +477,19 @@ extern blk64_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret);
 extern const char *ehandler_operation(const char *op);
 extern void ehandler_init(io_channel channel);
 
+/* extents.c */
+struct problem_context;
+errcode_t e2fsck_rebuild_extents_later(e2fsck_t ctx, ext2_ino_t ino);
+int e2fsck_ino_will_be_rebuilt(e2fsck_t ctx, ext2_ino_t ino);
+void e2fsck_pass1e(e2fsck_t ctx);
+errcode_t e2fsck_check_rebuild_extents(e2fsck_t ctx, ext2_ino_t ino,
+                                      struct ext2_inode *inode,
+                                      struct problem_context *pctx);
+errcode_t e2fsck_should_rebuild_extents(e2fsck_t ctx,
+                                       struct problem_context *pctx,
+                                       struct extent_tree_info *eti,
+                                       struct ext2_extent_info *info);
+
 /* journal.c */
 extern errcode_t e2fsck_check_ext3_journal(e2fsck_t ctx);
 extern errcode_t e2fsck_run_ext3_journal(e2fsck_t ctx);
@@ -524,7 +557,8 @@ extern int region_allocate(region_t region, region_addr_t start, int n);
 /* rehash.c */
 void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino);
 int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino);
-errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino);
+errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino,
+                           struct problem_context *pctx);
 void e2fsck_rehash_directories(e2fsck_t ctx);
 
 /* sigcatcher.c */
diff --git a/e2fsck/extents.c b/e2fsck/extents.c
new file mode 100644 (file)
index 0000000..a08fa94
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+ * extents.c --- rebuild extent tree
+ *
+ * Copyright (C) 2014 Oracle.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include "e2fsck.h"
+#include "problem.h"
+
+#undef DEBUG
+#undef DEBUG_SUMMARY
+#undef DEBUG_FREE
+
+#define NUM_EXTENTS    341     /* about one ETB' worth of extents */
+
+static errcode_t e2fsck_rebuild_extents(e2fsck_t ctx, ext2_ino_t ino);
+
+/* Schedule an inode to have its extent tree rebuilt during pass 1E. */
+errcode_t e2fsck_rebuild_extents_later(e2fsck_t ctx, ext2_ino_t ino)
+{
+       if (!EXT2_HAS_INCOMPAT_FEATURE(ctx->fs->super,
+                                      EXT3_FEATURE_INCOMPAT_EXTENTS) ||
+           (ctx->options & E2F_OPT_NO) ||
+           (ino != EXT2_ROOT_INO && ino < ctx->fs->super->s_first_ino))
+               return 0;
+
+       if (ctx->flags & E2F_FLAG_ALLOC_OK)
+               return e2fsck_rebuild_extents(ctx, ino);
+
+       if (!ctx->inodes_to_rebuild)
+               e2fsck_allocate_inode_bitmap(ctx->fs,
+                                            _("extent rebuild inode map"),
+                                            EXT2FS_BMAP64_RBTREE,
+                                            "inodes_to_rebuild",
+                                            &ctx->inodes_to_rebuild);
+       if (ctx->inodes_to_rebuild)
+               ext2fs_mark_inode_bitmap2(ctx->inodes_to_rebuild, ino);
+       return 0;
+}
+
+/* Ask if an inode will have its extents rebuilt during pass 1E. */
+int e2fsck_ino_will_be_rebuilt(e2fsck_t ctx, ext2_ino_t ino)
+{
+       if (!ctx->inodes_to_rebuild)
+               return 0;
+       return ext2fs_test_inode_bitmap2(ctx->inodes_to_rebuild, ino);
+}
+
+struct extent_list {
+       blk64_t blocks_freed;
+       struct ext2fs_extent *extents;
+       unsigned int count;
+       unsigned int size;
+       unsigned int ext_read;
+       errcode_t retval;
+       ext2_ino_t ino;
+};
+
+static errcode_t load_extents(e2fsck_t ctx, struct extent_list *list)
+{
+       ext2_filsys             fs = ctx->fs;
+       ext2_extent_handle_t    handle;
+       struct ext2fs_extent    extent;
+       errcode_t               retval;
+
+       retval = ext2fs_extent_open(fs, list->ino, &handle);
+       if (retval)
+               return retval;
+
+       retval = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
+       if (retval)
+               goto out;
+
+       do {
+               if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
+                       goto next;
+
+               /* Internal node; free it and we'll re-allocate it later */
+               if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) {
+#if defined(DEBUG) || defined(DEBUG_FREE)
+                       printf("ino=%d free=%llu bf=%llu\n", list->ino,
+                                       extent.e_pblk, list->blocks_freed + 1);
+#endif
+                       list->blocks_freed++;
+                       ext2fs_block_alloc_stats2(fs, extent.e_pblk, -1);
+                       goto next;
+               }
+
+               list->ext_read++;
+               /* Can we attach it to the previous extent? */
+               if (list->count) {
+                       struct ext2fs_extent *last = list->extents +
+                                                    list->count - 1;
+                       blk64_t end = last->e_len + extent.e_len;
+
+                       if (last->e_pblk + last->e_len == extent.e_pblk &&
+                           last->e_lblk + last->e_len == extent.e_lblk &&
+                           (last->e_flags & EXT2_EXTENT_FLAGS_UNINIT) ==
+                           (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+                           end < (1ULL << 32)) {
+                               last->e_len += extent.e_len;
+#ifdef DEBUG
+                               printf("R: ino=%d len=%u\n", list->ino,
+                                               last->e_len);
+#endif
+                               goto next;
+                       }
+               }
+
+               /* Do we need to expand? */
+               if (list->count == list->size) {
+                       unsigned int new_size = (list->size + NUM_EXTENTS) *
+                                               sizeof(struct ext2fs_extent);
+                       retval = ext2fs_resize_mem(0, new_size, &list->extents);
+                       if (retval)
+                               goto out;
+                       list->size += NUM_EXTENTS;
+               }
+
+               /* Add a new extent */
+               memcpy(list->extents + list->count, &extent, sizeof(extent));
+#ifdef DEBUG
+               printf("R: ino=%d pblk=%llu lblk=%llu len=%u\n", list->ino,
+                               extent.e_pblk, extent.e_lblk, extent.e_len);
+#endif
+               list->count++;
+next:
+               retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
+       } while (retval == 0);
+
+out:
+       /* Ok if we run off the end */
+       if (retval == EXT2_ET_EXTENT_NO_NEXT)
+               retval = 0;
+       ext2fs_extent_free(handle);
+       return retval;
+}
+
+static int find_blocks(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt,
+                      blk64_t ref_blk EXT2FS_ATTR((unused)),
+                      int ref_offset EXT2FS_ATTR((unused)), void *priv_data)
+{
+       struct extent_list *list = priv_data;
+
+       /* Internal node? */
+       if (blockcnt < 0) {
+#if defined(DEBUG) || defined(DEBUG_FREE)
+               printf("ino=%d free=%llu bf=%llu\n", list->ino, *blocknr,
+                               list->blocks_freed + 1);
+#endif
+               list->blocks_freed++;
+               ext2fs_block_alloc_stats2(fs, *blocknr, -1);
+               return 0;
+       }
+
+       /* Can we attach it to the previous extent? */
+       if (list->count) {
+               struct ext2fs_extent *last = list->extents +
+                                            list->count - 1;
+               blk64_t end = last->e_len + 1;
+
+               if (last->e_pblk + last->e_len == *blocknr &&
+                   end < (1ULL << 32)) {
+                       last->e_len++;
+#ifdef DEBUG
+                       printf("R: ino=%d len=%u\n", list->ino, last->e_len);
+#endif
+                       return 0;
+               }
+       }
+
+       /* Do we need to expand? */
+       if (list->count == list->size) {
+               unsigned int new_size = (list->size + NUM_EXTENTS) *
+                                       sizeof(struct ext2fs_extent);
+               list->retval = ext2fs_resize_mem(0, new_size, &list->extents);
+               if (list->retval)
+                       return BLOCK_ABORT;
+               list->size += NUM_EXTENTS;
+       }
+
+       /* Add a new extent */
+       list->extents[list->count].e_pblk = *blocknr;
+       list->extents[list->count].e_lblk = blockcnt;
+       list->extents[list->count].e_len = 1;
+       list->extents[list->count].e_flags = 0;
+#ifdef DEBUG
+       printf("R: ino=%d pblk=%llu lblk=%llu len=%u\n", list->ino, *blocknr,
+                       blockcnt, 1);
+#endif
+       list->count++;
+
+       return 0;
+}
+
+static errcode_t rebuild_extent_tree(e2fsck_t ctx, struct extent_list *list,
+                                    ext2_ino_t ino)
+{
+       struct ext2_inode       inode;
+       errcode_t               retval;
+       ext2_extent_handle_t    handle;
+       unsigned int            i, ext_written;
+       struct ext2fs_extent    *ex, extent;
+
+       list->count = 0;
+       list->blocks_freed = 0;
+       list->ino = ino;
+       list->ext_read = 0;
+       e2fsck_read_inode(ctx, ino, &inode, "rebuild_extents");
+
+       /* Skip deleted inodes and inline data files */
+       if (inode.i_links_count == 0 ||
+           inode.i_flags & EXT4_INLINE_DATA_FL)
+               return 0;
+
+       /* Collect lblk->pblk mappings */
+       if (inode.i_flags & EXT4_EXTENTS_FL) {
+               retval = load_extents(ctx, list);
+               goto extents_loaded;
+       }
+
+       retval = ext2fs_block_iterate3(ctx->fs, ino, BLOCK_FLAG_READ_ONLY, 0,
+                                      find_blocks, list);
+       if (retval)
+               goto err;
+       if (list->retval) {
+               retval = list->retval;
+               goto err;
+       }
+
+extents_loaded:
+       /* Reset extent tree */
+       inode.i_flags &= ~EXT4_EXTENTS_FL;
+       memset(inode.i_block, 0, sizeof(inode.i_block));
+
+       /* Make a note of freed blocks */
+       retval = ext2fs_iblk_sub_blocks(ctx->fs, &inode, list->blocks_freed);
+       if (retval)
+               goto err;
+
+       /* Now stuff extents into the file */
+       retval = ext2fs_extent_open2(ctx->fs, ino, &inode, &handle);
+       if (retval)
+               goto err;
+
+       ext_written = 0;
+       for (i = 0, ex = list->extents; i < list->count; i++, ex++) {
+               memcpy(&extent, ex, sizeof(struct ext2fs_extent));
+               extent.e_flags &= EXT2_EXTENT_FLAGS_UNINIT;
+               if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) {
+                       if (extent.e_len > EXT_UNINIT_MAX_LEN) {
+                               extent.e_len = EXT_UNINIT_MAX_LEN;
+                               ex->e_pblk += EXT_UNINIT_MAX_LEN;
+                               ex->e_lblk += EXT_UNINIT_MAX_LEN;
+                               ex->e_len -= EXT_UNINIT_MAX_LEN;
+                               ex--;
+                               i--;
+                       }
+               } else {
+                       if (extent.e_len > EXT_INIT_MAX_LEN) {
+                               extent.e_len = EXT_INIT_MAX_LEN;
+                               ex->e_pblk += EXT_INIT_MAX_LEN;
+                               ex->e_lblk += EXT_INIT_MAX_LEN;
+                               ex->e_len -= EXT_INIT_MAX_LEN;
+                               ex--;
+                               i--;
+                       }
+               }
+
+#ifdef DEBUG
+               printf("W: ino=%d pblk=%llu lblk=%llu len=%u\n", ino,
+                               extent.e_pblk, extent.e_lblk, extent.e_len);
+#endif
+               retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER,
+                                             &extent);
+               if (retval)
+                       goto err2;
+               retval = ext2fs_extent_fix_parents(handle);
+               if (retval)
+                       goto err2;
+               ext_written++;
+       }
+
+#if defined(DEBUG) || defined(DEBUG_SUMMARY)
+       printf("rebuild: ino=%d extents=%d->%d\n", ino, list->ext_read,
+              ext_written);
+#endif
+       e2fsck_write_inode(ctx, ino, &inode, "rebuild_extents");
+
+err2:
+       ext2fs_extent_free(handle);
+err:
+       return retval;
+}
+
+/* Rebuild the extents immediately */
+static errcode_t e2fsck_rebuild_extents(e2fsck_t ctx, ext2_ino_t ino)
+{
+       struct extent_list      list;
+       errcode_t err;
+
+       if (!EXT2_HAS_INCOMPAT_FEATURE(ctx->fs->super,
+                                      EXT3_FEATURE_INCOMPAT_EXTENTS) ||
+           (ctx->options & E2F_OPT_NO) ||
+           (ino != EXT2_ROOT_INO && ino < ctx->fs->super->s_first_ino))
+               return 0;
+
+       e2fsck_read_bitmaps(ctx);
+       memset(&list, 0, sizeof(list));
+       err = ext2fs_get_mem(sizeof(struct ext2fs_extent) * NUM_EXTENTS,
+                               &list.extents);
+       if (err)
+               return err;
+       list.size = NUM_EXTENTS;
+       err = rebuild_extent_tree(ctx, &list, ino);
+       ext2fs_free_mem(&list.extents);
+
+       return err;
+}
+
+static void rebuild_extents(e2fsck_t ctx, const char *pass_name, int pr_header)
+{
+       struct problem_context  pctx;
+#ifdef RESOURCE_TRACK
+       struct resource_track   rtrack;
+#endif
+       struct extent_list      list;
+       int                     first = 1;
+       ext2_ino_t              ino = 0;
+       errcode_t               retval;
+
+       if (!EXT2_HAS_INCOMPAT_FEATURE(ctx->fs->super,
+                                      EXT3_FEATURE_INCOMPAT_EXTENTS) ||
+           !ext2fs_test_valid(ctx->fs) ||
+           ctx->invalid_bitmaps) {
+               if (ctx->inodes_to_rebuild)
+                       ext2fs_free_inode_bitmap(ctx->inodes_to_rebuild);
+               ctx->inodes_to_rebuild = NULL;
+       }
+
+       if (ctx->inodes_to_rebuild == NULL)
+               return;
+
+       init_resource_track(&rtrack, ctx->fs->io);
+       clear_problem_context(&pctx);
+       e2fsck_read_bitmaps(ctx);
+
+       memset(&list, 0, sizeof(list));
+       retval = ext2fs_get_mem(sizeof(struct ext2fs_extent) * NUM_EXTENTS,
+                               &list.extents);
+       list.size = NUM_EXTENTS;
+       while (1) {
+               retval = ext2fs_find_first_set_inode_bitmap2(
+                               ctx->inodes_to_rebuild, ino + 1,
+                               ctx->fs->super->s_inodes_count, &ino);
+               if (retval)
+                       break;
+               pctx.ino = ino;
+               if (first) {
+                       fix_problem(ctx, pr_header, &pctx);
+                       first = 0;
+               }
+               pctx.errcode = rebuild_extent_tree(ctx, &list, ino);
+               if (pctx.errcode) {
+                       end_problem_latch(ctx, PR_LATCH_OPTIMIZE_EXT);
+                       fix_problem(ctx, PR_1E_OPTIMIZE_EXT_ERR, &pctx);
+               }
+               if (ctx->progress && !ctx->progress_fd)
+                       e2fsck_simple_progress(ctx, "Rebuilding extents",
+                                       100.0 * (float) ino /
+                                       (float) ctx->fs->super->s_inodes_count,
+                                       ino);
+       }
+       end_problem_latch(ctx, PR_LATCH_OPTIMIZE_EXT);
+
+       ext2fs_free_inode_bitmap(ctx->inodes_to_rebuild);
+       ctx->inodes_to_rebuild = NULL;
+       ext2fs_free_mem(&list.extents);
+
+       print_resource_track(ctx, pass_name, &rtrack, ctx->fs->io);
+}
+
+/* Scan a file to see if we should rebuild its extent tree */
+errcode_t e2fsck_check_rebuild_extents(e2fsck_t ctx, ext2_ino_t ino,
+                                 struct ext2_inode *inode,
+                                 struct problem_context *pctx)
+{
+       struct extent_tree_info eti;
+       struct ext2_extent_info info, top_info;
+       struct ext2fs_extent    extent;
+       ext2_extent_handle_t    ehandle;
+       ext2_filsys             fs = ctx->fs;
+       errcode_t               retval;
+
+       /* block map file and we want extent conversion */
+       if (!(inode->i_flags & EXT4_EXTENTS_FL) &&
+           !(inode->i_flags & EXT4_INLINE_DATA_FL) &&
+           (ctx->options & E2F_OPT_CONVERT_BMAP)) {
+               return e2fsck_rebuild_extents_later(ctx, ino);
+       }
+
+       if (!(inode->i_flags & EXT4_EXTENTS_FL))
+               return 0;
+       memset(&eti, 0, sizeof(eti));
+       eti.ino = ino;
+
+       /* Otherwise, go scan the extent tree... */
+       retval = ext2fs_extent_open2(fs, ino, inode, &ehandle);
+       if (retval)
+               return 0;
+
+       retval = ext2fs_extent_get_info(ehandle, &top_info);
+       if (retval)
+               goto out;
+
+       /* Check maximum extent depth */
+       pctx->ino = ino;
+       pctx->blk = top_info.max_depth;
+       pctx->blk2 = ext2fs_max_extent_depth(ehandle);
+       if (pctx->blk2 < pctx->blk &&
+           fix_problem(ctx, PR_1_EXTENT_BAD_MAX_DEPTH, pctx))
+               eti.force_rebuild = 1;
+
+       /* Can we collect extent tree level stats? */
+       pctx->blk = MAX_EXTENT_DEPTH_COUNT;
+       if (pctx->blk2 > pctx->blk)
+               fix_problem(ctx, PR_1E_MAX_EXTENT_TREE_DEPTH, pctx);
+
+       /* We need to fix tree depth problems, but the scan isn't a fix. */
+       if (ctx->options & E2F_OPT_FIXES_ONLY)
+               goto out;
+
+       retval = ext2fs_extent_get(ehandle, EXT2_EXTENT_ROOT, &extent);
+       if (retval)
+               goto out;
+
+       do {
+               retval = ext2fs_extent_get_info(ehandle, &info);
+               if (retval)
+                       break;
+
+               /*
+                * If this is the first extent in an extent block that we
+                * haven't visited, collect stats on the block.
+                */
+               if (info.curr_entry == 1 &&
+                   !(extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) &&
+                   !eti.force_rebuild) {
+                       struct extent_tree_level *etl;
+
+                       etl = eti.ext_info + info.curr_level;
+                       etl->num_extents += info.num_entries;
+                       etl->max_extents += info.max_entries;
+                       /*
+                        * Implementation wart: Splitting extent blocks when
+                        * appending will leave the old block with one free
+                        * entry.  Therefore unless the node is totally full,
+                        * pretend that a non-root extent block can hold one
+                        * fewer entry than it actually does, so that we don't
+                        * repeatedly rebuild the extent tree.
+                        */
+                       if (info.curr_level &&
+                           info.num_entries < info.max_entries)
+                               etl->max_extents--;
+               }
+
+               /* Skip to the end of a block of leaf nodes */
+               if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
+                       retval = ext2fs_extent_get(ehandle,
+                                                   EXT2_EXTENT_LAST_SIB,
+                                                   &extent);
+                       if (retval)
+                               break;
+               }
+
+               retval = ext2fs_extent_get(ehandle, EXT2_EXTENT_NEXT, &extent);
+       } while (retval == 0);
+out:
+       ext2fs_extent_free(ehandle);
+       return e2fsck_should_rebuild_extents(ctx, pctx, &eti, &top_info);
+}
+
+/* Having scanned a file's extent tree, decide if we should rebuild it */
+errcode_t e2fsck_should_rebuild_extents(e2fsck_t ctx,
+                                  struct problem_context *pctx,
+                                  struct extent_tree_info *eti,
+                                  struct ext2_extent_info *info)
+{
+       struct extent_tree_level *ei;
+       int i, j, op;
+       unsigned int extents_per_block;
+
+       if (eti->force_rebuild)
+               goto rebuild;
+
+       extents_per_block = (ctx->fs->blocksize -
+                            sizeof(struct ext3_extent_header)) /
+                           sizeof(struct ext3_extent);
+       /*
+        * If we can consolidate a level or shorten the tree, schedule the
+        * extent tree to be rebuilt.
+        */
+       for (i = 0, ei = eti->ext_info; i < info->max_depth + 1; i++, ei++) {
+               if (ei->max_extents - ei->num_extents > extents_per_block) {
+                       pctx->blk = i;
+                       op = PR_1E_CAN_NARROW_EXTENT_TREE;
+                       goto rebuild;
+               }
+               for (j = 0; j < i; j++) {
+                       if (ei->num_extents < eti->ext_info[j].max_extents) {
+                               pctx->blk = i;
+                               op = PR_1E_CAN_COLLAPSE_EXTENT_TREE;
+                               goto rebuild;
+                       }
+               }
+       }
+       return 0;
+
+rebuild:
+       if (eti->force_rebuild || fix_problem(ctx, op, pctx))
+               return e2fsck_rebuild_extents_later(ctx, eti->ino);
+       return 0;
+}
+
+void e2fsck_pass1e(e2fsck_t ctx)
+{
+       rebuild_extents(ctx, "Pass 1E", PR_1E_PASS_HEADER);
+}
index b13be545baa23fdb4e0ca53553b7ea4f568e0b5e..5e906c1f565023ba3171ee5401cbcf2d1513be77 100644 (file)
@@ -56,6 +56,8 @@
 #define _INLINE_ inline
 #endif
 
+#undef DEBUG
+
 static int process_block(ext2_filsys fs, blk64_t       *blocknr,
                         e2_blkcnt_t blockcnt, blk64_t ref_blk,
                         int ref_offset, void *priv_data);
@@ -95,6 +97,7 @@ struct process_block_struct {
        ext2fs_block_bitmap fs_meta_blocks;
        e2fsck_t        ctx;
        region_t        region;
+       struct extent_tree_info eti;
 };
 
 struct process_inode_block {
@@ -1835,6 +1838,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                }
                e2fsck_pass1_dupblocks(ctx, block_buf);
        }
+       ctx->flags |= E2F_FLAG_ALLOC_OK;
        ext2fs_free_mem(&inodes_to_process);
 endit:
        e2fsck_use_inode_shortcuts(ctx, 0);
@@ -2486,6 +2490,23 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
        pctx->errcode = ext2fs_extent_get_info(ehandle, &info);
        if (pctx->errcode)
                return;
+       if (!(ctx->options & E2F_OPT_FIXES_ONLY) &&
+           !pb->eti.force_rebuild) {
+               struct extent_tree_level *etl;
+
+               etl = pb->eti.ext_info + info.curr_level;
+               etl->num_extents += info.num_entries;
+               etl->max_extents += info.max_entries;
+               /*
+                * Implementation wart: Splitting extent blocks when appending
+                * will leave the old block with one free entry.  Therefore
+                * unless the node is totally full, pretend that a non-root
+                * extent block can hold one fewer entry than it actually does,
+                * so that we don't repeatedly rebuild the extent tree.
+                */
+               if (info.curr_level && info.num_entries < info.max_entries)
+                       etl->max_extents--;
+       }
 
        pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_FIRST_SIB,
                                          &extent);
@@ -2822,11 +2843,27 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx,
 
        retval = ext2fs_extent_get_info(ehandle, &info);
        if (retval == 0) {
-               if (info.max_depth >= MAX_EXTENT_DEPTH_COUNT)
-                       info.max_depth = MAX_EXTENT_DEPTH_COUNT-1;
-               ctx->extent_depth_count[info.max_depth]++;
+               int max_depth = info.max_depth;
+
+               if (max_depth >= MAX_EXTENT_DEPTH_COUNT)
+                       max_depth = MAX_EXTENT_DEPTH_COUNT-1;
+               ctx->extent_depth_count[max_depth]++;
        }
 
+       /* Check maximum extent depth */
+       pctx->blk = info.max_depth;
+       pctx->blk2 = ext2fs_max_extent_depth(ehandle);
+       if (pctx->blk2 < pctx->blk &&
+           fix_problem(ctx, PR_1_EXTENT_BAD_MAX_DEPTH, pctx))
+               pb->eti.force_rebuild = 1;
+
+       /* Can we collect extent tree level stats? */
+       pctx->blk = MAX_EXTENT_DEPTH_COUNT;
+       if (pctx->blk2 > pctx->blk)
+               fix_problem(ctx, PR_1E_MAX_EXTENT_TREE_DEPTH, pctx);
+       memset(pb->eti.ext_info, 0, sizeof(pb->eti.ext_info));
+       pb->eti.ino = pb->ino;
+
        pb->region = region_create(0, info.max_lblk);
        if (!pb->region) {
                ext2fs_extent_free(ehandle);
@@ -2849,6 +2886,16 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx,
        region_free(pb->region);
        pb->region = NULL;
        ext2fs_extent_free(ehandle);
+
+       /* Rebuild unless it's a dir and we're rehashing it */
+       if (LINUX_S_ISDIR(inode->i_mode) &&
+           e2fsck_dir_will_be_rehashed(ctx, ino))
+               return;
+
+       if (ctx->options & E2F_OPT_CONVERT_BMAP)
+               e2fsck_rebuild_extents_later(ctx, ino);
+       else
+               e2fsck_should_rebuild_extents(ctx, pctx, &pb->eti, &info);
 }
 
 /*
@@ -2933,6 +2980,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        pb.pctx = pctx;
        pb.ctx = ctx;
        pb.inode_modified = 0;
+       pb.eti.force_rebuild = 0;
        pctx->ino = ino;
        pctx->errcode = 0;
 
@@ -2984,6 +3032,15 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                                                  "check_blocks");
                        fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) |
                                    (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
+
+                       if (ctx->options & E2F_OPT_CONVERT_BMAP) {
+#ifdef DEBUG
+                               printf("bmap rebuild ino=%d\n", ino);
+#endif
+                               if (!LINUX_S_ISDIR(inode->i_mode) ||
+                                   !e2fsck_dir_will_be_rehashed(ctx, ino))
+                                       e2fsck_rebuild_extents_later(ctx, ino);
+                       }
                }
        }
        end_problem_latch(ctx, PR_LATCH_BLOCK);
index 62fce25d97f9eaab96008f4c2db835ccbb9be377..7e8ac5b61d388d1db78387b3dc2e7bacffb92861 100644 (file)
@@ -1101,6 +1101,11 @@ static struct e2fsck_problem problem_table[] = {
          N_("@A memory for encrypted @d list\n"),
          PROMPT_NONE, PR_FATAL },
 
+       /* Inode extent tree could be more shallow */
+       { PR_1_EXTENT_BAD_MAX_DEPTH,
+         N_("@i %i @x tree could be more shallow (%b; could be <= %c)\n"),
+         PROMPT_FIX, PR_NO_OK | PR_PREEN_NO | PR_PREEN_OK },
+
        /* Pass 1b errors */
 
        /* Pass 1B: Rescan for duplicate/bad blocks */
@@ -1198,6 +1203,48 @@ static struct e2fsck_problem problem_table[] = {
        { PR_1D_CLONE_ERROR,
          N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
 
+       /* Pass 1E Extent tree optimization     */
+
+       /* Pass 1E: Optimizing extent trees */
+       { PR_1E_PASS_HEADER,
+         N_("Pass 1E: Optimizing @x trees\n"),
+         PROMPT_NONE, PR_PREEN_NOMSG },
+
+       /* Failed to optimize extent tree */
+       { PR_1E_OPTIMIZE_EXT_ERR,
+         N_("Failed to optimize @x tree %p (%i): %m\n"),
+         PROMPT_NONE, 0 },
+
+       /* Optimizing extent trees */
+       { PR_1E_OPTIMIZE_EXT_HEADER,
+         N_("Optimizing @x trees: "),
+         PROMPT_NONE, PR_MSG_ONLY },
+
+       /* Rebuilding extent tree %d */
+       { PR_1E_OPTIMIZE_EXT,
+         " %i",
+         PROMPT_NONE, PR_LATCH_OPTIMIZE_EXT | PR_PREEN_NOHDR},
+
+       /* Rebuilding extent tree end */
+       { PR_1E_OPTIMIZE_EXT_END,
+         "\n",
+         PROMPT_NONE, PR_PREEN_NOHDR },
+
+       /* Internal error: extent tree depth too large */
+       { PR_1E_MAX_EXTENT_TREE_DEPTH,
+         N_("Internal error: max extent tree depth too large (%b; expected=%c).\n"),
+         PROMPT_NONE, PR_FATAL },
+
+       /* Inode extent tree could be shorter */
+       { PR_1E_CAN_COLLAPSE_EXTENT_TREE,
+         N_("@i %i @x tree (at level %b) could be shorter.  "),
+         PROMPT_FIX, PR_NO_OK | PR_PREEN_NO | PR_PREEN_OK },
+
+       /* Inode extent tree could be narrower */
+       { PR_1E_CAN_NARROW_EXTENT_TREE,
+         N_("@i %i @x tree (at (level %b) could be narrower.  "),
+         PROMPT_FIX, PR_NO_OK | PR_PREEN_NO | PR_PREEN_OK },
+
        /* Pass 2 errors */
 
        /* Pass 2: Checking directory structure */
@@ -1946,6 +1993,7 @@ static struct latch_descr pr_latch_info[] = {
        { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
        { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
        { PR_LATCH_BG_CHECKSUM, PR_0_GDT_CSUM_LATCH, 0 },
+       { PR_LATCH_OPTIMIZE_EXT, PR_1E_OPTIMIZE_EXT_HEADER, PR_1E_OPTIMIZE_EXT_END },
        { -1, 0, 0 },
 };
 
index bc959c483199254cb5a4a0ef012ac3a031d5782f..24260217588a18ccd6e737ecebf4f4c6d7fe16c8 100644 (file)
@@ -40,6 +40,7 @@ struct problem_context {
 #define PR_LATCH_TOOBIG        0x0080  /* Latch for file to big errors */
 #define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
 #define PR_LATCH_BG_CHECKSUM 0x00A0  /* Latch for block group checksums */
+#define PR_LATCH_OPTIMIZE_EXT 0x00B0  /* Latch for rebuild extents */
 
 #define PR_LATCH(x)    ((((x) & PR_LATCH_MASK) >> 4) - 1)
 
@@ -644,6 +645,9 @@ struct problem_context {
 /* Error allocating memory for encrypted directory list */
 #define PR_1_ALLOCATE_ENCRYPTED_DIRLIST                0x01007E
 
+/* extent tree max depth too big */
+#define PR_1_EXTENT_BAD_MAX_DEPTH              0x01007F
+
 /*
  * Pass 1b errors
  */
@@ -706,6 +710,33 @@ struct problem_context {
 /* Couldn't clone file (error) */
 #define PR_1D_CLONE_ERROR      0x013008
 
+/*
+ * Pass 1e --- rebuilding extent trees
+ */
+/* Pass 1e: Rebuilding extent trees */
+#define PR_1E_PASS_HEADER              0x014000
+
+/* Error rehash directory */
+#define PR_1E_OPTIMIZE_EXT_ERR         0x014001
+
+/* Rebuilding extent trees */
+#define PR_1E_OPTIMIZE_EXT_HEADER      0x014002
+
+/* Rebuilding extent %d */
+#define PR_1E_OPTIMIZE_EXT             0x014003
+
+/* Rebuilding extent tree end */
+#define PR_1E_OPTIMIZE_EXT_END         0x014004
+
+/* Internal error: extent tree depth too large */
+#define PR_1E_MAX_EXTENT_TREE_DEPTH    0x014005
+
+/* Inode extent tree could be shorter */
+#define PR_1E_CAN_COLLAPSE_EXTENT_TREE 0x014006
+
+/* Inode extent tree could be narrower */
+#define PR_1E_CAN_NARROW_EXTENT_TREE   0x014007
+
 /*
  * Pass 2 errors
  */
@@ -1035,6 +1066,8 @@ struct problem_context {
 /* Rehashing dir end */
 #define PR_3A_OPTIMIZE_DIR_END         0x031005
 
+/* Pass 3B is really just 1E */
+
 /*
  * Pass 4 errors
  */
index 150d5e24c6b0f317eada26bf68085413a44a3653..3efa114a5f0264465a8ab9845da64923f140e12d 100644 (file)
@@ -762,11 +762,11 @@ static int write_dir_block(ext2_filsys fs,
 
 static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
                                 struct out_dir *outdir,
-                                ext2_ino_t ino, int compress)
+                                ext2_ino_t ino, struct ext2_inode *inode,
+                                int compress)
 {
        struct write_dir_struct wd;
        errcode_t       retval;
-       struct ext2_inode       inode;
 
        retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
        if (retval)
@@ -785,22 +785,23 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
        if (wd.err)
                return wd.err;
 
-       e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
+       e2fsck_read_inode(ctx, ino, inode, "rehash_dir");
        if (compress)
-               inode.i_flags &= ~EXT2_INDEX_FL;
+               inode->i_flags &= ~EXT2_INDEX_FL;
        else
-               inode.i_flags |= EXT2_INDEX_FL;
-       retval = ext2fs_inode_size_set(fs, &inode,
+               inode->i_flags |= EXT2_INDEX_FL;
+       retval = ext2fs_inode_size_set(fs, inode,
                                       outdir->num * fs->blocksize);
        if (retval)
                return retval;
-       ext2fs_iblk_sub_blocks(fs, &inode, wd.cleared);
-       e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
+       ext2fs_iblk_sub_blocks(fs, inode, wd.cleared);
+       e2fsck_write_inode(ctx, ino, inode, "rehash_dir");
 
        return 0;
 }
 
-errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
+errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino,
+                           struct problem_context *pctx)
 {
        ext2_filsys             fs = ctx->fs;
        errcode_t               retval;
@@ -911,10 +912,14 @@ resort:
                        goto errout;
        }
 
-       retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
+       retval = write_directory(ctx, fs, &outdir, ino, &inode, fd.compress);
        if (retval)
                goto errout;
 
+       if (ctx->options & E2F_OPT_CONVERT_BMAP)
+               retval = e2fsck_rebuild_extents_later(ctx, ino);
+       else
+               retval = e2fsck_check_rebuild_extents(ctx, ino, &inode, pctx);
 errout:
        free(dir_buf);
        free(fd.harray);
@@ -979,7 +984,7 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
 #if 0
                fix_problem(ctx, PR_3A_OPTIMIZE_DIR, &pctx);
 #endif
-               pctx.errcode = e2fsck_rehash_dir(ctx, ino);
+               pctx.errcode = e2fsck_rehash_dir(ctx, ino, &pctx);
                if (pctx.errcode) {
                        end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
                        fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
index 9eebd4da4aa698b493f4f077c177abb7366068b6..397ad0f96238964f60b13ad344ed321ff2c263e6 100644 (file)
@@ -606,6 +606,13 @@ void check_super_block(e2fsck_t ctx)
                ext2fs_mark_super_dirty(fs);
        }
 
+       /* Did user ask us to convert files to extents? */
+       if (ctx->options & E2F_OPT_CONVERT_BMAP) {
+               fs->super->s_feature_incompat |=
+                       EXT3_FEATURE_INCOMPAT_EXTENTS;
+               ext2fs_mark_super_dirty(fs);
+       }
+
        if ((fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
            (fs->super->s_first_meta_bg > fs->desc_blocks)) {
                pctx.group = fs->desc_blocks;
index 1d665a30cec9189ee49624cda1cbd9f186761101..5bc829af3f3cbaa51f6d2eb9ed9d4faf0bbdd57d 100644 (file)
@@ -709,6 +709,12 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
                        else
                                ctx->log_fn = string_copy(ctx, arg, 0);
                        continue;
+               } else if (strcmp(token, "bmap2extent") == 0) {
+                       ctx->options |= E2F_OPT_CONVERT_BMAP;
+                       continue;
+               } else if (strcmp(token, "fixes_only") == 0) {
+                       ctx->options |= E2F_OPT_FIXES_ONLY;
+                       continue;
                } else {
                        fprintf(stderr, _("Unknown extended option: %s\n"),
                                token);
@@ -728,6 +734,7 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
                fputs(("\tdiscard\n"), stderr);
                fputs(("\tnodiscard\n"), stderr);
                fputs(("\treadahead_kb=<buffer size>\n"), stderr);
+               fputs(("\tbmap2extent\n"), stderr);
                fputc('\n', stderr);
                exit(1);
        }
@@ -961,6 +968,22 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
        if (extended_opts)
                parse_extended_opts(ctx, extended_opts);
 
+       /* Complain about mutually exclusive rebuilding activities */
+       if (getenv("E2FSCK_FIXES_ONLY"))
+               ctx->options |= E2F_OPT_FIXES_ONLY;
+       if ((ctx->options & E2F_OPT_COMPRESS_DIRS) &&
+           (ctx->options & E2F_OPT_FIXES_ONLY)) {
+               com_err(ctx->program_name, 0, "%s",
+                       _("The -D and -E fixes_only options are incompatible."));
+               fatal_error(ctx, 0);
+       }
+       if ((ctx->options & E2F_OPT_CONVERT_BMAP) &&
+           (ctx->options & E2F_OPT_FIXES_ONLY)) {
+               com_err(ctx->program_name, 0, "%s",
+                       _("The -E bmap2extent and fixes_only options are incompatible."));
+               fatal_error(ctx, 0);
+       }
+
        if ((cp = getenv("E2FSCK_CONFIG")) != NULL)
                config_fn[0] = cp;
        profile_set_syntax_err_cb(syntax_err_report);
index 0c0bc287bd0c7835d08f09787d5f175c855e47b4..81c25a371de3fe5c4b4757fb605edfab92462db7 100644 (file)
@@ -2,8 +2,11 @@ Pass 1: Checking inodes, blocks, and sizes
 Inode 12 has an invalid extent node (blk 22, lblk 0)
 Clear? yes
 
+Inode 12 extent tree (at level 1) could be shorter.  Fix? yes
+
 Inode 12, i_blocks is 16, should be 8.  Fix? yes
 
+Pass 1E: Optimizing extent trees
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
@@ -11,13 +14,13 @@ Pass 5: Checking group summary information
 Block bitmap differences:  -(21--23) -25
 Fix? yes
 
-Free blocks count wrong for group #0 (71, counted=75).
+Free blocks count wrong for group #0 (73, counted=77).
 Fix? yes
 
-Free blocks count wrong (71, counted=75).
+Free blocks count wrong (73, counted=77).
 Fix? yes
 
 
 test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
-test_filesys: 12/16 files (0.0% non-contiguous), 25/100 blocks
+test_filesys: 12/16 files (0.0% non-contiguous), 23/100 blocks
 Exit status is 1
index 568c792ed932641cd456f732d8f9716ac7b23cd3..b78b19310d574442917c3fe153f2112de24d6428 100644 (file)
@@ -3,5 +3,5 @@ Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 12/16 files (0.0% non-contiguous), 25/100 blocks
+test_filesys: 12/16 files (0.0% non-contiguous), 23/100 blocks
 Exit status is 0
index 0e82e2bc4b4adb13ab73d0c86d7d696080f6ec38..52091aceba9691f31693fba099c60777d82fbd4a 100644 (file)
@@ -2,8 +2,11 @@ Pass 1: Checking inodes, blocks, and sizes
 Inode 12 has an invalid extent node (blk 1295, lblk 0)
 Clear? yes
 
+Inode 12 extent tree (at level 1) could be shorter.  Fix? yes
+
 Inode 12, i_blocks is 712, should be 0.  Fix? yes
 
+Pass 1E: Optimizing extent trees
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
index 7b6dbf1393321a3989a162d5e6bc89a2fd6264e4..4c1777c61272a7dc6452f4b9cde8b3de31052b1a 100644 (file)
@@ -2,8 +2,11 @@ Pass 1: Checking inodes, blocks, and sizes
 Inode 12 has an invalid extent node (blk 1604, lblk 0)
 Clear? yes
 
+Inode 12 extent tree (at level 1) could be shorter.  Fix? yes
+
 Inode 12, i_blocks is 18, should be 0.  Fix? yes
 
+Pass 1E: Optimizing extent trees
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
index 3164ea0e82556b65cf3f72dac2271ef681a522fa..59e1952cc6956630bb644971906417a4e22fd0d1 100644 (file)
@@ -3,8 +3,11 @@ Inode 12, end of extent exceeds allowed value
        (logical block 15, physical block 200, len 30)
 Clear? yes
 
+Inode 12 extent tree (at (level 1) could be narrower.  Fix? yes
+
 Inode 12, i_blocks is 154, should be 94.  Fix? yes
 
+Pass 1E: Optimizing extent trees
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
@@ -12,13 +15,13 @@ Pass 5: Checking group summary information
 Block bitmap differences:  -(200--229)
 Fix? yes
 
-Free blocks count wrong for group #0 (156, counted=186).
+Free blocks count wrong for group #0 (158, counted=188).
 Fix? yes
 
-Free blocks count wrong (156, counted=186).
+Free blocks count wrong (158, counted=188).
 Fix? yes
 
 
 test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
-test_filesys: 12/32 files (8.3% non-contiguous), 70/256 blocks
+test_filesys: 12/32 files (8.3% non-contiguous), 68/256 blocks
 Exit status is 1
index 22c4f2cc358efd59153a655424c4937659a3276d..0729283168335766617f151e0f84efae6f060e09 100644 (file)
@@ -3,5 +3,5 @@ Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 12/32 files (8.3% non-contiguous), 70/256 blocks
+test_filesys: 12/32 files (8.3% non-contiguous), 68/256 blocks
 Exit status is 0
index aeebc7b18ae65d5aa5b4dd48a1ec05856423157f..e9d9e5b3f967ffd309a61e6599fa605321010c8a 100644 (file)
@@ -6,6 +6,8 @@ Inode 12 has an invalid extent
        (logical block 0, invalid physical block 21994527527949, len 17)
 Clear? yes
 
+Inode 12 extent tree (at level 1) could be shorter.  Fix? yes
+
 Inode 12, i_blocks is 34, should be 0.  Fix? yes
 
 Inode 13 missing EXTENT_FL, but is in extents format
@@ -21,6 +23,8 @@ Inode 17 has an invalid extent
        (logical block 0, invalid physical block 22011707397135, len 15)
 Clear? yes
 
+Inode 17 extent tree (at level 1) could be shorter.  Fix? yes
+
 Inode 17, i_blocks is 32, should be 0.  Fix? yes
 
 Error while reading over extent tree in inode 18: Corrupt extent header
@@ -31,6 +35,7 @@ Inode 18, i_blocks is 2, should be 0.  Fix? yes
 Special (device/socket/fifo) file (inode 19) has extents
 or inline-data flag set.  Clear? yes
 
+Pass 1E: Optimizing extent trees
 Pass 2: Checking directory structure
 Entry 'fbad-flag' in / (2) has deleted/unused inode 18.  Clear? yes