- rebuild extent records - fix block group accounting - reset csums for rescue nodatasum mount - prune corrupt extent allocation tree blocks - device scanning fixes for dm and multipath - initrd support: move btrfs device scan after block device setup - documentation updates - add csize for file commpressed size - updated restore utility OBS-URL: https://build.opensuse.org/package/show/filesystems/btrfsprogs?expand=0&rev=118
210 lines
6.4 KiB
Diff
210 lines
6.4 KiB
Diff
From a84a34ca88e7157fda4306ab87a6372e0825628d Mon Sep 17 00:00:00 2001
|
|
From: Chris Mason <chris.mason@oracle.com>
|
|
Date: Fri, 10 Feb 2012 13:28:50 -0500
|
|
Subject: [PATCH 14/18] btrfsck: remove extents from the fsck reference
|
|
tracker as they are freed
|
|
|
|
During btrfsck --repair, we make an index of extents that have incorrect
|
|
reference counts. Once we've collect the whole index, we go through
|
|
and modify the extent allocation tree to reflect the correct results.
|
|
|
|
Changing the extent allocation tree may free blocks, and so it may
|
|
end up removing a block that had a missing reference structure. The
|
|
fsck code may then circle back around and add the reference back.
|
|
|
|
The result is an extent that isn't actually used, but is recorded in the
|
|
extent allocation tree.
|
|
|
|
This commit adds a hook called as extents are freed. The hook searches
|
|
the index of incorrect references and updates it to reflect the freeing
|
|
of the extent.
|
|
|
|
Signed-off-by: Chris Mason <chris.mason@oracle.com>
|
|
---
|
|
btrfsck.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
|
|
ctree.h | 6 ++++
|
|
extent-tree.c | 6 ++++
|
|
3 files changed, 99 insertions(+), 7 deletions(-)
|
|
|
|
Index: btrfs-progs-v0.19-118-gfdb6c04/btrfsck.c
|
|
===================================================================
|
|
--- btrfs-progs-v0.19-118-gfdb6c04.orig/btrfsck.c
|
|
+++ btrfs-progs-v0.19-118-gfdb6c04/btrfsck.c
|
|
@@ -2137,7 +2137,7 @@ static int add_extent_rec(struct cache_t
|
|
if (inc_ref)
|
|
rec->refs++;
|
|
if (rec->nr == 1)
|
|
- rec->nr = nr;
|
|
+ rec->nr = max(nr, max_size);
|
|
|
|
if (start != rec->start) {
|
|
fprintf(stderr, "warning, start mismatch %llu %llu\n",
|
|
@@ -2175,7 +2175,7 @@ static int add_extent_rec(struct cache_t
|
|
rec = malloc(sizeof(*rec));
|
|
rec->start = start;
|
|
rec->max_size = max_size;
|
|
- rec->nr = nr;
|
|
+ rec->nr = max(nr, max_size);
|
|
rec->content_checked = 0;
|
|
rec->owner_ref_checked = 0;
|
|
INIT_LIST_HEAD(&rec->backrefs);
|
|
@@ -2722,6 +2722,77 @@ static int add_root_to_pending(struct ex
|
|
return 0;
|
|
}
|
|
|
|
+/* as we fix the tree, we might be deleting blocks that
|
|
+ * we're tracking for repair. This hook makes sure we
|
|
+ * remove any backrefs for blocks as we are fixing them.
|
|
+ */
|
|
+static int free_extent_hook(struct btrfs_trans_handle *trans,
|
|
+ struct btrfs_root *root,
|
|
+ u64 bytenr, u64 num_bytes, u64 parent,
|
|
+ u64 root_objectid, u64 owner, u64 offset,
|
|
+ int refs_to_drop)
|
|
+{
|
|
+ struct extent_record *rec;
|
|
+ struct cache_extent *cache;
|
|
+ int is_data;
|
|
+ struct cache_tree *extent_cache = root->fs_info->fsck_extent_cache;
|
|
+
|
|
+ is_data = owner >= BTRFS_FIRST_FREE_OBJECTID;
|
|
+ cache = find_cache_extent(extent_cache, bytenr, num_bytes);
|
|
+ if (!cache)
|
|
+ return 0;
|
|
+
|
|
+ rec = container_of(cache, struct extent_record, cache);
|
|
+ if (is_data) {
|
|
+ struct data_backref *back;
|
|
+ back = find_data_backref(rec, parent, root_objectid, owner,
|
|
+ offset);
|
|
+ if (!back)
|
|
+ goto out;
|
|
+ if (back->node.found_ref) {
|
|
+ back->found_ref -= refs_to_drop;
|
|
+ if (rec->refs)
|
|
+ rec->refs -= refs_to_drop;
|
|
+ }
|
|
+ if (back->node.found_extent_tree) {
|
|
+ back->num_refs -= refs_to_drop;
|
|
+ if (rec->extent_item_refs)
|
|
+ rec->extent_item_refs -= refs_to_drop;
|
|
+ }
|
|
+ if (back->found_ref == 0)
|
|
+ back->node.found_ref = 0;
|
|
+ if (back->num_refs == 0)
|
|
+ back->node.found_extent_tree = 0;
|
|
+
|
|
+ if (!back->node.found_extent_tree && back->node.found_ref) {
|
|
+ list_del(&back->node.list);
|
|
+ free(back);
|
|
+ }
|
|
+ } else {
|
|
+ struct tree_backref *back;
|
|
+ back = find_tree_backref(rec, parent, root_objectid);
|
|
+ if (!back)
|
|
+ goto out;
|
|
+ if (back->node.found_ref) {
|
|
+ if (rec->refs)
|
|
+ rec->refs--;
|
|
+ back->node.found_ref = 0;
|
|
+ }
|
|
+ if (back->node.found_extent_tree) {
|
|
+ if (rec->extent_item_refs)
|
|
+ rec->extent_item_refs--;
|
|
+ back->node.found_extent_tree = 0;
|
|
+ }
|
|
+ if (!back->node.found_extent_tree && back->node.found_ref) {
|
|
+ list_del(&back->node.list);
|
|
+ free(back);
|
|
+ }
|
|
+ }
|
|
+ maybe_free_extent_rec(extent_cache, rec);
|
|
+out:
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int delete_extent_records(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root,
|
|
struct btrfs_path *path,
|
|
@@ -3008,7 +3079,7 @@ static int check_extent_refs(struct btrf
|
|
while(cache) {
|
|
rec = container_of(cache, struct extent_record, cache);
|
|
btrfs_pin_extent(root->fs_info,
|
|
- rec->start, rec->nr);
|
|
+ rec->start, rec->max_size);
|
|
cache = next_cache_extent(cache);
|
|
}
|
|
}
|
|
@@ -3105,6 +3176,11 @@ static int check_extents(struct btrfs_tr
|
|
cache_tree_init(&nodes);
|
|
cache_tree_init(&reada);
|
|
|
|
+ if (repair) {
|
|
+ root->fs_info->fsck_extent_cache = &extent_cache;
|
|
+ root->fs_info->free_extent_hook = free_extent_hook;
|
|
+ }
|
|
+
|
|
bits_nr = 1024;
|
|
bits = malloc(bits_nr * sizeof(struct block_info));
|
|
if (!bits) {
|
|
@@ -3163,6 +3239,12 @@ static int check_extents(struct btrfs_tr
|
|
break;
|
|
}
|
|
ret = check_extent_refs(trans, root, &extent_cache, repair);
|
|
+
|
|
+ if (repair) {
|
|
+ root->fs_info->fsck_extent_cache = NULL;
|
|
+ root->fs_info->free_extent_hook = NULL;
|
|
+ }
|
|
+
|
|
free(bits);
|
|
return ret;
|
|
}
|
|
@@ -3269,10 +3351,8 @@ int main(int ac, char **av)
|
|
}
|
|
|
|
ret = check_extents(trans, root, repair);
|
|
- if (ret) {
|
|
- fprintf(stderr, "check extents failed with %d!!!!!!!!!\n", ret);
|
|
- goto out;
|
|
- }
|
|
+ if (ret)
|
|
+ fprintf(stderr, "Errors found in extent allocation tree\n");
|
|
|
|
fprintf(stderr, "checking fs roots\n");
|
|
ret = check_fs_roots(root, &root_cache);
|
|
Index: btrfs-progs-v0.19-118-gfdb6c04/ctree.h
|
|
===================================================================
|
|
--- btrfs-progs-v0.19-118-gfdb6c04.orig/ctree.h
|
|
+++ btrfs-progs-v0.19-118-gfdb6c04/ctree.h
|
|
@@ -797,6 +797,12 @@ struct btrfs_fs_info {
|
|
struct list_head space_info;
|
|
int system_allocs;
|
|
int readonly;
|
|
+ int (*free_extent_hook)(struct btrfs_trans_handle *trans,
|
|
+ struct btrfs_root *root,
|
|
+ u64 bytenr, u64 num_bytes, u64 parent,
|
|
+ u64 root_objectid, u64 owner, u64 offset,
|
|
+ int refs_to_drop);
|
|
+ struct cache_tree * fsck_extent_cache;
|
|
};
|
|
|
|
/*
|
|
Index: btrfs-progs-v0.19-118-gfdb6c04/extent-tree.c
|
|
===================================================================
|
|
--- btrfs-progs-v0.19-118-gfdb6c04.orig/extent-tree.c
|
|
+++ btrfs-progs-v0.19-118-gfdb6c04/extent-tree.c
|
|
@@ -2083,6 +2083,12 @@ static int __free_extent(struct btrfs_tr
|
|
u32 item_size;
|
|
u64 refs;
|
|
|
|
+ if (root->fs_info->free_extent_hook) {
|
|
+ root->fs_info->free_extent_hook(trans, root, bytenr, num_bytes,
|
|
+ parent, root_objectid, owner_objectid,
|
|
+ owner_offset, refs_to_drop);
|
|
+
|
|
+ }
|
|
path = btrfs_alloc_path();
|
|
if (!path)
|
|
return -ENOMEM;
|