# This is a BitKeeper generated patch for the following project: # Project Name: Ext2 filesystem utilities # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.1020 -> 1.1021 # e2fsck/pass2.c 1.45 -> 1.46 # e2fsck/ChangeLog 1.281 -> 1.282 # e2fsck/unix.c 1.66 -> 1.67 # e2fsck/rehash.c 1.3 -> 1.4 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/09/28 tytso@think.thunk.org 1.1021 # Add a more sophisticated algorithm to e2fsck to salvage corrupted # 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. # -------------------------------------------- # diff -Nru a/e2fsck/ChangeLog b/e2fsck/ChangeLog --- a/e2fsck/ChangeLog Sat Sep 28 09:50:43 2002 +++ b/e2fsck/ChangeLog Sat Sep 28 09:50:43 2002 @@ -1,3 +1,20 @@ +2002-09-28 Theodore Ts'o + + * 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 * Release of E2fsprogs 1.29 diff -Nru a/e2fsck/pass2.c b/e2fsck/pass2.c --- a/e2fsck/pass2.c Sat Sep 28 09:50:43 2002 +++ b/e2fsck/pass2.c Sat Sep 28 09:50:43 2002 @@ -574,6 +574,55 @@ } #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 @@ #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 @@ } #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 @@ ((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 @@ } } - 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 @@ * 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 @@ * 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 @@ * 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 @@ * 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 @@ * 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 @@ 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"); diff -Nru a/e2fsck/rehash.c b/e2fsck/rehash.c --- a/e2fsck/rehash.c Sat Sep 28 09:50:43 2002 +++ b/e2fsck/rehash.c Sat Sep 28 09:50:43 2002 @@ -522,7 +522,9 @@ 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 @@ 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; diff -Nru a/e2fsck/unix.c b/e2fsck/unix.c --- a/e2fsck/unix.c Sat Sep 28 09:50:43 2002 +++ b/e2fsck/unix.c Sat Sep 28 09:50:43 2002 @@ -974,8 +974,11 @@ 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 @@ 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;