]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck: Recover a special file which looks an awful lot like a directory
authorTheodore Ts'o <tytso@mit.edu>
Mon, 2 Apr 2007 14:08:59 +0000 (10:08 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 2 Apr 2007 14:08:59 +0000 (10:08 -0400)
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>
e2fsck/ChangeLog
e2fsck/pass1.c
e2fsck/problem.c
e2fsck/problem.h
tests/ChangeLog
tests/f_dir_bad_mode/expect.1 [new file with mode: 0644]
tests/f_dir_bad_mode/expect.2 [new file with mode: 0644]
tests/f_dir_bad_mode/image.gz [new file with mode: 0644]
tests/f_dir_bad_mode/name [new file with mode: 0644]

index bb0f6b5fe29a2312872da992106418a699b4c975..1eb1064dd24f14c07e1852cf7b235c6bc0136662 100644 (file)
@@ -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.
 
index 9162b668b4d5606d83fb7d059749654cafbabb28..6f816dff5a8eb96a286e6beede89a6363a5e59bd 100644 (file)
@@ -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);
index cb535ad0189b94598a700fcc2e715ad04afdfef8..9f4af50973065eee2ba309741c61034e40dab283 100644 (file)
@@ -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 */
index e5070b05a25ce8be39defd747aadbce198ae179b..560907ac7c20c3ad090855dedb1a507fb241e828 100644 (file)
@@ -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
  */
index d77113840f3893af49007f32fb94ac4db167846f..98e1b7533889387185d94c93c0b3bb8da8e17c7d 100644 (file)
@@ -1,3 +1,7 @@
+2007-04-01  Theodore Tso  <tytso@mit.edu>
+
+       * f_dir_bad_mode: New test case.
+
 2007-03-31  Theodore Tso  <tytso@mit.edu>
 
        * f_orphan_dotdot_ft: New test case which checks to see what
diff --git a/tests/f_dir_bad_mode/expect.1 b/tests/f_dir_bad_mode/expect.1
new file mode 100644 (file)
index 0000000..10b4a99
--- /dev/null
@@ -0,0 +1,12 @@
+Pass 1: Checking inodes, blocks, and sizes
+Inode 12 is a socket but it looks like it is really a directory.
+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: 15/16 files (0.0% non-contiguous), 25/100 blocks
+Exit status is 1
diff --git a/tests/f_dir_bad_mode/expect.2 b/tests/f_dir_bad_mode/expect.2
new file mode 100644 (file)
index 0000000..7d2352d
--- /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: 15/16 files (0.0% non-contiguous), 25/100 blocks
+Exit status is 0
diff --git a/tests/f_dir_bad_mode/image.gz b/tests/f_dir_bad_mode/image.gz
new file mode 100644 (file)
index 0000000..2f647a1
Binary files /dev/null and b/tests/f_dir_bad_mode/image.gz differ
diff --git a/tests/f_dir_bad_mode/name b/tests/f_dir_bad_mode/name
new file mode 100644 (file)
index 0000000..7cbacc2
--- /dev/null
@@ -0,0 +1 @@
+directory with corrupted i_mode