Whamcloud - gitweb
e2fsck: collapse holes in extent-based directories
authorDarrick J. Wong <darrick.wong@oracle.com>
Fri, 18 Jul 2014 22:54:30 +0000 (15:54 -0700)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 25 Jul 2014 12:30:11 +0000 (08:30 -0400)
If we notice a hole in the block map of an extent-based directory,
offer to collapse the hole by decreasing the logical block # of the
extent.  This saves us from pass 3's inefficient strategy, which fills
the holes by mapping in a lot of empty directory blocks.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
e2fsck/pass1.c
e2fsck/problem.c
e2fsck/problem.h
tests/f_holedir2/expect.1
tests/f_holedir3/expect.1 [new file with mode: 0644]
tests/f_holedir3/expect.2 [new file with mode: 0644]
tests/f_holedir3/image.gz [new file with mode: 0644]
tests/f_holedir3/name [new file with mode: 0644]

index cf36a08..87e6d34 100644 (file)
@@ -1927,6 +1927,45 @@ report_problem:
                        }
                        pb->fragmented = 1;
                }
+               /*
+                * If we notice a gap in the logical block mappings of an
+                * extent-mapped directory, offer to close the hole by
+                * moving the logical block down, otherwise we'll go mad in
+                * pass 3 allocating empty directory blocks to fill the hole.
+                */
+               if (is_dir &&
+                   pb->last_block + 1 < (e2_blkcnt_t)extent.e_lblk) {
+                       blk64_t new_lblk;
+
+                       new_lblk = pb->last_block + 1;
+                       if (EXT2FS_CLUSTER_RATIO(ctx->fs) > 1)
+                               new_lblk = ((new_lblk +
+                                            EXT2FS_CLUSTER_RATIO(ctx->fs)) &
+                                           EXT2FS_CLUSTER_MASK(ctx->fs)) |
+                                          (extent.e_lblk &
+                                           EXT2FS_CLUSTER_MASK(ctx->fs));
+                       pctx->blk = extent.e_lblk;
+                       pctx->blk2 = new_lblk;
+                       if (fix_problem(ctx, PR_1_COLLAPSE_DBLOCK, pctx)) {
+                               extent.e_lblk = new_lblk;
+                               pb->inode_modified = 1;
+                               pctx->errcode = ext2fs_extent_replace(ehandle,
+                                                               0, &extent);
+                               if (pctx->errcode) {
+                                       pctx->errcode = 0;
+                                       goto alloc_later;
+                               }
+                               pctx->errcode = ext2fs_extent_fix_parents(ehandle);
+                               if (pctx->errcode)
+                                       goto failed_add_dir_block;
+                               pctx->errcode = ext2fs_extent_goto(ehandle,
+                                                               extent.e_lblk);
+                               if (pctx->errcode)
+                                       goto failed_add_dir_block;
+                               last_lblk = extent.e_lblk + extent.e_len - 1;
+                       }
+               }
+alloc_later:
                while (is_dir && (++pb->last_db_block <
                                  (e2_blkcnt_t) extent.e_lblk)) {
                        pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist,
index 4c105bb..837d111 100644 (file)
@@ -967,6 +967,11 @@ static struct e2fsck_problem problem_table[] = {
          PROMPT_CLEAR, 0 },
 
 
+       /* Directory inode block <block> should be at block <otherblock> */
+       { PR_1_COLLAPSE_DBLOCK,
+         N_("@d @i %i @b %b should be at @b %c.  "),
+         PROMPT_FIX, 0 },
+
        /* Pass 1b errors */
 
        /* Pass 1B: Rescan for duplicate/bad blocks */
index 6cb09cf..d3e66ad 100644 (file)
@@ -624,6 +624,9 @@ struct problem_context {
 /* Couldn't clone file (error) */
 #define PR_1D_CLONE_ERROR      0x013008
 
+/* Directory inode has a missing block (hole) */
+#define PR_1_COLLAPSE_DBLOCK           0x010072
+
 /*
  * Pass 2 errors
  */
index 5124f61..455f4b0 100644 (file)
@@ -1,21 +1,19 @@
 Pass 1: Checking inodes, blocks, and sizes
 Inode 12, i_size is 0, should be 5120.  Fix? yes
 
-Inode 13, i_size is 4096, should be 5120.  Fix? yes
+Directory inode 13 block 2 should be at block 1.  Fix? yes
 
 Pass 2: Checking directory structure
 Directory inode 12 has an unallocated block #3.  Allocate? yes
 
-Directory inode 13 has an unallocated block #1.  Allocate? yes
-
 Pass 3: Checking directory connectivity
 Pass 3A: Optimizing directories
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-Free blocks count wrong for group #0 (79, counted=77).
+Free blocks count wrong for group #0 (78, counted=77).
 Fix? yes
 
-Free blocks count wrong (79, counted=77).
+Free blocks count wrong (78, counted=77).
 Fix? yes
 
 
diff --git a/tests/f_holedir3/expect.1 b/tests/f_holedir3/expect.1
new file mode 100644 (file)
index 0000000..074ca6c
--- /dev/null
@@ -0,0 +1,13 @@
+Pass 1: Checking inodes, blocks, and sizes
+Directory inode 12 block 5 should be at block 2.  Fix? yes
+
+Inode 12, i_size is 6144, should be 3072.  Fix? yes
+
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 17/128 files (5.9% non-contiguous), 1093/2048 blocks
+Exit status is 1
diff --git a/tests/f_holedir3/expect.2 b/tests/f_holedir3/expect.2
new file mode 100644 (file)
index 0000000..b675e6d
--- /dev/null
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 17/128 files (5.9% non-contiguous), 1093/2048 blocks
+Exit status is 0
diff --git a/tests/f_holedir3/image.gz b/tests/f_holedir3/image.gz
new file mode 100644 (file)
index 0000000..c5eeb37
Binary files /dev/null and b/tests/f_holedir3/image.gz differ
diff --git a/tests/f_holedir3/name b/tests/f_holedir3/name
new file mode 100644 (file)
index 0000000..a526787
--- /dev/null
@@ -0,0 +1,2 @@
+real directories with holes and zero i_size
+