Skip to content

Commit 09bd06c

Browse files
adam900710kdave
authored andcommitted
btrfs-progs: check: add repair functionality for orphan dev extents
There is a bug report that btrfs-check --repair didn't repair an orphan dev extent, which makes kernel to reject the fs due to the overly strict chunk related checks. However for btrfs-check, it's pretty easy to remove orphan device extents (which do not have corresponding chunks), and update the device item to reflect the used_bytes changes. Add a repair function, btrfs_remove_dev_extent() which does the repair, and call that function for both original and lowmem modes. Pull-request: #1043 Signed-off-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent d855965 commit 09bd06c

File tree

4 files changed

+90
-2
lines changed

4 files changed

+90
-2
lines changed

check/main.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8601,7 +8601,11 @@ int check_chunks(struct cache_tree *chunk_cache,
86018601
dext_rec->objectid,
86028602
dext_rec->offset,
86038603
dext_rec->length);
8604-
if (!ret)
8604+
err = -ENOENT;
8605+
if (opt_check_repair)
8606+
err = btrfs_remove_dev_extent(gfs_info, dext_rec->objectid,
8607+
dext_rec->offset);
8608+
if (err && !ret)
86058609
ret = 1;
86068610
}
86078611
return ret;

check/mode-lowmem.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4749,7 +4749,12 @@ static int check_dev_extent_item(struct extent_buffer *eb, int slot)
47494749
error(
47504750
"device extent[%llu, %llu, %llu] did not find the related chunk",
47514751
devext_key.objectid, devext_key.offset, length);
4752-
return REFERENCER_MISSING;
4752+
ret = -ENOENT;
4753+
if (opt_check_repair)
4754+
ret = btrfs_remove_dev_extent(gfs_info, devext_key.objectid,
4755+
devext_key.offset);
4756+
if (ret < 0)
4757+
return REFERENCER_MISSING;
47534758
}
47544759
return 0;
47554760
}

check/repair.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
#include "kernel-shared/extent_io.h"
3232
#include "kernel-shared/disk-io.h"
3333
#include "kernel-shared/tree-checker.h"
34+
#include "kernel-shared/volumes.h"
3435
#include "common/extent-cache.h"
36+
#include "common/messages.h"
3537
#include "check/repair.h"
3638

3739
int opt_check_repair = 0;
@@ -354,6 +356,82 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans)
354356
return ret;
355357
}
356358

359+
int btrfs_remove_dev_extent(struct btrfs_fs_info *fs_info, u64 devid, u64 physical)
360+
{
361+
struct btrfs_trans_handle *trans;
362+
struct btrfs_root *dev_root = fs_info->dev_root;
363+
struct btrfs_key key = {
364+
.objectid = devid,
365+
.type = BTRFS_DEV_EXTENT_KEY,
366+
.offset = physical,
367+
};
368+
struct btrfs_path path = { 0 };
369+
struct btrfs_dev_extent *dext;
370+
struct btrfs_device *dev;
371+
u64 length;
372+
int ret;
373+
374+
dev = btrfs_find_device_by_devid(fs_info->fs_devices, devid, 0);
375+
if (!dev) {
376+
ret = -ENOENT;
377+
error("failed to find devid %llu", devid);
378+
return ret;
379+
}
380+
trans = btrfs_start_transaction(dev_root, 1);
381+
if (IS_ERR(trans)) {
382+
ret = PTR_ERR(trans);
383+
errno = -ret;
384+
error_msg(ERROR_MSG_START_TRANS, "%m");
385+
return ret;
386+
}
387+
ret = btrfs_search_slot(trans, dev_root, &key, &path, -1, 1);
388+
if (ret > 0)
389+
ret = -ENOENT;
390+
if (ret < 0) {
391+
errno = -ret;
392+
error("failed to locate dev extent, devid %llu physical %llu: %m",
393+
devid, physical);
394+
goto abort;
395+
}
396+
dext = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_dev_extent);
397+
length = btrfs_dev_extent_length(path.nodes[0], dext);
398+
ret = btrfs_del_item(trans, dev_root, &path);
399+
if (ret < 0) {
400+
errno = -ret;
401+
error("failed to delete dev extent, devid %llu physical %llu: %m",
402+
devid, physical);
403+
goto abort;
404+
}
405+
btrfs_release_path(&path);
406+
if (dev->bytes_used < length) {
407+
warning("devid %llu has bytes_used %llu, smaller than %llu",
408+
devid, dev->bytes_used, length);
409+
dev->bytes_used = 0;
410+
} else {
411+
dev->bytes_used -= length;
412+
}
413+
ret = btrfs_update_device(trans, dev);
414+
if (ret < 0) {
415+
errno = -ret;
416+
error("failed to update device, devid %llu: %m", devid);
417+
goto abort;
418+
}
419+
ret = btrfs_commit_transaction(trans, dev_root);
420+
if (ret < 0) {
421+
errno = -ret;
422+
error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
423+
} else {
424+
printf("removed orphan dev extent, devid %llu physical %llu length %llu\n",
425+
devid, physical, length);
426+
}
427+
return ret;
428+
429+
abort:
430+
btrfs_release_path(&path);
431+
btrfs_abort_transaction(trans, ret);
432+
return ret;
433+
}
434+
357435
enum btrfs_tree_block_status btrfs_check_block_for_repair(struct extent_buffer *eb,
358436
struct btrfs_key *first_key)
359437
{

check/repair.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ int btrfs_mark_used_tree_blocks(struct btrfs_fs_info *fs_info,
4848
struct extent_io_tree *tree);
4949
int btrfs_mark_used_blocks(struct btrfs_fs_info *fs_info,
5050
struct extent_io_tree *tree);
51+
int btrfs_remove_dev_extent(struct btrfs_fs_info *fs_info, u64 devid, u64 physical);
5152
enum btrfs_tree_block_status btrfs_check_block_for_repair(struct extent_buffer *eb,
5253
struct btrfs_key *first_key);
5354
void btrfs_set_item_key_unsafe(struct btrfs_root *root, struct btrfs_path *path,

0 commit comments

Comments
 (0)