]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
Add a more sophisticated algorithm to e2fsck to salvage corrupted
authorTheodore Ts'o <tytso@mit.edu>
Sat, 28 Sep 2002 13:16:28 +0000 (09:16 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sat, 28 Sep 2002 13:16:28 +0000 (09:16 -0400)
directories.

Speed up e2fsck slightly by only updating the master superblock;
there is no point to update the backup superblocks.

Fix a small bug in the rehashing code which could leave the indexed
flag set even after the directory was compressed instead of indexed.
(Not fatal, since the kernel will deal with this, but technically
it filesystem isn't consistent, and the filesystem will be marked
as being in error when the kernel comes across the directory.  It
should also never happen in real life, since directories that small
will never be indexed, but better safe than sorry.)

Also change the threshold of when directories are indexed, so that
directories of size 2 blocks will be indexed.  Otherwise they will
never be indexed by the kernel when they grow.

e2fsck/ChangeLog
e2fsck/pass2.c
e2fsck/rehash.c
e2fsck/unix.c

index 7fbc70f77da2b07a4ea7dc8b5709b6664f680b5b..7fe81f18a10eae42d2a8b4626647854673d9ccda 100644 (file)
@@ -1,3 +1,20 @@
+2002-09-28  Theodore Ts'o  <tytso@mit.edu>
+
+       * rehash.c (write_directory): Clear the index flag if by
+               reoptimizing the directory, we bring it back into a
+               non-indexed state.
+               (e2fsck_rehash_dir): Allow directories that contain two
+               blocks to be indexed.  Otherwise when they grow, they
+               never will be indexed by the kernel.
+
+       * unix.c (main): Only update the master superblock; there's no
+               point updating the backup superblocks, and it speeds up
+               fsck slightly.
+
+       * pass2.c (salvage_directory): New function called by
+               check_dir_block() which is much more sophisticated about
+               how it salvages corrupted filesystems.
+
 2001-09-24  Theodore Tso  <tytso@mit.edu>
 
        * Release of E2fsprogs 1.29
index 329107bfb048db7f9fa9f28e2d29cca5945974b9..f3f2f9e12928d1958b9faa62f52a7f077717e3be 100644 (file)
@@ -574,6 +574,55 @@ static void parse_int_node(ext2_filsys fs,
 }
 #endif /* ENABLE_HTREE */
 
+/*
+ * Given a busted directory, try to salvage it somehow.
+ * 
+ */
+static int salvage_directory(ext2_filsys fs,
+                             struct ext2_dir_entry *dirent,
+                             struct ext2_dir_entry *prev,
+                             int offset)
+{
+       char    *cp = (char *) dirent;
+       int left = fs->blocksize - offset - dirent->rec_len;
+       int prev_offset = offset - ((char *) dirent - (char *) prev);
+
+       /*
+        * Special case of directory entry of size 8: copy what's left
+        * of the directory block up to cover up the invalid hole.
+        */
+       if ((left >= 12) && (dirent->rec_len == 8)) {
+               memmove(cp, cp+8, left);
+               memset(cp + left, 0, 8);
+               return offset;
+       }
+       /*
+        * If the directory entry is a multiple of four, so it is
+        * valid, let the previous directory entry absorb the invalid
+        * one. 
+        */
+       if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
+               prev->rec_len += dirent->rec_len;
+               return prev_offset;
+       }
+       /*
+        * Default salvage method --- kill all of the directory
+        * entries for the rest of the block.  We will either try to
+        * absorb it into the previous directory entry, or create a
+        * new empty directory entry the rest of the directory block.
+        */
+       if (prev) {
+               prev->rec_len += fs->blocksize - offset;
+               return prev_offset;
+       } else {
+               dirent->rec_len = fs->blocksize - offset;
+               dirent->name_len = 0;
+               dirent->inode = 0;
+               return offset;
+       }
+       
+}
+
 static int check_dir_block(ext2_filsys fs,
                           struct ext2_db_entry *db,
                           void *priv_data)
@@ -583,7 +632,7 @@ static int check_dir_block(ext2_filsys fs,
 #ifdef ENABLE_HTREE
        struct dx_dirblock_info *dx_db = 0;
 #endif /* ENABLE_HTREE */
-       struct ext2_dir_entry   *dirent;
+       struct ext2_dir_entry   *dirent, *prev;
        ext2_dirhash_t          hash;
        int                     offset = 0;
        int                     dir_modified = 0;
@@ -680,8 +729,8 @@ static int check_dir_block(ext2_filsys fs,
        }
 #endif /* ENABLE_HTREE */
 
+       prev = 0;
        do {
-               dot_state++;
                problem = 0;
                dirent = (struct ext2_dir_entry *) (buf + offset);
                cd->pctx.dirent = dirent;
@@ -691,10 +740,10 @@ static int check_dir_block(ext2_filsys fs,
                    ((dirent->rec_len % 4) != 0) ||
                    (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
                        if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
-                               dirent->rec_len = fs->blocksize - offset;
-                               dirent->name_len = 0;
-                               dirent->inode = 0;
+                               offset = salvage_directory(fs, dirent,
+                                                          prev, offset);
                                dir_modified++;
+                               continue;
                        } else
                                return DIRENT_ABORT;
                }
@@ -705,10 +754,10 @@ static int check_dir_block(ext2_filsys fs,
                        }
                }
 
-               if (dot_state == 1) {
+               if (dot_state == 0) {
                        if (check_dot(ctx, dirent, ino, &cd->pctx))
                                dir_modified++;
-               } else if (dot_state == 2) {
+               } else if (dot_state == 1) {
                        dir = e2fsck_get_dir_info(ctx, ino);
                        if (!dir) {
                                fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
@@ -749,7 +798,7 @@ static int check_dir_block(ext2_filsys fs,
                         * clear it.
                         */
                        problem = PR_2_BB_INODE;
-               } else if ((dot_state > 2) &&
+               } else if ((dot_state > 1) &&
                           ((dirent->name_len & 0xFF) == 1) &&
                           (dirent->name[0] == '.')) {
                        /*
@@ -758,7 +807,7 @@ static int check_dir_block(ext2_filsys fs,
                         * duplicate entry that should be removed.
                         */
                        problem = PR_2_DUP_DOT;
-               } else if ((dot_state > 2) &&
+               } else if ((dot_state > 1) &&
                           ((dirent->name_len & 0xFF) == 2) &&
                           (dirent->name[0] == '.') && 
                           (dirent->name[1] == '.')) {
@@ -768,7 +817,7 @@ static int check_dir_block(ext2_filsys fs,
                         * duplicate entry that should be removed.
                         */
                        problem = PR_2_DUP_DOT_DOT;
-               } else if ((dot_state > 2) &&
+               } else if ((dot_state > 1) &&
                           (dirent->inode == EXT2_ROOT_INO)) {
                        /*
                         * Don't allow links to the root directory.
@@ -777,7 +826,7 @@ static int check_dir_block(ext2_filsys fs,
                         * directory hasn't been created yet.
                         */
                        problem = PR_2_LINK_ROOT;
-               } else if ((dot_state > 2) &&
+               } else if ((dot_state > 1) &&
                           (dirent->name_len & 0xFF) == 0) {
                        /*
                         * Don't allow zero-length directory names.
@@ -842,7 +891,7 @@ static int check_dir_block(ext2_filsys fs,
                 * hard link.  We assume the first link is correct,
                 * and ask the user if he/she wants to clear this one.
                 */
-               if ((dot_state > 2) &&
+               if ((dot_state > 1) &&
                    (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
                                              dirent->inode))) {
                        subdir = e2fsck_get_dir_info(ctx, dirent->inode);
@@ -871,7 +920,9 @@ static int check_dir_block(ext2_filsys fs,
                        ctx->fs_links_count++;
                ctx->fs_total_count++;
        next:
+               prev = dirent;
                offset += dirent->rec_len;
+               dot_state++;
        } while (offset < fs->blocksize);
 #if 0
        printf("\n");
index 4ceb3e5812940c206ef06b6da6e7041bcd84360e..dcec6a5713eab0ac3aca049bb4e2f3f4df896e9d 100644 (file)
@@ -522,7 +522,9 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
                return wd.err;
 
        e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
-       if (!compress)
+       if (compress)
+               inode.i_flags &= ~EXT2_INDEX_FL;
+       else
                inode.i_flags |= EXT2_INDEX_FL;
        inode.i_size = outdir->num * fs->blocksize;
        inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
@@ -561,7 +563,7 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
        fd.dir_size = 0;
        fd.compress = 0;
        if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
-           (inode.i_size / fs->blocksize) < 3)
+           (inode.i_size / fs->blocksize) < 2)
                fd.compress = 1;
        fd.parent = 0;
 
index 156d25b1ba40e2fa4f65ca24b2a7215957a7559f..4de0637f10d85e2bbbbeecf2c77ed247877d1a3d 100644 (file)
@@ -974,8 +974,11 @@ restart:
                ext2fs_mark_super_dirty(fs);
 
        /*
-        * Don't overwrite the backup superblock and block
-        * descriptors, until we're sure the filesystem is OK....
+        * We only update the master superblock because (a) paranoia;
+        * we don't want to corrupt the backup superblocks, and (b) we
+        * don't need to update the mount count and last checked
+        * fields in the backup superblock (the kernel doesn't
+        * update the backup superblocks anyway).
         */
        fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
 
@@ -1058,9 +1061,7 @@ restart:
                        exit_value |= FSCK_REBOOT;
                }
        }
-       if (ext2fs_test_valid(fs))
-               fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
-       else {
+       if (!ext2fs_test_valid(fs)) {
                printf(_("\n%s: ********** WARNING: Filesystem still has "
                         "errors **********\n\n"), ctx->device_name);
                exit_value |= FSCK_UNCORRECTED;