--- /dev/null
+From 1645c283a87c61f84b2bffd81f50724df959b11a Mon Sep 17 00:00:00 2001
+From: Qu Wenruo <wqu@suse.com>
+Date: Tue, 24 Oct 2023 12:41:11 +1030
+Subject: btrfs: tree-checker: add type and sequence check for inline backrefs
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit 1645c283a87c61f84b2bffd81f50724df959b11a upstream.
+
+[BUG]
+There is a bug report that ntfs2btrfs had a bug that it can lead to
+transaction abort and the filesystem flips to read-only.
+
+[CAUSE]
+For inline backref items, kernel has a strict requirement for their
+ordered, they must follow the following rules:
+
+- All btrfs_extent_inline_ref::type should be in an ascending order
+
+- Within the same type, the items should follow a descending order by
+ their sequence number
+
+ For EXTENT_DATA_REF type, the sequence number is result from
+ hash_extent_data_ref().
+ For other types, their sequence numbers are
+ btrfs_extent_inline_ref::offset.
+
+Thus if there is any code not following above rules, the resulted
+inline backrefs can prevent the kernel to locate the needed inline
+backref and lead to transaction abort.
+
+[FIX]
+Ntrfs2btrfs has already fixed the problem, and btrfs-progs has added the
+ability to detect such problems.
+
+For kernel, let's be more noisy and be more specific about the order, so
+that the next time kernel hits such problem we would reject it in the
+first place, without leading to transaction abort.
+
+Link: https://github.com/kdave/btrfs-progs/pull/622
+Reviewed-by: Josef Bacik <josef@toxicpanda.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[ Fix a conflict due to header cleanup. ]
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/tree-checker.c | 39 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -29,6 +29,7 @@
+ #include "accessors.h"
+ #include "file-item.h"
+ #include "inode-item.h"
++#include "extent-tree.h"
+
+ /*
+ * Error message should follow the following format:
+@@ -1274,6 +1275,8 @@ static int check_extent_item(struct exte
+ unsigned long ptr; /* Current pointer inside inline refs */
+ unsigned long end; /* Extent item end */
+ const u32 item_size = btrfs_item_size(leaf, slot);
++ u8 last_type = 0;
++ u64 last_seq = U64_MAX;
+ u64 flags;
+ u64 generation;
+ u64 total_refs; /* Total refs in btrfs_extent_item */
+@@ -1320,6 +1323,18 @@ static int check_extent_item(struct exte
+ * 2.2) Ref type specific data
+ * Either using btrfs_extent_inline_ref::offset, or specific
+ * data structure.
++ *
++ * All above inline items should follow the order:
++ *
++ * - All btrfs_extent_inline_ref::type should be in an ascending
++ * order
++ *
++ * - Within the same type, the items should follow a descending
++ * order by their sequence number. The sequence number is
++ * determined by:
++ * * btrfs_extent_inline_ref::offset for all types other than
++ * EXTENT_DATA_REF
++ * * hash_extent_data_ref() for EXTENT_DATA_REF
+ */
+ if (unlikely(item_size < sizeof(*ei))) {
+ extent_err(leaf, slot,
+@@ -1401,6 +1416,7 @@ static int check_extent_item(struct exte
+ struct btrfs_extent_inline_ref *iref;
+ struct btrfs_extent_data_ref *dref;
+ struct btrfs_shared_data_ref *sref;
++ u64 seq;
+ u64 dref_offset;
+ u64 inline_offset;
+ u8 inline_type;
+@@ -1414,6 +1430,7 @@ static int check_extent_item(struct exte
+ iref = (struct btrfs_extent_inline_ref *)ptr;
+ inline_type = btrfs_extent_inline_ref_type(leaf, iref);
+ inline_offset = btrfs_extent_inline_ref_offset(leaf, iref);
++ seq = inline_offset;
+ if (unlikely(ptr + btrfs_extent_inline_ref_size(inline_type) > end)) {
+ extent_err(leaf, slot,
+ "inline ref item overflows extent item, ptr %lu iref size %u end %lu",
+@@ -1444,6 +1461,10 @@ static int check_extent_item(struct exte
+ case BTRFS_EXTENT_DATA_REF_KEY:
+ dref = (struct btrfs_extent_data_ref *)(&iref->offset);
+ dref_offset = btrfs_extent_data_ref_offset(leaf, dref);
++ seq = hash_extent_data_ref(
++ btrfs_extent_data_ref_root(leaf, dref),
++ btrfs_extent_data_ref_objectid(leaf, dref),
++ btrfs_extent_data_ref_offset(leaf, dref));
+ if (unlikely(!IS_ALIGNED(dref_offset,
+ fs_info->sectorsize))) {
+ extent_err(leaf, slot,
+@@ -1470,6 +1491,24 @@ static int check_extent_item(struct exte
+ inline_type);
+ return -EUCLEAN;
+ }
++ if (inline_type < last_type) {
++ extent_err(leaf, slot,
++ "inline ref out-of-order: has type %u, prev type %u",
++ inline_type, last_type);
++ return -EUCLEAN;
++ }
++ /* Type changed, allow the sequence starts from U64_MAX again. */
++ if (inline_type > last_type)
++ last_seq = U64_MAX;
++ if (seq > last_seq) {
++ extent_err(leaf, slot,
++"inline ref out-of-order: has type %u offset %llu seq 0x%llx, prev type %u seq 0x%llx",
++ inline_type, inline_offset, seq,
++ last_type, last_seq);
++ return -EUCLEAN;
++ }
++ last_type = inline_type;
++ last_seq = seq;
+ ptr += btrfs_extent_inline_ref_size(inline_type);
+ }
+ /* No padding is allowed */