]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck: make insert_dirent_tail more robust
authorDarrick J. Wong <darrick.wong@oracle.com>
Sun, 27 Jul 2014 23:46:15 +0000 (19:46 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 27 Jul 2014 23:46:15 +0000 (19:46 -0400)
Fix the routine that adds dirent checksum structures to the directory
block to handle oddball situations a bit more robustly.

First, when we're walking the entry array, we might encounter an
entry that ends exactly one byte before where the checksum entry needs
to start, i.e. there's space for the tail entry, but it needs to be
reinitialized.  When that happens, we should proceed until d points to
that space so that the tail entry can be initialized.

Second, it's possible that we've been fed a directory block where the
entries end just short of the end of the block.  In this case, we need
to adjust the size of the last entry to point exactly to where the
dirent tail starts.  The current code requires that entries end
exactly on the block boundary, but this is not always the case with
damaged filesystems.

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

index 4774f0653df0aea1c9a710d4de5d777b3fd62299..69ebab6fef3f52546ad94c2d5d417bc87ac30fbf 100644 (file)
@@ -740,6 +740,7 @@ static int is_last_entry(ext2_filsys fs, int inline_data_size,
                return (offset < fs->blocksize - csum_size);
 }
 
+#define NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len))
 static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf)
 {
        struct ext2_dir_entry *d;
@@ -750,20 +751,15 @@ static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf)
        d = dirbuf;
        top = EXT2_DIRENT_TAIL(dirbuf, fs->blocksize);
 
-       rec_len = d->rec_len;
-       while (rec_len && !(rec_len & 0x3)) {
-               d = (struct ext2_dir_entry *)(((char *)d) + rec_len);
-               if (((void *)d) + d->rec_len >= top)
-                       break;
-               rec_len = d->rec_len;
-       }
+       while (d->rec_len && !(d->rec_len & 0x3) && NEXT_DIRENT(d) <= top)
+               d = NEXT_DIRENT(d);
 
        if (d != top) {
                size_t min_size = EXT2_DIR_REC_LEN(
                                ext2fs_dirent_name_len(dirbuf));
-               if (min_size > d->rec_len - sizeof(struct ext2_dir_entry_tail))
+               if (min_size > top - (void *)d)
                        return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
-               d->rec_len -= sizeof(struct ext2_dir_entry_tail);
+               d->rec_len = top - (void *)d;
        }
 
        t = (struct ext2_dir_entry_tail *)top;
@@ -774,6 +770,7 @@ static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf)
 
        return 0;
 }
+#undef NEXT_DIRENT
 
 static int check_dir_block(ext2_filsys fs,
                           struct ext2_db_entry2 *db,
diff --git a/tests/f_corrupt_dirent_tail/expect.1 b/tests/f_corrupt_dirent_tail/expect.1
new file mode 100644 (file)
index 0000000..0813755
--- /dev/null
@@ -0,0 +1,16 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Directory inode 2, block #0, offset 0: directory has no checksum.
+Fix? yes
+
+Directory inode 2, block #0, offset 1012: directory corrupted
+Salvage? yes
+
+Pass 3: Checking directory connectivity
+Pass 3A: Optimizing directories
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 11/128 files (9.1% non-contiguous), 1090/2048 blocks
+Exit status is 1
diff --git a/tests/f_corrupt_dirent_tail/expect.2 b/tests/f_corrupt_dirent_tail/expect.2
new file mode 100644 (file)
index 0000000..c42466d
--- /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: 11/128 files (9.1% non-contiguous), 1090/2048 blocks
+Exit status is 0
diff --git a/tests/f_corrupt_dirent_tail/image.gz b/tests/f_corrupt_dirent_tail/image.gz
new file mode 100644 (file)
index 0000000..f275308
Binary files /dev/null and b/tests/f_corrupt_dirent_tail/image.gz differ
diff --git a/tests/f_corrupt_dirent_tail/name b/tests/f_corrupt_dirent_tail/name
new file mode 100644 (file)
index 0000000..08259a3
--- /dev/null
@@ -0,0 +1 @@
+rebuild a directory with corrupt dirent tail