summaryrefslogtreecommitdiff
path: root/e2fsck
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2007-04-02 10:08:59 -0400
committerTheodore Ts'o <tytso@mit.edu>2007-04-02 10:08:59 -0400
commitfbc3f90194ca557e377c25f71db242c38c78f836 (patch)
tree1abe334b7af80fc5742d007289024f5c1a6f149b /e2fsck
parentccfbb266d6ca61c3c94ef6f391bb6384b8c58841 (diff)
downloade2fsprogs-fbc3f90194ca557e377c25f71db242c38c78f836.tar.gz
e2fsck: Recover a special file which looks an awful lot like a directory
This is probably only useful in artificial test cases, but it will be useful if we ever do the "inodes in directory" idea for ext4. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'e2fsck')
-rw-r--r--e2fsck/ChangeLog10
-rw-r--r--e2fsck/pass1.c69
-rw-r--r--e2fsck/problem.c5
-rw-r--r--e2fsck/problem.h3
4 files changed, 87 insertions, 0 deletions
diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog
index bb0f6b5f..1eb1064d 100644
--- a/e2fsck/ChangeLog
+++ b/e2fsck/ChangeLog
@@ -1,5 +1,15 @@
2007-03-31 Theodore Tso <tytso@mit.edu>
+ * pass1.c (e2fsck_pass1, check_is_really_dir): Check for an edge
+ condition where the mode of a directory is incorrect, and
+ looks like a special device, but it is really a directory.
+ We can't do this for regular files because of the
+ performance hit, but this will catch directories which
+ have their i_mode bits mutated so they looks like a
+ special device.
+
+ * problem.c, problem.h (PR_1_TREAT_AS_DIRECTORY): New problem code
+
* message.c (expand_percent_expression): Add support for %It, which
will print the type of the inode.
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 9162b668..6f816dff 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -372,6 +372,74 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
}
}
+/*
+ * Check to see if the inode might really be a directory, despite i_mode
+ *
+ * This is a lot of complexity for something for which I'm not really
+ * convinced happens frequently in the wild. If for any reason this
+ * causes any problems, take this code out.
+ * [tytso:20070331.0827EDT]
+ */
+static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
+ char *buf)
+{
+ struct ext2_inode *inode = pctx->inode;
+ int i, not_device = 0;
+ blk_t blk;
+ struct ext2_dir_entry *dirent;
+
+ if (LINUX_S_ISDIR(inode->i_mode) || LINUX_S_ISREG(inode->i_mode) ||
+ inode->i_block[0] == 0)
+ return;
+
+ for (i=1; i < EXT2_N_BLOCKS; i++) {
+ blk = inode->i_block[i];
+ if (!blk)
+ continue;
+ if (i >= 4)
+ not_device++;
+
+ if (blk < ctx->fs->super->s_first_data_block ||
+ blk >= ctx->fs->super->s_blocks_count ||
+ ext2fs_fast_test_block_bitmap(ctx->block_found_map, blk))
+ return; /* Invalid block, can't be dir */
+ }
+
+ if ((LINUX_S_ISCHR(inode->i_mode) || LINUX_S_ISBLK(inode->i_mode)) &&
+ (inode->i_links_count == 1) && !not_device)
+ return;
+
+ if (LINUX_S_ISLNK(inode->i_mode) && inode->i_links_count == 1)
+ return;
+
+ if (ext2fs_read_dir_block(ctx->fs, inode->i_block[0], buf))
+ return;
+
+ dirent = (struct ext2_dir_entry *) buf;
+ if (((dirent->name_len & 0xFF) != 1) ||
+ (dirent->name[0] != '.') ||
+ (dirent->inode != pctx->ino) ||
+ (dirent->rec_len < 12) ||
+ (dirent->rec_len % 4) ||
+ (dirent->rec_len >= ctx->fs->blocksize - 12))
+ return;
+
+ dirent = (struct ext2_dir_entry *) (buf + dirent->rec_len);
+ if (((dirent->name_len & 0xFF) != 2) ||
+ (dirent->name[0] != '.') ||
+ (dirent->name[1] != '.') ||
+ (dirent->rec_len < 12) ||
+ (dirent->rec_len % 4))
+ return;
+
+ if (fix_problem(ctx, PR_1_TREAT_AS_DIRECTORY, pctx)) {
+ inode->i_mode = (inode->i_mode & 07777) | LINUX_S_IFDIR;
+ e2fsck_write_inode_full(ctx, pctx->ino, inode,
+ EXT2_INODE_SIZE(ctx->fs->super),
+ "check_is_really_dir");
+ }
+}
+
void e2fsck_pass1(e2fsck_t ctx)
{
int i;
@@ -769,6 +837,7 @@ void e2fsck_pass1(e2fsck_t ctx)
}
check_inode_extra_space(ctx, &pctx);
+ check_is_really_dir(ctx, &pctx, block_buf);
if (LINUX_S_ISDIR(inode->i_mode)) {
ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index cb535ad0..9f4af509 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -779,6 +779,11 @@ static struct e2fsck_problem problem_table[] = {
N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"),
PROMPT_CLEAR, PR_PREEN_OK },
+ /* inode appears to be a directory */
+ { PR_1_TREAT_AS_DIRECTORY,
+ N_("@i %i is a %It but it looks like it is really a directory.\n"),
+ PROMPT_FIX, 0 },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index e5070b05..560907ac 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -452,6 +452,9 @@ struct problem_context {
/* wrong EA hash value */
#define PR_1_ATTR_HASH 0x010054
+/* inode appears to be a directory */
+#define PR_1_TREAT_AS_DIRECTORY 0x010055
+
/*
* Pass 1b errors
*/