]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: properly limit inline data extent according to block size
authorQu Wenruo <wqu@suse.com>
Tue, 25 Feb 2025 04:00:44 +0000 (14:30 +1030)
committerDavid Sterba <dsterba@suse.com>
Tue, 18 Mar 2025 19:35:49 +0000 (20:35 +0100)
Btrfs utilizes inline data extent for the following cases:

- Regular small files
- Symlinks

And "btrfs check" detects any file extents that are too large as an
error.

It's not a problem for 4K block size, but for the incoming smaller
block sizes (2K), it can cause problems due to bad limits:

- Non-compressed inline data extents
  We do not allow a non-compressed inline data extent to be as large as
  block size.

- Symlinks
  Currently the only real limit on symlinks are 4K, which can be larger
  than 2K block size.

These will result btrfs-check to report too large file extents.

Fix it by adding proper size checks for the above cases.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/inode.c

index 012ac47351fce376995d124f8a6ea961f622faba..0740a42d393baf7308a21f47a666253fc13eaa88 100644 (file)
@@ -570,6 +570,10 @@ static bool can_cow_file_range_inline(struct btrfs_inode *inode,
        if (size > fs_info->sectorsize)
                return false;
 
+       /* We do not allow a non-compressed extent to be as large as block size. */
+       if (data_len >= fs_info->sectorsize)
+               return false;
+
        /* We cannot exceed the maximum inline data size. */
        if (data_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info))
                return false;
@@ -8670,7 +8674,12 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
        struct extent_buffer *leaf;
 
        name_len = strlen(symname);
-       if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info))
+       /*
+        * Symlinks utilize uncompressed inline extent data, which should not
+        * reach block size.
+        */
+       if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info) ||
+           name_len >= fs_info->sectorsize)
                return -ENAMETOOLONG;
 
        inode = new_inode(dir->i_sb);