Skip to content

Commit 1645c28

Browse files
adam900710kdave
authored andcommitted
btrfs: tree-checker: add type and sequence check for inline backrefs
[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: kdave/btrfs-progs#622 Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent d393315 commit 1645c28

1 file changed

Lines changed: 39 additions & 0 deletions

File tree

fs/btrfs/tree-checker.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "inode-item.h"
3232
#include "dir-item.h"
3333
#include "raid-stripe-tree.h"
34+
#include "extent-tree.h"
3435

3536
/*
3637
* Error message should follow the following format:
@@ -1276,6 +1277,8 @@ static int check_extent_item(struct extent_buffer *leaf,
12761277
unsigned long ptr; /* Current pointer inside inline refs */
12771278
unsigned long end; /* Extent item end */
12781279
const u32 item_size = btrfs_item_size(leaf, slot);
1280+
u8 last_type = 0;
1281+
u64 last_seq = U64_MAX;
12791282
u64 flags;
12801283
u64 generation;
12811284
u64 total_refs; /* Total refs in btrfs_extent_item */
@@ -1322,6 +1325,18 @@ static int check_extent_item(struct extent_buffer *leaf,
13221325
* 2.2) Ref type specific data
13231326
* Either using btrfs_extent_inline_ref::offset, or specific
13241327
* data structure.
1328+
*
1329+
* All above inline items should follow the order:
1330+
*
1331+
* - All btrfs_extent_inline_ref::type should be in an ascending
1332+
* order
1333+
*
1334+
* - Within the same type, the items should follow a descending
1335+
* order by their sequence number. The sequence number is
1336+
* determined by:
1337+
* * btrfs_extent_inline_ref::offset for all types other than
1338+
* EXTENT_DATA_REF
1339+
* * hash_extent_data_ref() for EXTENT_DATA_REF
13251340
*/
13261341
if (unlikely(item_size < sizeof(*ei))) {
13271342
extent_err(leaf, slot,
@@ -1403,6 +1418,7 @@ static int check_extent_item(struct extent_buffer *leaf,
14031418
struct btrfs_extent_inline_ref *iref;
14041419
struct btrfs_extent_data_ref *dref;
14051420
struct btrfs_shared_data_ref *sref;
1421+
u64 seq;
14061422
u64 dref_offset;
14071423
u64 inline_offset;
14081424
u8 inline_type;
@@ -1416,6 +1432,7 @@ static int check_extent_item(struct extent_buffer *leaf,
14161432
iref = (struct btrfs_extent_inline_ref *)ptr;
14171433
inline_type = btrfs_extent_inline_ref_type(leaf, iref);
14181434
inline_offset = btrfs_extent_inline_ref_offset(leaf, iref);
1435+
seq = inline_offset;
14191436
if (unlikely(ptr + btrfs_extent_inline_ref_size(inline_type) > end)) {
14201437
extent_err(leaf, slot,
14211438
"inline ref item overflows extent item, ptr %lu iref size %u end %lu",
@@ -1446,6 +1463,10 @@ static int check_extent_item(struct extent_buffer *leaf,
14461463
case BTRFS_EXTENT_DATA_REF_KEY:
14471464
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
14481465
dref_offset = btrfs_extent_data_ref_offset(leaf, dref);
1466+
seq = hash_extent_data_ref(
1467+
btrfs_extent_data_ref_root(leaf, dref),
1468+
btrfs_extent_data_ref_objectid(leaf, dref),
1469+
btrfs_extent_data_ref_offset(leaf, dref));
14491470
if (unlikely(!IS_ALIGNED(dref_offset,
14501471
fs_info->sectorsize))) {
14511472
extent_err(leaf, slot,
@@ -1475,6 +1496,24 @@ static int check_extent_item(struct extent_buffer *leaf,
14751496
inline_type);
14761497
return -EUCLEAN;
14771498
}
1499+
if (inline_type < last_type) {
1500+
extent_err(leaf, slot,
1501+
"inline ref out-of-order: has type %u, prev type %u",
1502+
inline_type, last_type);
1503+
return -EUCLEAN;
1504+
}
1505+
/* Type changed, allow the sequence starts from U64_MAX again. */
1506+
if (inline_type > last_type)
1507+
last_seq = U64_MAX;
1508+
if (seq > last_seq) {
1509+
extent_err(leaf, slot,
1510+
"inline ref out-of-order: has type %u offset %llu seq 0x%llx, prev type %u seq 0x%llx",
1511+
inline_type, inline_offset, seq,
1512+
last_type, last_seq);
1513+
return -EUCLEAN;
1514+
}
1515+
last_type = inline_type;
1516+
last_seq = seq;
14781517
ptr += btrfs_extent_inline_ref_size(inline_type);
14791518
}
14801519
/* No padding is allowed */

0 commit comments

Comments
 (0)