[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.