]> git.hungrycats.org Git - linux/commitdiff
btrfs: stripe_alloc: do not read an inline backref off a keyed-ref extent item
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Thu, 10 Sep 2026 14:53:38 +0000 (10:53 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Wed, 16 Sep 2026 21:40:06 +0000 (17:40 -0400)
stripe_extents_owned_by() decides whether every extent in a stripe range
belongs to one inode by reading the single inline backref of each data
extent item.  A data extent's only backref is keyed rather than inline
when the leaf had no room to grow the item: insert_inline_extent_backref()
returns -EAGAIN and insert_extent_backref() adds a separate
EXTENT_DATA_REF item.  Such an extent item ends right after the header,
so the walk read the next item's bytes as an inline ref and
btrfs_get_extent_inline_ref_type() warned:

  BTRFS error (device dm-22): eb 81657856 iref 0x3a5c invalid extent inline ref type 1
  WARNING: CPU: 6 PID: 199813 at fs/btrfs/extent-tree.c:380 btrfs_get_extent_inline_ref_type+0xe0/0x190
   stripe_extents_owned_by+0x270/0x310
   btrfs_alloc_from_inode_stripe_run+0x48e/0x6a0
   find_free_extent+0xf9b/0x1870
   btrfs_reserve_extent+0x214/0x890
   __btrfs_prealloc_file_range+0xf1/0x470
   btrfs_fallocate+0xa99/0x10f0

The answer was already the conservative "not owned" (the garbage type is
not EXTENT_DATA_REF_KEY), so only the warning and the taint were wrong.
Check the item size before reading the inline ref and treat a keyed-ref
extent as not owned.

Assisted-by: Claude:claude-fable-5
fs/btrfs/block-group.c

index 6c64f1476a2dc26ea239030586a636c31aa31a31..b808f6128c65be3c6144ed98fedf6fc15694d11c 100644 (file)
@@ -1409,6 +1409,21 @@ static bool stripe_extents_owned_by(struct btrfs_fs_info *fs_info,
                        ret = false;
                        goto out;
                }
+               /*
+                * The extent's single backref is keyed, not inline, when the
+                * leaf had no room to grow the item (insert_inline_extent_backref()
+                * returns -EAGAIN and insert_extent_backref() adds a separate
+                * EXTENT_DATA_REF item).  Such an extent item ends right after
+                * the header, and reading an inline ref there reads the next
+                * item's bytes: btrfs_get_extent_inline_ref_type() warns about
+                * "invalid extent inline ref type 1".  Treat it as not owned;
+                * the answer is the conservative one either way.
+                */
+               if (btrfs_item_size(leaf, slot) < sizeof(*ei) +
+                   btrfs_extent_inline_ref_size(BTRFS_EXTENT_DATA_REF_KEY)) {
+                       ret = false;
+                       goto out;
+               }
                iref = (struct btrfs_extent_inline_ref *)(ei + 1);
                type = btrfs_get_extent_inline_ref_type(leaf, iref,
                                                        BTRFS_REF_TYPE_DATA);