summaryrefslogtreecommitdiff
path: root/e2fsck
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2002-09-28 09:16:28 -0400
committerTheodore Ts'o <tytso@mit.edu>2002-09-28 09:16:28 -0400
commite70ae99e077f5085c15a4526028e2aac0e91d7c1 (patch)
tree5e293cb21b1cb5cb7d33027bb63f51fd2a05b2e7 /e2fsck
parent0a3a541509b39003445c50b879c0037f14ff2c70 (diff)
downloade2fsprogs-e70ae99e077f5085c15a4526028e2aac0e91d7c1.tar.gz
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.
Diffstat (limited to 'e2fsck')
-rw-r--r--e2fsck/ChangeLog17
-rw-r--r--e2fsck/pass2.c75
-rw-r--r--e2fsck/rehash.c6
-rw-r--r--e2fsck/unix.c11
4 files changed, 90 insertions, 19 deletions
diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog
index 7fbc70f7..7fe81f18 100644
--- a/e2fsck/ChangeLog
+++ b/e2fsck/ChangeLog
@@ -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
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 329107bf..f3f2f9e1 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -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");
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 4ceb3e58..dcec6a57 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -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;
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 156d25b1..4de0637f 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -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;