Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

btrfs: Add graceful handling of V0 extents

Following the removal of the v0 handling code let's be courteous and
print an error message when such extents are handled. In the cases
where we have a transaction just abort it, otherwise just call
btrfs_handle_fs_error. Both cases result in the FS being re-mounted RO.

In case the error handling would be too intrusive, leave the BUG_ON in
place, like extent_data_ref_count, other proper handling would catch
that earlier.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>

authored by

Nikolay Borisov and committed by
David Sterba
ba3c2b19 a79865c6

+72 -10
+7
fs/btrfs/ctree.h
··· 3432 3432 #define ASSERT(expr) ((void)0) 3433 3433 #endif 3434 3434 3435 + __cold 3436 + static inline void btrfs_print_v0_err(struct btrfs_fs_info *fs_info) 3437 + { 3438 + btrfs_err(fs_info, 3439 + "Unsupported V0 extent filesystem detected. Aborting. Please re-create your filesystem with a newer kernel"); 3440 + } 3441 + 3435 3442 __printf(5, 6) 3436 3443 __cold 3437 3444 void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function,
+35 -4
fs/btrfs/extent-tree.c
··· 867 867 num_refs = btrfs_extent_refs(leaf, ei); 868 868 extent_flags = btrfs_extent_flags(leaf, ei); 869 869 } else { 870 - BUG(); 870 + ret = -EINVAL; 871 + btrfs_print_v0_err(fs_info); 872 + if (trans) 873 + btrfs_abort_transaction(trans, ret); 874 + else 875 + btrfs_handle_fs_error(fs_info, ret, NULL); 876 + 877 + goto out_free; 871 878 } 879 + 872 880 BUG_ON(num_refs == 0); 873 881 } else { 874 882 num_refs = 0; ··· 1304 1296 ref2 = btrfs_item_ptr(leaf, path->slots[0], 1305 1297 struct btrfs_shared_data_ref); 1306 1298 num_refs = btrfs_shared_data_ref_count(leaf, ref2); 1299 + } else if (key.type == BTRFS_EXTENT_REF_V0_KEY) { 1300 + btrfs_print_v0_err(trans->fs_info); 1301 + btrfs_abort_transaction(trans, -EINVAL); 1302 + return -EINVAL; 1307 1303 } else { 1308 1304 BUG(); 1309 1305 } ··· 1340 1328 1341 1329 leaf = path->nodes[0]; 1342 1330 btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); 1331 + 1332 + BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY); 1343 1333 if (iref) { 1344 1334 /* 1345 1335 * If type is invalid, we should have bailed out earlier than ··· 1555 1541 1556 1542 leaf = path->nodes[0]; 1557 1543 item_size = btrfs_item_size_nr(leaf, path->slots[0]); 1558 - BUG_ON(item_size < sizeof(*ei)); 1544 + if (item_size < sizeof(*ei)) { 1545 + err = -EINVAL; 1546 + btrfs_print_v0_err(fs_info); 1547 + btrfs_abort_transaction(trans, err); 1548 + goto out; 1549 + } 1559 1550 1560 1551 ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); 1561 1552 flags = btrfs_extent_flags(leaf, ei); ··· 2284 2265 2285 2266 leaf = path->nodes[0]; 2286 2267 item_size = btrfs_item_size_nr(leaf, path->slots[0]); 2287 - BUG_ON(item_size < sizeof(*ei)); 2268 + 2269 + if (item_size < sizeof(*ei)) { 2270 + err = -EINVAL; 2271 + btrfs_print_v0_err(fs_info); 2272 + btrfs_abort_transaction(trans, err); 2273 + goto out; 2274 + } 2275 + 2288 2276 ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); 2289 2277 __run_delayed_extent_op(extent_op, leaf, ei); 2290 2278 ··· 6822 6796 6823 6797 leaf = path->nodes[0]; 6824 6798 item_size = btrfs_item_size_nr(leaf, extent_slot); 6825 - BUG_ON(item_size < sizeof(*ei)); 6799 + if (item_size < sizeof(*ei)) { 6800 + ret = -EINVAL; 6801 + btrfs_print_v0_err(info); 6802 + btrfs_abort_transaction(trans, ret); 6803 + goto out; 6804 + } 6826 6805 ei = btrfs_item_ptr(leaf, extent_slot, 6827 6806 struct btrfs_extent_item); 6828 6807 if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID &&
+6 -3
fs/btrfs/print-tree.c
··· 52 52 u64 offset; 53 53 int ref_index = 0; 54 54 55 - if (item_size < sizeof(*ei)) 56 - BUG(); 55 + if (item_size < sizeof(*ei)) { 56 + btrfs_print_v0_err(eb->fs_info); 57 + btrfs_handle_fs_error(eb->fs_info, -EINVAL, NULL); 58 + } 57 59 58 60 ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); 59 61 flags = btrfs_extent_flags(eb, ei); ··· 258 256 btrfs_file_extent_ram_bytes(l, fi)); 259 257 break; 260 258 case BTRFS_EXTENT_REF_V0_KEY: 261 - BUG(); 259 + btrfs_print_v0_err(fs_info); 260 + btrfs_handle_fs_error(fs_info, -EINVAL, NULL); 262 261 break; 263 262 case BTRFS_BLOCK_GROUP_ITEM_KEY: 264 263 bi = btrfs_item_ptr(l, i,
+24 -3
fs/btrfs/relocation.c
··· 598 598 btrfs_item_key_to_cpu(leaf, &key, slot); 599 599 600 600 item_size = btrfs_item_size_nr(leaf, slot); 601 + if (item_size < sizeof(*ei)) { 602 + btrfs_print_v0_err(leaf->fs_info); 603 + btrfs_handle_fs_error(leaf->fs_info, -EINVAL, NULL); 604 + return 1; 605 + } 601 606 ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); 602 607 WARN_ON(!(btrfs_extent_flags(leaf, ei) & 603 608 BTRFS_EXTENT_FLAG_TREE_BLOCK)); ··· 787 782 goto next; 788 783 } 789 784 790 - ASSERT(key.type != BTRFS_EXTENT_REF_V0_KEY); 791 785 if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) { 792 786 if (key.objectid == key.offset) { 793 787 /* ··· 830 826 edge->node[UPPER] = upper; 831 827 832 828 goto next; 829 + } else if (key.type == BTRFS_EXTENT_REF_V0_KEY) { 830 + err = -EINVAL; 831 + btrfs_print_v0_err(rc->extent_root->fs_info); 832 + btrfs_handle_fs_error(rc->extent_root->fs_info, err, 833 + NULL); 834 + goto out; 833 835 } else if (key.type != BTRFS_TREE_BLOCK_REF_KEY) { 834 836 goto next; 835 837 } ··· 3325 3315 level = (int)extent_key->offset; 3326 3316 } 3327 3317 generation = btrfs_extent_generation(eb, ei); 3318 + } else if (item_size == sizeof(struct btrfs_extent_item_v0)) { 3319 + btrfs_print_v0_err(eb->fs_info); 3320 + btrfs_handle_fs_error(eb->fs_info, -EINVAL, NULL); 3321 + return -EINVAL; 3328 3322 } else { 3329 3323 BUG(); 3330 3324 } ··· 3734 3720 if (key.objectid != extent_key->objectid) 3735 3721 break; 3736 3722 3737 - BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY); 3738 3723 if (key.type == BTRFS_SHARED_DATA_REF_KEY) { 3739 3724 ret = __add_tree_block(rc, key.offset, blocksize, 3740 3725 blocks); ··· 3742 3729 struct btrfs_extent_data_ref); 3743 3730 ret = find_data_references(rc, extent_key, 3744 3731 eb, dref, blocks); 3732 + } else if (key.type == BTRFS_EXTENT_REF_V0_KEY) { 3733 + btrfs_print_v0_err(eb->fs_info); 3734 + btrfs_handle_fs_error(eb->fs_info, -EINVAL, NULL); 3735 + ret = -EINVAL; 3745 3736 } else { 3746 3737 ret = 0; 3747 3738 } ··· 3984 3967 flags = btrfs_extent_flags(path->nodes[0], ei); 3985 3968 ret = check_extent_flags(flags); 3986 3969 BUG_ON(ret); 3987 - 3970 + } else if (item_size == sizeof(struct btrfs_extent_item_v0)) { 3971 + err = -EINVAL; 3972 + btrfs_print_v0_err(trans->fs_info); 3973 + btrfs_abort_transaction(trans, err); 3974 + break; 3988 3975 } else { 3989 3976 BUG(); 3990 3977 }