diff --git a/check/main.c b/check/main.c index 91ce6d740..e457c9073 100644 --- a/check/main.c +++ b/check/main.c @@ -8601,7 +8601,11 @@ int check_chunks(struct cache_tree *chunk_cache, dext_rec->objectid, dext_rec->offset, dext_rec->length); - if (!ret) + err = -ENOENT; + if (opt_check_repair) + err = btrfs_remove_dev_extent(gfs_info, dext_rec->objectid, + dext_rec->offset); + if (err && !ret) ret = 1; } return ret; diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c index 774a57c86..363dc4ae1 100644 --- a/check/mode-lowmem.c +++ b/check/mode-lowmem.c @@ -4749,7 +4749,12 @@ static int check_dev_extent_item(struct extent_buffer *eb, int slot) error( "device extent[%llu, %llu, %llu] did not find the related chunk", devext_key.objectid, devext_key.offset, length); - return REFERENCER_MISSING; + ret = -ENOENT; + if (opt_check_repair) + ret = btrfs_remove_dev_extent(gfs_info, devext_key.objectid, + devext_key.offset); + if (ret < 0) + return REFERENCER_MISSING; } return 0; } diff --git a/check/repair.c b/check/repair.c index e500c4fa0..729b040ce 100644 --- a/check/repair.c +++ b/check/repair.c @@ -31,7 +31,9 @@ #include "kernel-shared/extent_io.h" #include "kernel-shared/disk-io.h" #include "kernel-shared/tree-checker.h" +#include "kernel-shared/volumes.h" #include "common/extent-cache.h" +#include "common/messages.h" #include "check/repair.h" int opt_check_repair = 0; @@ -354,6 +356,82 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans) return ret; } +int btrfs_remove_dev_extent(struct btrfs_fs_info *fs_info, u64 devid, u64 physical) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *dev_root = fs_info->dev_root; + struct btrfs_key key = { + .objectid = devid, + .type = BTRFS_DEV_EXTENT_KEY, + .offset = physical, + }; + struct btrfs_path path = { 0 }; + struct btrfs_dev_extent *dext; + struct btrfs_device *dev; + u64 length; + int ret; + + dev = btrfs_find_device_by_devid(fs_info->fs_devices, devid, 0); + if (!dev) { + ret = -ENOENT; + error("failed to find devid %llu", devid); + return ret; + } + trans = btrfs_start_transaction(dev_root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + errno = -ret; + error_msg(ERROR_MSG_START_TRANS, "%m"); + return ret; + } + ret = btrfs_search_slot(trans, dev_root, &key, &path, -1, 1); + if (ret > 0) + ret = -ENOENT; + if (ret < 0) { + errno = -ret; + error("failed to locate dev extent, devid %llu physical %llu: %m", + devid, physical); + goto abort; + } + dext = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_dev_extent); + length = btrfs_dev_extent_length(path.nodes[0], dext); + ret = btrfs_del_item(trans, dev_root, &path); + if (ret < 0) { + errno = -ret; + error("failed to delete dev extent, devid %llu physical %llu: %m", + devid, physical); + goto abort; + } + btrfs_release_path(&path); + if (dev->bytes_used < length) { + warning("devid %llu has bytes_used %llu, smaller than %llu", + devid, dev->bytes_used, length); + dev->bytes_used = 0; + } else { + dev->bytes_used -= length; + } + ret = btrfs_update_device(trans, dev); + if (ret < 0) { + errno = -ret; + error("failed to update device, devid %llu: %m", devid); + goto abort; + } + ret = btrfs_commit_transaction(trans, dev_root); + if (ret < 0) { + errno = -ret; + error_msg(ERROR_MSG_COMMIT_TRANS, "%m"); + } else { + printf("removed orphan dev extent, devid %llu physical %llu length %llu\n", + devid, physical, length); + } + return ret; + +abort: + btrfs_release_path(&path); + btrfs_abort_transaction(trans, ret); + return ret; +} + enum btrfs_tree_block_status btrfs_check_block_for_repair(struct extent_buffer *eb, struct btrfs_key *first_key) { diff --git a/check/repair.h b/check/repair.h index 4d59ca1dd..d8a191b89 100644 --- a/check/repair.h +++ b/check/repair.h @@ -48,6 +48,7 @@ int btrfs_mark_used_tree_blocks(struct btrfs_fs_info *fs_info, struct extent_io_tree *tree); int btrfs_mark_used_blocks(struct btrfs_fs_info *fs_info, struct extent_io_tree *tree); +int btrfs_remove_dev_extent(struct btrfs_fs_info *fs_info, u64 devid, u64 physical); enum btrfs_tree_block_status btrfs_check_block_for_repair(struct extent_buffer *eb, struct btrfs_key *first_key); void btrfs_set_item_key_unsafe(struct btrfs_root *root, struct btrfs_path *path, diff --git a/tests/fsck-tests/068-orphan-dev-extent/.lowmem_repairable b/tests/fsck-tests/068-orphan-dev-extent/.lowmem_repairable new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fsck-tests/068-orphan-dev-extent/default.img.xz b/tests/fsck-tests/068-orphan-dev-extent/default.img.xz new file mode 100644 index 000000000..6ca960a73 Binary files /dev/null and b/tests/fsck-tests/068-orphan-dev-extent/default.img.xz differ