[PATCH 4/5] btrfs-progs: check: detect invalid file extent items
Qu Wenruo <[email protected]>
| Newsgroups | org.kernel.vger.linux-btrfs |
|---|---|
| Message-ID | <aa7d6552b90ddcb8ff836ad5756474e187e545c8.1787045341.git.wqu@suse.com> |
File extent items should only exist for regular and symlink inodes. And for symlink inodes, the file extent must be inlined. Add such check for lowmem mode to detect those invalid file extents. Now for a corrupted image with the following fs tree layout: item 0 key (256 INODE_ITEM 0) itemoff 16123 itemsize 160 generation 3 transid 9 size 12 nbytes 16384 block group 0 mode 40755 links 1 uid 0 gid 0 rdev 0 sequence 1 flags 0x0(none) item 1 key (256 INODE_REF 256) itemoff 16111 itemsize 12 index 0 namelen 2 name: .. item 2 key (256 DIR_ITEM 496027801) itemoff 16075 itemsize 36 location key (257 INODE_ITEM 0) type BLKDEV transid 9 data_len 0 name_len 6 name: foobar item 3 key (256 DIR_INDEX 2) itemoff 16039 itemsize 36 location key (257 INODE_ITEM 0) type BLKDEV transid 9 data_len 0 name_len 6 name: foobar item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160 generation 9 transid 9 size 8192 nbytes 8192 block group 0 mode 60660 links 1 uid 0 gid 0 rdev 0 sequence 2 flags 0x0(none) item 5 key (257 INODE_REF 256) itemoff 15863 itemsize 16 index 2 namelen 6 name: foobar item 6 key (257 EXTENT_DATA 0) itemoff 15810 itemsize 53 generation 9 type 1 (regular) extent data disk byte 13631488 nr 8192 extent data offset 0 nr 8192 ram 8192 extent compression 0 (none) Lowmem mode will detect the error like the following: [5/8] checking fs roots ERROR: root 5 ino 257 should not have file extent ERROR: root 5 INODE[257] nbytes 8192 not equal to extent_size 0 ERROR: errors found in fs roots found 172032 bytes used, error(s) found For the original mode, it's less strict than the lowmem mode, and it only rejects the obvious cases, without the extra verification on inlined extent for symlinks: [5/8] checking fs roots root 5 inode 257 errors 40, bad file extent ERROR: errors found in fs roots found 172032 bytes used, error(s) found Signed-off-by: Qu Wenruo <[email protected]> --- check/main.c | 5 +++++ check/mode-lowmem.c | 26 ++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/check/main.c b/check/main.c index 3f162ad408d1..b96498294645 100644 --- a/check/main.c +++ b/check/main.c @@ -844,6 +844,11 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache, if (!rec->found_inode_item) return; + /* If it's not REG or SYMLNK, there should be no file extent. */ + if (is_valid_imode(rec->imode) && !S_ISREG(rec->imode) && + !S_ISLNK(rec->imode) && rec->found_file_extent) + rec->errors |= I_ERR_BAD_FILE_EXTENT; + filetype = imode_to_type(rec->imode); list_for_each_entry_safe(backref, tmp, &rec->backrefs, list) { if (backref->found_dir_item && backref->found_dir_index) { diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c index a3c590051054..813e29e2a9bb 100644 --- a/check/mode-lowmem.c +++ b/check/mode-lowmem.c @@ -2093,8 +2093,8 @@ static int check_file_extent_inline(struct btrfs_root *root, * Return 0 if no error occurred. */ static int check_file_extent(struct btrfs_root *root, struct btrfs_path *path, - unsigned int nodatasum, u64 isize, u64 *size, - u64 *end) + unsigned int nodatasum, u64 isize, u32 mode, + u64 *size, u64 *end) { struct btrfs_file_extent_item *fi; struct btrfs_key fkey; @@ -2119,6 +2119,14 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_path *path, fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); extent_type = btrfs_file_extent_type(node, fi); + /* Only regular and symlink can have file extents. */ + if (is_valid_imode(mode) && !S_ISREG(mode) && !S_ISLNK(mode)) { + err |= FILE_EXTENT_ERROR; + error("root %llu ino %llu should not have file extent", + btrfs_root_id(root), fkey.objectid); + return err; + } + /* Check extent type */ if (extent_type != BTRFS_FILE_EXTENT_REG && extent_type != BTRFS_FILE_EXTENT_PREALLOC && @@ -2129,6 +2137,13 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_path *path, return err; } + if (S_ISLNK(mode) && extent_type != BTRFS_FILE_EXTENT_INLINE) { + err |= FILE_EXTENT_ERROR; + error("root %llu ino %llu should not have regular/prealloc file extent", + root->objectid, fkey.objectid); + return err; + } + /* Check inline extent */ if (extent_type == BTRFS_FILE_EXTENT_INLINE) return check_file_extent_inline(root, path, size, end); @@ -2807,12 +2822,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path) err |= ret; break; case BTRFS_EXTENT_DATA_KEY: - if (dir) { - warning("root %llu DIR INODE[%llu] shouldn't EXTENT_DATA[%llu %llu]", - root->objectid, inode_id, key.objectid, - key.offset); - } - ret = check_file_extent(root, path, nodatasum, isize, + ret = check_file_extent(root, path, nodatasum, isize, mode, &extent_size, &extent_end); err |= ret; break; -- 2.54.0