Whamcloud - gitweb
libext2fs: don't cache inodes that fail checksum verification
authorDarrick J. Wong <darrick.wong@oracle.com>
Sun, 3 Aug 2014 02:49:12 +0000 (22:49 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 3 Aug 2014 02:49:23 +0000 (22:49 -0400)
If an inode fails checksum verification, don't stuff a copy of it in
the inode cache, because this can cause the library to fail to return
the "corrupt inode" error code.

In general, this happens if ext2fs_read_inode_full() is called twice
on an inode with an incorrect checksum.  If fs->flags has
EXT2_FLAG_IGNORE_CSUM_ERRORS set during the first call and *unset*
during the second call, the cache hit during the second call fails to
return EXT2_ET_INODE_CSUM_INVALID as you'd expect.  This happens
during fsck because the first read_inode call happens as part of
check_blocks and the second call happens during inode checksum
revalidation.  A file system with a slightly corrupt non-extent inode
will trigger this.

While we're at it, make the inode read function consistent with the
rest of libext2fs -- copy the metadata object into the caller's buffer
even if it fails checksum verification.  This will help e2fsck avoid a
double re-read later on down the line.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
lib/ext2fs/inode.c
tests/f_no_cache_corrupt_inode/expect.1 [new file with mode: 0644]
tests/f_no_cache_corrupt_inode/expect.2 [new file with mode: 0644]
tests/f_no_cache_corrupt_inode/image.gz [new file with mode: 0644]
tests/f_no_cache_corrupt_inode/name [new file with mode: 0644]

index a6213ae..6c766af 100644 (file)
@@ -709,7 +709,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
        io_channel      io;
        int             length = EXT2_INODE_SIZE(fs->super);
        struct ext2_inode_large *iptr;
-       int             cache_slot;
+       int             cache_slot, fail_csum;
 
        EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -787,9 +787,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
        length = EXT2_INODE_SIZE(fs->super);
 
        /* Verify the inode checksum. */
-       if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
-           !ext2fs_inode_csum_verify(fs, ino, iptr))
-               return EXT2_ET_INODE_CSUM_INVALID;
+       fail_csum = !ext2fs_inode_csum_verify(fs, ino, iptr);
 
 #ifdef WORDS_BIGENDIAN
        ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
@@ -798,10 +796,15 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
 #endif
 
        /* Update the inode cache bookkeeping */
-       fs->icache->cache_last = cache_slot;
-       fs->icache->cache[cache_slot].ino = ino;
+       if (!fail_csum) {
+               fs->icache->cache_last = cache_slot;
+               fs->icache->cache[cache_slot].ino = ino;
+       }
        memcpy(inode, iptr, (bufsize > length) ? length : bufsize);
 
+       if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && fail_csum)
+               return EXT2_ET_INODE_CSUM_INVALID;
+
        return 0;
 }
 
diff --git a/tests/f_no_cache_corrupt_inode/expect.1 b/tests/f_no_cache_corrupt_inode/expect.1
new file mode 100644 (file)
index 0000000..4f82f75
--- /dev/null
@@ -0,0 +1,11 @@
+Pass 1: Checking inodes, blocks, and sizes
+Inode 12 passes checks, but checksum does not match inode.  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: 12/128 files (0.0% non-contiguous), 19/512 blocks
+Exit status is 1
diff --git a/tests/f_no_cache_corrupt_inode/expect.2 b/tests/f_no_cache_corrupt_inode/expect.2
new file mode 100644 (file)
index 0000000..1b43315
--- /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: 12/128 files (0.0% non-contiguous), 19/512 blocks
+Exit status is 0
diff --git a/tests/f_no_cache_corrupt_inode/image.gz b/tests/f_no_cache_corrupt_inode/image.gz
new file mode 100644 (file)
index 0000000..e17e921
Binary files /dev/null and b/tests/f_no_cache_corrupt_inode/image.gz differ
diff --git a/tests/f_no_cache_corrupt_inode/name b/tests/f_no_cache_corrupt_inode/name
new file mode 100644 (file)
index 0000000..fb213e2
--- /dev/null
@@ -0,0 +1 @@
+don't cache inodes that fail checksum verification