Skip to content

Commit b1ccd69

Browse files
committed
btrfs-progs: check/lowmem: detect invalid file extents for symbolic links
[BUG] There is a recent bug that btrfs/012 fails and kernel rejects to read a symbolic link which is backed by a regular extent. Furthremore in that case, "btrfs check --mode=lowmem" doesn't detect such problem at all. [CAUSE] For symbolic links, we only allow inline extents, and this means we should only have a symbolic link target which is smaller than 4K. But lowmem mode btrfs check doesn't handle symbolic link inodes any differently, thus it doesn't check if the file extents are inlined or not, nor reporting this problem as an error. [FIX] When processing data extents, if we find the owning inode is a symbolic link, and the file extent is regular/preallocated, report an error for the bad file extent item. Signed-off-by: Qu Wenruo <wqu@suse.com>
1 parent 2afdbd7 commit b1ccd69

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

check/mode-lowmem.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3351,6 +3351,31 @@ static int repair_extent_data_item(struct btrfs_root *root,
33513351
return err;
33523352
}
33533353

3354+
static int read_inode_item(struct btrfs_root *root,
3355+
u64 ino, struct btrfs_inode_item *ret_ii)
3356+
{
3357+
struct btrfs_path path = { 0 };
3358+
struct btrfs_key key = {
3359+
.objectid = ino,
3360+
.type = BTRFS_INODE_ITEM_KEY,
3361+
.offset = 0
3362+
};
3363+
int ret;
3364+
3365+
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
3366+
if (ret > 0)
3367+
ret = -ENOENT;
3368+
if (ret < 0)
3369+
goto out;
3370+
3371+
read_extent_buffer(path.nodes[0], ret_ii,
3372+
btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
3373+
sizeof(*ret_ii));
3374+
out:
3375+
btrfs_release_path(&path);
3376+
return ret;
3377+
}
3378+
33543379
/*
33553380
* Check EXTENT_DATA item, mainly for its dbackref in extent tree
33563381
*
@@ -3371,6 +3396,7 @@ static int check_extent_data_item(struct btrfs_root *root,
33713396
struct btrfs_extent_item *ei;
33723397
struct btrfs_extent_inline_ref *iref;
33733398
struct btrfs_extent_data_ref *dref;
3399+
struct btrfs_inode_item inode_item;
33743400
u64 owner;
33753401
u64 disk_bytenr;
33763402
u64 disk_num_bytes;
@@ -3400,6 +3426,24 @@ static int check_extent_data_item(struct btrfs_root *root,
34003426
extent_num_bytes = btrfs_file_extent_num_bytes(eb, fi);
34013427
offset = btrfs_file_extent_offset(eb, fi);
34023428

3429+
/*
3430+
* There is a regular/preallocated data extent. Make sure the owning
3431+
* inode is not a symbolic link.
3432+
* As symbolic links can only have inline data extents.
3433+
*/
3434+
ret = read_inode_item(root, fi_key.objectid, &inode_item);
3435+
if (ret < 0) {
3436+
errno = -ret;
3437+
error("failed to grab the inode item for inode %llu: %m",
3438+
fi_key.objectid);
3439+
err |= INODE_ITEM_MISSING;
3440+
}
3441+
if (S_ISLNK(inode_item.mode)) {
3442+
error("symbolic link at root %lld ino %llu has regular/preallocated extents",
3443+
root->root_key.objectid, fi_key.objectid);
3444+
err |= FILE_EXTENT_ERROR;
3445+
}
3446+
34033447
/* Check unaligned disk_bytenr, disk_num_bytes and num_bytes */
34043448
if (!IS_ALIGNED(disk_bytenr, gfs_info->sectorsize)) {
34053449
error(

0 commit comments

Comments
 (0)