From: Darrick J. Wong Date: Sun, 27 Jul 2014 23:46:15 +0000 (-0400) Subject: e2fsck: make insert_dirent_tail more robust X-Git-Tag: v1.43-WIP-2015-05-18~268^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d3eb1502fd07a4c751d20ad5fa4b75bfda039d52;p=thirdparty%2Fe2fsprogs.git e2fsck: make insert_dirent_tail more robust 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 Signed-off-by: Theodore Ts'o --- diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 4774f0653..69ebab6fe 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -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 index 000000000..0813755c7 --- /dev/null +++ b/tests/f_corrupt_dirent_tail/expect.1 @@ -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 index 000000000..c42466d37 --- /dev/null +++ b/tests/f_corrupt_dirent_tail/expect.2 @@ -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 index 000000000..f2753080f 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 index 000000000..08259a31c --- /dev/null +++ b/tests/f_corrupt_dirent_tail/name @@ -0,0 +1 @@ +rebuild a directory with corrupt dirent tail