From 4305d024938113df5d73021a09eb2a991f54ca2f Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Mon, 13 Feb 2017 11:24:33 +0000 Subject: New upstream release Closes: #849353, #817806, #854915, #845473 --- cmds-check.c | 2604 +++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 2028 insertions(+), 576 deletions(-) (limited to 'cmds-check.c') diff --git a/cmds-check.c b/cmds-check.c index df97d3b6..37e5ff18 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -41,6 +41,7 @@ #include "rbtree-utils.h" #include "backref.h" #include "ulist.h" +#include "hash.h" enum task_position { TASK_EXTENTS, @@ -112,6 +113,24 @@ struct data_backref { u32 found_ref; }; +#define ROOT_DIR_ERROR (1<<1) /* bad ROOT_DIR */ +#define DIR_ITEM_MISSING (1<<2) /* DIR_ITEM not found */ +#define DIR_ITEM_MISMATCH (1<<3) /* DIR_ITEM found but not match */ +#define INODE_REF_MISSING (1<<4) /* INODE_REF/INODE_EXTREF not found */ +#define INODE_ITEM_MISSING (1<<5) /* INODE_ITEM not found */ +#define INODE_ITEM_MISMATCH (1<<6) /* INODE_ITEM found but not match */ +#define FILE_EXTENT_ERROR (1<<7) /* bad FILE_EXTENT */ +#define ODD_CSUM_ITEM (1<<8) /* CSUM_ITEM error */ +#define CSUM_ITEM_MISSING (1<<9) /* CSUM_ITEM not found */ +#define LINK_COUNT_ERROR (1<<10) /* INODE_ITEM nlink count error */ +#define NBYTES_ERROR (1<<11) /* INODE_ITEM nbytes count error */ +#define ISIZE_ERROR (1<<12) /* INODE_ITEM size count error */ +#define ORPHAN_ITEM (1<<13) /* INODE_ITEM no reference */ +#define NO_INODE_ITEM (1<<14) /* no inode_item */ +#define LAST_ITEM (1<<15) /* Complete this tree traversal */ +#define ROOT_REF_MISSING (1<<16) /* ROOT_REF not found */ +#define ROOT_REF_MISMATCH (1<<17) /* ROOT_REF found but not match */ + static inline struct data_backref* to_data_backref(struct extent_backref *back) { return container_of(back, struct data_backref, node); @@ -185,9 +204,9 @@ struct inode_backref { unsigned int found_dir_item:1; unsigned int found_dir_index:1; unsigned int found_inode_ref:1; - unsigned int filetype:8; + u8 filetype; + u8 ref_type; int errors; - unsigned int ref_type; u64 dir; u64 index; u16 namelen; @@ -659,6 +678,7 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec) struct inode_backref *tmp; struct orphan_data_extent *src_orphan; struct orphan_data_extent *dst_orphan; + struct rb_node *rb; size_t size; int ret; @@ -691,10 +711,21 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec) list_add_tail(&dst_orphan->list, &rec->orphan_extents); } ret = copy_file_extent_holes(&rec->holes, &orig_rec->holes); - BUG_ON(ret < 0); + if (ret < 0) + goto cleanup_rb; return rec; +cleanup_rb: + rb = rb_first(&rec->holes); + while (rb) { + struct file_extent_hole *hole; + + hole = rb_entry(rb, struct file_extent_hole, node); + rb = rb_next(rb); + free(hole); + } + cleanup: if (!list_empty(&rec->backrefs)) list_for_each_entry_safe(orig, tmp, &rec->backrefs, list) { @@ -924,7 +955,7 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache, struct cache_extent *cache; struct inode_backref *tmp, *backref; struct ptr_node *node; - unsigned char filetype; + u8 filetype; if (!rec->found_inode_item) return; @@ -1055,7 +1086,7 @@ static struct inode_backref *get_inode_backref(struct inode_record *rec, static int add_inode_backref(struct cache_tree *inode_cache, u64 ino, u64 dir, u64 index, const char *name, int namelen, - int filetype, int itemtype, int errors) + u8 filetype, u8 itemtype, int errors) { struct inode_record *rec; struct inode_backref *backref; @@ -1458,7 +1489,7 @@ static int process_dir_item(struct btrfs_root *root, u32 data_len; int error; int nritems = 0; - int filetype; + u8 filetype; struct btrfs_dir_item *di; struct inode_record *rec; struct cache_tree *root_cache; @@ -1826,6 +1857,96 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb, return ret; } +struct node_refs { + u64 bytenr[BTRFS_MAX_LEVEL]; + u64 refs[BTRFS_MAX_LEVEL]; + int need_check[BTRFS_MAX_LEVEL]; +}; + +static int update_nodes_refs(struct btrfs_root *root, u64 bytenr, + struct node_refs *nrefs, u64 level); +static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path, + unsigned int ext_ref); + +static int process_one_leaf_v2(struct btrfs_root *root, struct btrfs_path *path, + struct node_refs *nrefs, int *level, int ext_ref) +{ + struct extent_buffer *cur = path->nodes[0]; + struct btrfs_key key; + u64 cur_bytenr; + u32 nritems; + u64 first_ino = 0; + int root_level = btrfs_header_level(root->node); + int i; + int ret = 0; /* Final return value */ + int err = 0; /* Positive error bitmap */ + + cur_bytenr = cur->start; + + /* skip to first inode item or the first inode number change */ + nritems = btrfs_header_nritems(cur); + for (i = 0; i < nritems; i++) { + btrfs_item_key_to_cpu(cur, &key, i); + if (i == 0) + first_ino = key.objectid; + if (key.type == BTRFS_INODE_ITEM_KEY || + (first_ino && first_ino != key.objectid)) + break; + } + if (i == nritems) { + path->slots[0] = nritems; + return 0; + } + path->slots[0] = i; + +again: + err |= check_inode_item(root, path, ext_ref); + + if (err & LAST_ITEM) + goto out; + + /* still have inode items in thie leaf */ + if (cur->start == cur_bytenr) + goto again; + + /* + * we have switched to another leaf, above nodes may + * have changed, here walk down the path, if a node + * or leaf is shared, check whether we can skip this + * node or leaf. + */ + for (i = root_level; i >= 0; i--) { + if (path->nodes[i]->start == nrefs->bytenr[i]) + continue; + + ret = update_nodes_refs(root, + path->nodes[i]->start, + nrefs, i); + if (ret) + goto out; + + if (!nrefs->need_check[i]) { + *level += 1; + break; + } + } + + for (i = 0; i < *level; i++) { + free_extent_buffer(path->nodes[i]); + path->nodes[i] = NULL; + } +out: + err &= ~LAST_ITEM; + /* + * Convert any error bitmap to -EIO, as we should avoid + * mixing positive and negative return value to represent + * error + */ + if (err && !ret) + ret = -EIO; + return ret; +} + static void reada_walk_down(struct btrfs_root *root, struct extent_buffer *node, int slot) { @@ -1899,10 +2020,66 @@ static int check_child_node(struct btrfs_root *root, return ret; } -struct node_refs { - u64 bytenr[BTRFS_MAX_LEVEL]; - u64 refs[BTRFS_MAX_LEVEL]; -}; +/* + * for a tree node or leaf, if it's shared, indeed we don't need to iterate it + * in every fs or file tree check. Here we find its all root ids, and only check + * it in the fs or file tree which has the smallest root id. + */ +static int need_check(struct btrfs_root *root, struct ulist *roots) +{ + struct rb_node *node; + struct ulist_node *u; + + if (roots->nnodes == 1) + return 1; + + node = rb_first(&roots->root); + u = rb_entry(node, struct ulist_node, rb_node); + /* + * current root id is not smallest, we skip it and let it be checked + * in the fs or file tree who hash the smallest root id. + */ + if (root->objectid != u->val) + return 0; + + return 1; +} + +/* + * for a tree node or leaf, we record its reference count, so later if we still + * process this node or leaf, don't need to compute its reference count again. + */ +static int update_nodes_refs(struct btrfs_root *root, u64 bytenr, + struct node_refs *nrefs, u64 level) +{ + int check, ret; + u64 refs; + struct ulist *roots; + + if (nrefs->bytenr[level] != bytenr) { + ret = btrfs_lookup_extent_info(NULL, root, bytenr, + level, 1, &refs, NULL); + if (ret < 0) + return ret; + + nrefs->bytenr[level] = bytenr; + nrefs->refs[level] = refs; + if (refs > 1) { + ret = btrfs_find_all_roots(NULL, root->fs_info, bytenr, + 0, &roots); + if (ret) + return -EIO; + + check = need_check(root, roots); + ulist_free(roots); + nrefs->need_check[level] = check; + } else { + nrefs->need_check[level] = 1; + } + } + + return 0; +} static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path, struct walk_control *wc, int *level, @@ -2033,6 +2210,110 @@ out: return err; } +static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path, + unsigned int ext_ref); + +static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path, + int *level, struct node_refs *nrefs, int ext_ref) +{ + enum btrfs_tree_block_status status; + u64 bytenr; + u64 ptr_gen; + struct extent_buffer *next; + struct extent_buffer *cur; + u32 blocksize; + int ret; + + WARN_ON(*level < 0); + WARN_ON(*level >= BTRFS_MAX_LEVEL); + + ret = update_nodes_refs(root, path->nodes[*level]->start, + nrefs, *level); + if (ret < 0) + return ret; + + while (*level >= 0) { + WARN_ON(*level < 0); + WARN_ON(*level >= BTRFS_MAX_LEVEL); + cur = path->nodes[*level]; + + if (btrfs_header_level(cur) != *level) + WARN_ON(1); + + if (path->slots[*level] >= btrfs_header_nritems(cur)) + break; + /* Don't forgot to check leaf/node validation */ + if (*level == 0) { + ret = btrfs_check_leaf(root, NULL, cur); + if (ret != BTRFS_TREE_BLOCK_CLEAN) { + ret = -EIO; + break; + } + ret = process_one_leaf_v2(root, path, nrefs, + level, ext_ref); + break; + } else { + ret = btrfs_check_node(root, NULL, cur); + if (ret != BTRFS_TREE_BLOCK_CLEAN) { + ret = -EIO; + break; + } + } + bytenr = btrfs_node_blockptr(cur, path->slots[*level]); + ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]); + blocksize = root->nodesize; + + ret = update_nodes_refs(root, bytenr, nrefs, *level - 1); + if (ret) + break; + if (!nrefs->need_check[*level - 1]) { + path->slots[*level]++; + continue; + } + + next = btrfs_find_tree_block(root, bytenr, blocksize); + if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) { + free_extent_buffer(next); + reada_walk_down(root, cur, path->slots[*level]); + next = read_tree_block(root, bytenr, blocksize, + ptr_gen); + if (!extent_buffer_uptodate(next)) { + struct btrfs_key node_key; + + btrfs_node_key_to_cpu(path->nodes[*level], + &node_key, + path->slots[*level]); + btrfs_add_corrupt_extent_record(root->fs_info, + &node_key, + path->nodes[*level]->start, + root->nodesize, *level); + ret = -EIO; + break; + } + } + + ret = check_child_node(root, cur, path->slots[*level], next); + if (ret < 0) + break; + + if (btrfs_is_leaf(next)) + status = btrfs_check_leaf(root, NULL, next); + else + status = btrfs_check_node(root, NULL, next); + if (status != BTRFS_TREE_BLOCK_CLEAN) { + free_extent_buffer(next); + ret = -EIO; + break; + } + + *level = *level - 1; + free_extent_buffer(path->nodes[*level]); + path->nodes[*level] = next; + path->slots[*level] = 0; + } + return ret; +} + static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path, struct walk_control *wc, int *level) { @@ -2057,6 +2338,27 @@ static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path, return 1; } +static int walk_up_tree_v2(struct btrfs_root *root, struct btrfs_path *path, + int *level) +{ + int i; + struct extent_buffer *leaf; + + for (i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { + leaf = path->nodes[i]; + if (path->slots[i] + 1 < btrfs_header_nritems(leaf)) { + path->slots[i]++; + *level = i; + return 0; + } else { + free_extent_buffer(path->nodes[*level]); + path->nodes[*level] = NULL; + *level = i + 1; + } + } + return 1; +} + static int check_root_dir(struct inode_record *rec) { struct inode_backref *backref; @@ -2174,7 +2476,7 @@ static int add_missing_dir_index(struct btrfs_root *root, struct inode_record *rec, struct inode_backref *backref) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_trans_handle *trans; struct btrfs_dir_item *dir_item; struct extent_buffer *leaf; @@ -2185,27 +2487,22 @@ static int add_missing_dir_index(struct btrfs_root *root, u32 data_size = sizeof(*dir_item) + backref->namelen; int ret; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } fprintf(stderr, "repairing missing dir index item for inode %llu\n", (unsigned long long)rec->ino); + + btrfs_init_path(&path); key.objectid = backref->dir; key.type = BTRFS_DIR_INDEX_KEY; key.offset = backref->index; - - ret = btrfs_insert_empty_item(trans, root, path, &key, data_size); + ret = btrfs_insert_empty_item(trans, root, &path, &key, data_size); BUG_ON(ret); - leaf = path->nodes[0]; - dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); + leaf = path.nodes[0]; + dir_item = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_dir_item); disk_key.objectid = cpu_to_le64(rec->ino); disk_key.type = BTRFS_INODE_ITEM_KEY; @@ -2218,7 +2515,7 @@ static int add_missing_dir_index(struct btrfs_root *root, name_ptr = (unsigned long)(dir_item + 1); write_extent_buffer(leaf, backref->name, name_ptr, backref->namelen); btrfs_mark_buffer_dirty(leaf); - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); backref->found_dir_index = 1; @@ -2243,31 +2540,25 @@ static int delete_dir_index(struct btrfs_root *root, { struct btrfs_trans_handle *trans; struct btrfs_dir_item *di; - struct btrfs_path *path; + struct btrfs_path path; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } - fprintf(stderr, "Deleting bad dir index [%llu,%u,%llu] root %llu\n", (unsigned long long)backref->dir, BTRFS_DIR_INDEX_KEY, (unsigned long long)backref->index, (unsigned long long)root->objectid); - di = btrfs_lookup_dir_index(trans, root, path, backref->dir, + btrfs_init_path(&path); + di = btrfs_lookup_dir_index(trans, root, &path, backref->dir, backref->name, backref->namelen, backref->index, -1); if (IS_ERR(di)) { ret = PTR_ERR(di); - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); if (ret == -ENOENT) return 0; @@ -2275,11 +2566,11 @@ static int delete_dir_index(struct btrfs_root *root, } if (!di) - ret = btrfs_del_item(trans, root, path); + ret = btrfs_del_item(trans, root, &path); else - ret = btrfs_delete_one_dir_name(trans, root, path, di); + ret = btrfs_delete_one_dir_name(trans, root, &path, di); BUG_ON(ret); - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); return ret; } @@ -2562,6 +2853,31 @@ out: return ret; } +static int get_highest_inode(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 *highest_ino) +{ + struct btrfs_key key, found_key; + int ret; + + btrfs_init_path(path); + key.objectid = BTRFS_LAST_FREE_OBJECTID; + key.offset = -1; + key.type = BTRFS_INODE_ITEM_KEY; + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret == 1) { + btrfs_item_key_to_cpu(path->nodes[0], &found_key, + path->slots[0] - 1); + *highest_ino = found_key.objectid; + ret = 0; + } + if (*highest_ino >= BTRFS_LAST_FREE_OBJECTID) + ret = -EOVERFLOW; + btrfs_release_path(path); + return ret; +} + static int repair_inode_nlinks(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, @@ -2607,11 +2923,9 @@ static int repair_inode_nlinks(struct btrfs_trans_handle *trans, } if (rec->found_link == 0) { - lost_found_ino = root->highest_inode; - if (lost_found_ino >= BTRFS_LAST_FREE_OBJECTID) { - ret = -EOVERFLOW; + ret = get_highest_inode(trans, root, path, &lost_found_ino); + if (ret < 0) goto out; - } lost_found_ino++; ret = btrfs_mkdir(trans, root, dir_name, strlen(dir_name), BTRFS_FIRST_FREE_OBJECTID, &lost_found_ino, @@ -2682,48 +2996,46 @@ out: */ static int find_normal_file_extent(struct btrfs_root *root, u64 ino) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_key key; struct btrfs_key found_key; struct btrfs_file_extent_item *fi; u8 type; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - goto out; + btrfs_init_path(&path); key.objectid = ino; key.type = BTRFS_EXTENT_DATA_KEY; key.offset = 0; - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) { ret = 0; goto out; } - if (ret && path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(root, path); + if (ret && path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); if (ret) { ret = 0; goto out; } } while (1) { - btrfs_item_key_to_cpu(path->nodes[0], &found_key, - path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &found_key, + path.slots[0]); if (found_key.objectid != ino || found_key.type != BTRFS_EXTENT_DATA_KEY) break; - fi = btrfs_item_ptr(path->nodes[0], path->slots[0], + fi = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_file_extent_item); - type = btrfs_file_extent_type(path->nodes[0], fi); + type = btrfs_file_extent_type(path.nodes[0], fi); if (type != BTRFS_FILE_EXTENT_INLINE) { ret = 1; goto out; } } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -2914,7 +3226,7 @@ out: static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) { struct btrfs_trans_handle *trans; - struct btrfs_path *path; + struct btrfs_path path; int ret = 0; if (!(rec->errors & (I_ERR_DIR_ISIZE_WRONG | @@ -2926,10 +3238,6 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) I_ERR_FILE_NBYTES_WRONG))) return rec->errors; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - /* * For nlink repair, it may create a dir and add link, so * 2 for parent(256)'s dir_index and dir_item @@ -2938,27 +3246,26 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) * 2 for lost+found dir's dir_index and dir_item for the file */ trans = btrfs_start_transaction(root, 7); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } + btrfs_init_path(&path); if (rec->errors & I_ERR_NO_INODE_ITEM) - ret = repair_inode_no_item(trans, root, path, rec); + ret = repair_inode_no_item(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_FILE_EXTENT_ORPHAN) - ret = repair_inode_orphan_extent(trans, root, path, rec); + ret = repair_inode_orphan_extent(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_FILE_EXTENT_DISCOUNT) - ret = repair_inode_discount_extent(trans, root, path, rec); + ret = repair_inode_discount_extent(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_DIR_ISIZE_WRONG) - ret = repair_inode_isize(trans, root, path, rec); + ret = repair_inode_isize(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_NO_ORPHAN_ITEM) - ret = repair_inode_orphan_item(trans, root, path, rec); + ret = repair_inode_orphan_item(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_LINK_COUNT_WRONG) - ret = repair_inode_nlinks(trans, root, path, rec); + ret = repair_inode_nlinks(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_FILE_NBYTES_WRONG) - ret = repair_inode_nbytes(trans, root, path, rec); + ret = repair_inode_nbytes(trans, root, &path, rec); btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -2981,21 +3288,6 @@ static int check_inode_recs(struct btrfs_root *root, return 0; } - /* - * We need to record the highest inode number for later 'lost+found' - * dir creation. - * We must select an ino not used/referred by any existing inode, or - * 'lost+found' ino may be a missing ino in a corrupted leaf, - * this may cause 'lost+found' dir has wrong nlinks. - */ - cache = last_cache_extent(inode_cache); - if (cache) { - node = container_of(cache, struct ptr_node, cache); - rec = node->data; - if (rec->ino > root->highest_inode) - root->highest_inode = rec->ino; - } - /* * We need to repair backrefs first because we could change some of the * errors in the inode recs. @@ -3210,7 +3502,7 @@ static void free_root_record(struct cache_extent *cache) free(backref); } - kfree(rec); + free(rec); } FREE_EXTENT_CACHE_BASED_TREE(root_recs, free_root_record); @@ -3491,7 +3783,7 @@ static int repair_btree(struct btrfs_root *root, struct cache_tree *corrupt_blocks) { struct btrfs_trans_handle *trans; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_corrupt_block *corrupt; struct cache_extent *cache; struct btrfs_key key; @@ -3502,23 +3794,20 @@ static int repair_btree(struct btrfs_root *root, if (cache_tree_empty(corrupt_blocks)) return 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); fprintf(stderr, "Error starting transaction: %s\n", strerror(-ret)); - goto out_free_path; + return ret; } + btrfs_init_path(&path); cache = first_cache_extent(corrupt_blocks); while (cache) { corrupt = container_of(cache, struct btrfs_corrupt_block, cache); level = corrupt->level; - path->lowest_level = level; + path.lowest_level = level; key.objectid = corrupt->key.objectid; key.type = corrupt->key.type; key.offset = corrupt->key.offset; @@ -3529,22 +3818,22 @@ static int repair_btree(struct btrfs_root *root, * so ins_len set to 0 here. * Balance will be done after all corrupt node/leaf is deleted. */ - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); if (ret < 0) goto out; - offset = btrfs_node_blockptr(path->nodes[level], - path->slots[level]); + offset = btrfs_node_blockptr(path.nodes[level], + path.slots[level]); /* Remove the ptr */ - ret = btrfs_del_ptr(trans, root, path, level, - path->slots[level]); + ret = btrfs_del_ptr(trans, root, &path, level, + path.slots[level]); if (ret < 0) goto out; /* * Remove the corresponding extent * return value is not concerned. */ - btrfs_release_path(path); + btrfs_release_path(&path); ret = btrfs_free_extent(trans, root, offset, root->nodesize, 0, root->root_key.objectid, level - 1, 0); @@ -3557,18 +3846,17 @@ static int repair_btree(struct btrfs_root *root, corrupt = container_of(cache, struct btrfs_corrupt_block, cache); memcpy(&key, &corrupt->key, sizeof(key)); - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret < 0) goto out; /* return will always >0 since it won't find the item */ ret = 0; - btrfs_release_path(path); + btrfs_release_path(&path); cache = next_cache_extent(cache); } out: btrfs_commit_transaction(trans, root); -out_free_path: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -3720,119 +4008,1213 @@ skip_walking: root_node.current); } - err = check_inode_recs(root, &root_node.inode_cache); - if (!ret) - ret = err; + err = check_inode_recs(root, &root_node.inode_cache); + if (!ret) + ret = err; + + free_corrupt_blocks_tree(&corrupt_blocks); + root->fs_info->corrupt_blocks = NULL; + free_orphan_data_extents(&root->orphan_data_extents); + return ret; +} + +static int fs_root_objectid(u64 objectid) +{ + if (objectid == BTRFS_TREE_RELOC_OBJECTID || + objectid == BTRFS_DATA_RELOC_TREE_OBJECTID) + return 1; + return is_fstree(objectid); +} + +static int check_fs_roots(struct btrfs_root *root, + struct cache_tree *root_cache) +{ + struct btrfs_path path; + struct btrfs_key key; + struct walk_control wc; + struct extent_buffer *leaf, *tree_node; + struct btrfs_root *tmp_root; + struct btrfs_root *tree_root = root->fs_info->tree_root; + int ret; + int err = 0; + + if (ctx.progress_enabled) { + ctx.tp = TASK_FS_ROOTS; + task_start(ctx.info); + } + + /* + * Just in case we made any changes to the extent tree that weren't + * reflected into the free space cache yet. + */ + if (repair) + reset_cached_block_groups(root->fs_info); + memset(&wc, 0, sizeof(wc)); + cache_tree_init(&wc.shared); + btrfs_init_path(&path); + +again: + key.offset = 0; + key.objectid = 0; + key.type = BTRFS_ROOT_ITEM_KEY; + ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0); + if (ret < 0) { + err = 1; + goto out; + } + tree_node = tree_root->node; + while (1) { + if (tree_node != tree_root->node) { + free_root_recs_tree(root_cache); + btrfs_release_path(&path); + goto again; + } + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(tree_root, &path); + if (ret) { + if (ret < 0) + err = 1; + break; + } + leaf = path.nodes[0]; + } + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.type == BTRFS_ROOT_ITEM_KEY && + fs_root_objectid(key.objectid)) { + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) { + tmp_root = btrfs_read_fs_root_no_cache( + root->fs_info, &key); + } else { + key.offset = (u64)-1; + tmp_root = btrfs_read_fs_root( + root->fs_info, &key); + } + if (IS_ERR(tmp_root)) { + err = 1; + goto next; + } + ret = check_fs_root(tmp_root, root_cache, &wc); + if (ret == -EAGAIN) { + free_root_recs_tree(root_cache); + btrfs_release_path(&path); + goto again; + } + if (ret) + err = 1; + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) + btrfs_free_fs_root(tmp_root); + } else if (key.type == BTRFS_ROOT_REF_KEY || + key.type == BTRFS_ROOT_BACKREF_KEY) { + process_root_ref(leaf, path.slots[0], &key, + root_cache); + } +next: + path.slots[0]++; + } +out: + btrfs_release_path(&path); + if (err) + free_extent_cache_tree(&wc.shared); + if (!cache_tree_empty(&wc.shared)) + fprintf(stderr, "warning line %d\n", __LINE__); + + task_stop(ctx.info); + + return err; +} + +/* + * Find DIR_ITEM/DIR_INDEX for the given key and check it with the specified + * INODE_REF/INODE_EXTREF match. + * + * @root: the root of the fs/file tree + * @ref_key: the key of the INODE_REF/INODE_EXTREF + * @key: the key of the DIR_ITEM/DIR_INDEX + * @index: the index in the INODE_REF/INODE_EXTREF, be used to + * distinguish root_dir between normal dir/file + * @name: the name in the INODE_REF/INODE_EXTREF + * @namelen: the length of name in the INODE_REF/INODE_EXTREF + * @mode: the st_mode of INODE_ITEM + * + * Return 0 if no error occurred. + * Return ROOT_DIR_ERROR if found DIR_ITEM/DIR_INDEX for root_dir. + * Return DIR_ITEM_MISSING if couldn't find DIR_ITEM/DIR_INDEX for normal + * dir/file. + * Return DIR_ITEM_MISMATCH if INODE_REF/INODE_EXTREF and DIR_ITEM/DIR_INDEX + * not match for normal dir/file. + */ +static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key, + struct btrfs_key *key, u64 index, char *name, + u32 namelen, u32 mode) +{ + struct btrfs_path path; + struct extent_buffer *node; + struct btrfs_dir_item *di; + struct btrfs_key location; + char namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u32 data_len; + u8 filetype; + int slot; + int ret; + + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); + if (ret < 0) { + ret = DIR_ITEM_MISSING; + goto out; + } + + /* Process root dir and goto out*/ + if (index == 0) { + if (ret == 0) { + ret = ROOT_DIR_ERROR; + error( + "root %llu INODE %s[%llu %llu] ROOT_DIR shouldn't have %s", + root->objectid, + ref_key->type == BTRFS_INODE_REF_KEY ? + "REF" : "EXTREF", + ref_key->objectid, ref_key->offset, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX"); + } else { + ret = 0; + } + + goto out; + } + + /* Process normal file/dir */ + if (ret > 0) { + ret = DIR_ITEM_MISSING; + error( + "root %llu INODE %s[%llu %llu] doesn't have related %s[%llu %llu] namelen %u filename %s filetype %d", + root->objectid, + ref_key->type == BTRFS_INODE_REF_KEY ? "REF" : "EXTREF", + ref_key->objectid, ref_key->offset, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, namelen, name, + imode_to_type(mode)); + goto out; + } + + /* Check whether inode_id/filetype/name match */ + node = path.nodes[0]; + slot = path.slots[0]; + di = btrfs_item_ptr(node, slot, struct btrfs_dir_item); + total = btrfs_item_size_nr(node, slot); + while (cur < total) { + ret = DIR_ITEM_MISMATCH; + name_len = btrfs_dir_name_len(node, di); + data_len = btrfs_dir_data_len(node, di); + + btrfs_dir_item_key_to_cpu(node, di, &location); + if (location.objectid != ref_key->objectid || + location.type != BTRFS_INODE_ITEM_KEY || + location.offset != 0) + goto next; + + filetype = btrfs_dir_type(node, di); + if (imode_to_type(mode) != filetype) + goto next; + + if (name_len <= BTRFS_NAME_LEN) { + len = name_len; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu %s[%llu %llu] name too long %u, trimmed", + root->objectid, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, name_len); + } + read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len); + if (len != namelen || strncmp(namebuf, name, len)) + goto next; + + ret = 0; + goto out; +next: + len = sizeof(*di) + name_len + data_len; + di = (struct btrfs_dir_item *)((char *)di + len); + cur += len; + } + if (ret == DIR_ITEM_MISMATCH) + error( + "root %llu INODE %s[%llu %llu] and %s[%llu %llu] mismatch namelen %u filename %s filetype %d", + root->objectid, + ref_key->type == BTRFS_INODE_REF_KEY ? "REF" : "EXTREF", + ref_key->objectid, ref_key->offset, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, namelen, name, + imode_to_type(mode)); +out: + btrfs_release_path(&path); + return ret; +} + +/* + * Traverse the given INODE_REF and call find_dir_item() to find related + * DIR_ITEM/DIR_INDEX. + * + * @root: the root of the fs/file tree + * @ref_key: the key of the INODE_REF + * @refs: the count of INODE_REF + * @mode: the st_mode of INODE_ITEM + * + * Return 0 if no error occurred. + */ +static int check_inode_ref(struct btrfs_root *root, struct btrfs_key *ref_key, + struct extent_buffer *node, int slot, u64 *refs, + int mode) +{ + struct btrfs_key key; + struct btrfs_inode_ref *ref; + char namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u64 index; + int ret, err = 0; + + ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref); + total = btrfs_item_size_nr(node, slot); + +next: + /* Update inode ref count */ + (*refs)++; + + index = btrfs_inode_ref_index(node, ref); + name_len = btrfs_inode_ref_name_len(node, ref); + if (name_len <= BTRFS_NAME_LEN) { + len = name_len; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu INODE_REF[%llu %llu] name too long", + root->objectid, ref_key->objectid, ref_key->offset); + } + + read_extent_buffer(node, namebuf, (unsigned long)(ref + 1), len); + + /* Check root dir ref name */ + if (index == 0 && strncmp(namebuf, "..", name_len)) { + error("root %llu INODE_REF[%llu %llu] ROOT_DIR name shouldn't be %s", + root->objectid, ref_key->objectid, ref_key->offset, + namebuf); + err |= ROOT_DIR_ERROR; + } + + /* Find related DIR_INDEX */ + key.objectid = ref_key->offset; + key.type = BTRFS_DIR_INDEX_KEY; + key.offset = index; + ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + err |= ret; + + /* Find related dir_item */ + key.objectid = ref_key->offset; + key.type = BTRFS_DIR_ITEM_KEY; + key.offset = btrfs_name_hash(namebuf, len); + ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + err |= ret; + + len = sizeof(*ref) + name_len; + ref = (struct btrfs_inode_ref *)((char *)ref + len); + cur += len; + if (cur < total) + goto next; + + return err; +} + +/* + * Traverse the given INODE_EXTREF and call find_dir_item() to find related + * DIR_ITEM/DIR_INDEX. + * + * @root: the root of the fs/file tree + * @ref_key: the key of the INODE_EXTREF + * @refs: the count of INODE_EXTREF + * @mode: the st_mode of INODE_ITEM + * + * Return 0 if no error occurred. + */ +static int check_inode_extref(struct btrfs_root *root, + struct btrfs_key *ref_key, + struct extent_buffer *node, int slot, u64 *refs, + int mode) +{ + struct btrfs_key key; + struct btrfs_inode_extref *extref; + char namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u64 index; + u64 parent; + int ret; + int err = 0; + + extref = btrfs_item_ptr(node, slot, struct btrfs_inode_extref); + total = btrfs_item_size_nr(node, slot); + +next: + /* update inode ref count */ + (*refs)++; + name_len = btrfs_inode_extref_name_len(node, extref); + index = btrfs_inode_extref_index(node, extref); + parent = btrfs_inode_extref_parent(node, extref); + if (name_len <= BTRFS_NAME_LEN) { + len = name_len; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu INODE_EXTREF[%llu %llu] name too long", + root->objectid, ref_key->objectid, ref_key->offset); + } + read_extent_buffer(node, namebuf, (unsigned long)(extref + 1), len); + + /* Check root dir ref name */ + if (index == 0 && strncmp(namebuf, "..", name_len)) { + error("root %llu INODE_EXTREF[%llu %llu] ROOT_DIR name shouldn't be %s", + root->objectid, ref_key->objectid, ref_key->offset, + namebuf); + err |= ROOT_DIR_ERROR; + } + + /* find related dir_index */ + key.objectid = parent; + key.type = BTRFS_DIR_INDEX_KEY; + key.offset = index; + ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + err |= ret; + + /* find related dir_item */ + key.objectid = parent; + key.type = BTRFS_DIR_ITEM_KEY; + key.offset = btrfs_name_hash(namebuf, len); + ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + err |= ret; + + len = sizeof(*extref) + name_len; + extref = (struct btrfs_inode_extref *)((char *)extref + len); + cur += len; + + if (cur < total) + goto next; + + return err; +} + +/* + * Find INODE_REF/INODE_EXTREF for the given key and check it with the specified + * DIR_ITEM/DIR_INDEX match. + * + * @root: the root of the fs/file tree + * @key: the key of the INODE_REF/INODE_EXTREF + * @name: the name in the INODE_REF/INODE_EXTREF + * @namelen: the length of name in the INODE_REF/INODE_EXTREF + * @index: the index in the INODE_REF/INODE_EXTREF, for DIR_ITEM set index + * to (u64)-1 + * @ext_ref: the EXTENDED_IREF feature + * + * Return 0 if no error occurred. + * Return >0 for error bitmap + */ +static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key, + char *name, int namelen, u64 index, + unsigned int ext_ref) +{ + struct btrfs_path path; + struct btrfs_inode_ref *ref; + struct btrfs_inode_extref *extref; + struct extent_buffer *node; + char ref_namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 ref_namelen; + u64 ref_index; + u64 parent; + u64 dir_id; + int slot; + int ret; + + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); + if (ret) { + ret = INODE_REF_MISSING; + goto extref; + } + + node = path.nodes[0]; + slot = path.slots[0]; + + ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref); + total = btrfs_item_size_nr(node, slot); + + /* Iterate all entry of INODE_REF */ + while (cur < total) { + ret = INODE_REF_MISSING; + + ref_namelen = btrfs_inode_ref_name_len(node, ref); + ref_index = btrfs_inode_ref_index(node, ref); + if (index != (u64)-1 && index != ref_index) + goto next_ref; + + if (ref_namelen <= BTRFS_NAME_LEN) { + len = ref_namelen; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu INODE %s[%llu %llu] name too long", + root->objectid, + key->type == BTRFS_INODE_REF_KEY ? + "REF" : "EXTREF", + key->objectid, key->offset); + } + read_extent_buffer(node, ref_namebuf, (unsigned long)(ref + 1), + len); + + if (len != namelen || strncmp(ref_namebuf, name, len)) + goto next_ref; + + ret = 0; + goto out; +next_ref: + len = sizeof(*ref) + ref_namelen; + ref = (struct btrfs_inode_ref *)((char *)ref + len); + cur += len; + } + +extref: + /* Skip if not support EXTENDED_IREF feature */ + if (!ext_ref) + goto out; + + btrfs_release_path(&path); + btrfs_init_path(&path); + + dir_id = key->offset; + key->type = BTRFS_INODE_EXTREF_KEY; + key->offset = btrfs_extref_hash(dir_id, name, namelen); + + ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); + if (ret) { + ret = INODE_REF_MISSING; + goto out; + } + + node = path.nodes[0]; + slot = path.slots[0]; + + extref = btrfs_item_ptr(node, slot, struct btrfs_inode_extref); + cur = 0; + total = btrfs_item_size_nr(node, slot); + + /* Iterate all entry of INODE_EXTREF */ + while (cur < total) { + ret = INODE_REF_MISSING; + + ref_namelen = btrfs_inode_extref_name_len(node, extref); + ref_index = btrfs_inode_extref_index(node, extref); + parent = btrfs_inode_extref_parent(node, extref); + if (index != (u64)-1 && index != ref_index) + goto next_extref; + + if (parent != dir_id) + goto next_extref; + + if (ref_namelen <= BTRFS_NAME_LEN) { + len = ref_namelen; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu INODE %s[%llu %llu] name too long", + root->objectid, + key->type == BTRFS_INODE_REF_KEY ? + "REF" : "EXTREF", + key->objectid, key->offset); + } + read_extent_buffer(node, ref_namebuf, + (unsigned long)(extref + 1), len); + + if (len != namelen || strncmp(ref_namebuf, name, len)) + goto next_extref; + + ret = 0; + goto out; + +next_extref: + len = sizeof(*extref) + ref_namelen; + extref = (struct btrfs_inode_extref *)((char *)extref + len); + cur += len; + + } +out: + btrfs_release_path(&path); + return ret; +} + +/* + * Traverse the given DIR_ITEM/DIR_INDEX and check related INODE_ITEM and + * call find_inode_ref() to check related INODE_REF/INODE_EXTREF. + * + * @root: the root of the fs/file tree + * @key: the key of the INODE_REF/INODE_EXTREF + * @size: the st_size of the INODE_ITEM + * @ext_ref: the EXTENDED_IREF feature + * + * Return 0 if no error occurred. + */ +static int check_dir_item(struct btrfs_root *root, struct btrfs_key *key, + struct extent_buffer *node, int slot, u64 *size, + unsigned int ext_ref) +{ + struct btrfs_dir_item *di; + struct btrfs_inode_item *ii; + struct btrfs_path path; + struct btrfs_key location; + char namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u32 data_len; + u8 filetype; + u32 mode; + u64 index; + int ret; + int err = 0; + + /* + * For DIR_ITEM set index to (u64)-1, so that find_inode_ref + * ignore index check. + */ + index = (key->type == BTRFS_DIR_INDEX_KEY) ? key->offset : (u64)-1; + + di = btrfs_item_ptr(node, slot, struct btrfs_dir_item); + total = btrfs_item_size_nr(node, slot); + + while (cur < total) { + data_len = btrfs_dir_data_len(node, di); + if (data_len) + error("root %llu %s[%llu %llu] data_len shouldn't be %u", + root->objectid, key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, data_len); + + name_len = btrfs_dir_name_len(node, di); + if (name_len <= BTRFS_NAME_LEN) { + len = name_len; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu %s[%llu %llu] name too long", + root->objectid, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset); + } + (*size) += name_len; + + read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len); + filetype = btrfs_dir_type(node, di); + + btrfs_init_path(&path); + btrfs_dir_item_key_to_cpu(node, di, &location); + + /* Ignore related ROOT_ITEM check */ + if (location.type == BTRFS_ROOT_ITEM_KEY) + goto next; + + /* Check relative INODE_ITEM(existence/filetype) */ + ret = btrfs_search_slot(NULL, root, &location, &path, 0, 0); + if (ret) { + err |= INODE_ITEM_MISSING; + error("root %llu %s[%llu %llu] couldn't find relative INODE_ITEM[%llu] namelen %u filename %s filetype %x", + root->objectid, key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", key->objectid, + key->offset, location.objectid, name_len, + namebuf, filetype); + goto next; + } + + ii = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_inode_item); + mode = btrfs_inode_mode(path.nodes[0], ii); + + if (imode_to_type(mode) != filetype) { + err |= INODE_ITEM_MISMATCH; + error("root %llu %s[%llu %llu] relative INODE_ITEM filetype mismatch namelen %u filename %s filetype %d", + root->objectid, key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", key->objectid, + key->offset, name_len, namebuf, filetype); + } + + /* Check relative INODE_REF/INODE_EXTREF */ + location.type = BTRFS_INODE_REF_KEY; + location.offset = key->objectid; + ret = find_inode_ref(root, &location, namebuf, len, + index, ext_ref); + err |= ret; + if (ret & INODE_REF_MISSING) + error("root %llu %s[%llu %llu] relative INODE_REF missing namelen %u filename %s filetype %d", + root->objectid, key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", key->objectid, + key->offset, name_len, namebuf, filetype); + +next: + btrfs_release_path(&path); + len = sizeof(*di) + name_len + data_len; + di = (struct btrfs_dir_item *)((char *)di + len); + cur += len; + + if (key->type == BTRFS_DIR_INDEX_KEY && cur < total) { + error("root %llu DIR_INDEX[%llu %llu] should contain only one entry", + root->objectid, key->objectid, key->offset); + break; + } + } + + return err; +} + +/* + * Check file extent datasum/hole, update the size of the file extents, + * check and update the last offset of the file extent. + * + * @root: the root of fs/file tree. + * @fkey: the key of the file extent. + * @nodatasum: INODE_NODATASUM feature. + * @size: the sum of all EXTENT_DATA items size for this inode. + * @end: the offset of the last extent. + * + * Return 0 if no error occurred. + */ +static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey, + struct extent_buffer *node, int slot, + unsigned int nodatasum, u64 *size, u64 *end) +{ + struct btrfs_file_extent_item *fi; + u64 disk_bytenr; + u64 disk_num_bytes; + u64 extent_num_bytes; + u64 found; + unsigned int extent_type; + unsigned int is_hole; + int ret; + int err = 0; + + fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); + + extent_type = btrfs_file_extent_type(node, fi); + /* Skip if file extent is inline */ + if (extent_type == BTRFS_FILE_EXTENT_INLINE) { + struct btrfs_item *e = btrfs_item_nr(slot); + u32 item_inline_len; + + item_inline_len = btrfs_file_extent_inline_item_len(node, e); + extent_num_bytes = btrfs_file_extent_inline_len(node, slot, fi); + if (extent_num_bytes == 0 || + extent_num_bytes != item_inline_len) + err |= FILE_EXTENT_ERROR; + *size += extent_num_bytes; + return err; + } + + /* Check extent type */ + if (extent_type != BTRFS_FILE_EXTENT_REG && + extent_type != BTRFS_FILE_EXTENT_PREALLOC) { + err |= FILE_EXTENT_ERROR; + error("root %llu EXTENT_DATA[%llu %llu] type bad", + root->objectid, fkey->objectid, fkey->offset); + return err; + } + + /* Check REG_EXTENT/PREALLOC_EXTENT */ + disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi); + disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi); + extent_num_bytes = btrfs_file_extent_num_bytes(node, fi); + is_hole = (disk_bytenr == 0) && (disk_num_bytes == 0); + + /* Check EXTENT_DATA datasum */ + ret = count_csum_range(root, disk_bytenr, disk_num_bytes, &found); + if (found > 0 && nodatasum) { + err |= ODD_CSUM_ITEM; + error("root %llu EXTENT_DATA[%llu %llu] nodatasum shouldn't have datasum", + root->objectid, fkey->objectid, fkey->offset); + } else if (extent_type == BTRFS_FILE_EXTENT_REG && !nodatasum && + !is_hole && + (ret < 0 || found == 0 || found < disk_num_bytes)) { + err |= CSUM_ITEM_MISSING; + error("root %llu EXTENT_DATA[%llu %llu] datasum missing", + root->objectid, fkey->objectid, fkey->offset); + } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && found > 0) { + err |= ODD_CSUM_ITEM; + error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have datasum", + root->objectid, fkey->objectid, fkey->offset); + } + + /* Check EXTENT_DATA hole */ + if (no_holes && is_hole) { + err |= FILE_EXTENT_ERROR; + error("root %llu EXTENT_DATA[%llu %llu] shouldn't be hole", + root->objectid, fkey->objectid, fkey->offset); + } else if (!no_holes && *end != fkey->offset) { + err |= FILE_EXTENT_ERROR; + error("root %llu EXTENT_DATA[%llu %llu] interrupt", + root->objectid, fkey->objectid, fkey->offset); + } + + *end += extent_num_bytes; + if (!is_hole) + *size += extent_num_bytes; + + return err; +} + +/* + * Check INODE_ITEM and related ITEMs (the same inode number) + * 1. check link count + * 2. check inode ref/extref + * 3. check dir item/index + * + * @ext_ref: the EXTENDED_IREF feature + * + * Return 0 if no error occurred. + * Return >0 for error or hit the traversal is done(by error bitmap) + */ +static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path, + unsigned int ext_ref) +{ + struct extent_buffer *node; + struct btrfs_inode_item *ii; + struct btrfs_key key; + u64 inode_id; + u32 mode; + u64 nlink; + u64 nbytes; + u64 isize; + u64 size = 0; + u64 refs = 0; + u64 extent_end = 0; + u64 extent_size = 0; + unsigned int dir; + unsigned int nodatasum; + int slot; + int ret; + int err = 0; + + node = path->nodes[0]; + slot = path->slots[0]; + + btrfs_item_key_to_cpu(node, &key, slot); + inode_id = key.objectid; + + if (inode_id == BTRFS_ORPHAN_OBJECTID) { + ret = btrfs_next_item(root, path); + if (ret > 0) + err |= LAST_ITEM; + return err; + } + + ii = btrfs_item_ptr(node, slot, struct btrfs_inode_item); + isize = btrfs_inode_size(node, ii); + nbytes = btrfs_inode_nbytes(node, ii); + mode = btrfs_inode_mode(node, ii); + dir = imode_to_type(mode) == BTRFS_FT_DIR; + nlink = btrfs_inode_nlink(node, ii); + nodatasum = btrfs_inode_flags(node, ii) & BTRFS_INODE_NODATASUM; + + while (1) { + ret = btrfs_next_item(root, path); + if (ret < 0) { + /* out will fill 'err' rusing current statistics */ + goto out; + } else if (ret > 0) { + err |= LAST_ITEM; + goto out; + } + + node = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(node, &key, slot); + if (key.objectid != inode_id) + goto out; + + switch (key.type) { + case BTRFS_INODE_REF_KEY: + ret = check_inode_ref(root, &key, node, slot, &refs, + mode); + err |= ret; + break; + case BTRFS_INODE_EXTREF_KEY: + if (key.type == BTRFS_INODE_EXTREF_KEY && !ext_ref) + warning("root %llu EXTREF[%llu %llu] isn't supported", + root->objectid, key.objectid, + key.offset); + ret = check_inode_extref(root, &key, node, slot, &refs, + mode); + err |= ret; + break; + case BTRFS_DIR_ITEM_KEY: + case BTRFS_DIR_INDEX_KEY: + if (!dir) { + warning("root %llu INODE[%llu] mode %u shouldn't have DIR_INDEX[%llu %llu]", + root->objectid, inode_id, + imode_to_type(mode), key.objectid, + key.offset); + } + ret = check_dir_item(root, &key, node, slot, &size, + ext_ref); + err |= ret; + break; + case BTRFS_EXTENT_DATA_KEY: + if (dir) { + warning("root %llu DIR INODE[%llu] shouldn't EXTENT_DATA[%llu %llu]", + root->objectid, inode_id, key.objectid, + key.offset); + } + ret = check_file_extent(root, &key, node, slot, + nodatasum, &extent_size, + &extent_end); + err |= ret; + break; + case BTRFS_XATTR_ITEM_KEY: + break; + default: + error("ITEM[%llu %u %llu] UNKNOWN TYPE", + key.objectid, key.type, key.offset); + } + } + +out: + /* verify INODE_ITEM nlink/isize/nbytes */ + if (dir) { + if (nlink != 1) { + err |= LINK_COUNT_ERROR; + error("root %llu DIR INODE[%llu] shouldn't have more than one link(%llu)", + root->objectid, inode_id, nlink); + } + + /* + * Just a warning, as dir inode nbytes is just an + * instructive value. + */ + if (!IS_ALIGNED(nbytes, root->nodesize)) { + warning("root %llu DIR INODE[%llu] nbytes should be aligned to %u", + root->objectid, inode_id, root->nodesize); + } + + if (isize != size) { + err |= ISIZE_ERROR; + error("root %llu DIR INODE [%llu] size(%llu) not equal to %llu", + root->objectid, inode_id, isize, size); + } + } else { + if (nlink != refs) { + err |= LINK_COUNT_ERROR; + error("root %llu INODE[%llu] nlink(%llu) not equal to inode_refs(%llu)", + root->objectid, inode_id, nlink, refs); + } else if (!nlink) { + err |= ORPHAN_ITEM; + } + + if (!nbytes && !no_holes && extent_end < isize) { + err |= NBYTES_ERROR; + error("root %llu INODE[%llu] size (%llu) should have a file extent hole", + root->objectid, inode_id, isize); + } + + if (nbytes != extent_size) { + err |= NBYTES_ERROR; + error("root %llu INODE[%llu] nbytes(%llu) not equal to extent_size(%llu)", + root->objectid, inode_id, nbytes, extent_size); + } + } + + return err; +} + +static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref) +{ + struct btrfs_path path; + struct btrfs_key key; + int err = 0; + int ret; + + key.objectid = BTRFS_FIRST_FREE_OBJECTID; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + /* For root being dropped, we don't need to check first inode */ + if (btrfs_root_refs(&root->root_item) == 0 && + btrfs_disk_key_objectid(&root->root_item.drop_progress) >= + key.objectid) + return 0; + + btrfs_init_path(&path); + + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + err |= INODE_ITEM_MISSING; + } + + err |= check_inode_item(root, &path, ext_ref); + err &= ~LAST_ITEM; + if (err && !ret) + ret = -EIO; +out: + btrfs_release_path(&path); + return ret; +} + +/* + * Iterate all item on the tree and call check_inode_item() to check. + * + * @root: the root of the tree to be checked. + * @ext_ref: the EXTENDED_IREF feature + * + * Return 0 if no error found. + * Return <0 for error. + */ +static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref) +{ + struct btrfs_path path; + struct node_refs nrefs; + struct btrfs_root_item *root_item = &root->root_item; + int ret, wret; + int level; + + /* + * We need to manually check the first inode item(256) + * As the following traversal function will only start from + * the first inode item in the leaf, if inode item(256) is missing + * we will just skip it forever. + */ + ret = check_fs_first_inode(root, ext_ref); + if (ret < 0) + return ret; + + memset(&nrefs, 0, sizeof(nrefs)); + level = btrfs_header_level(root->node); + btrfs_init_path(&path); + + if (btrfs_root_refs(root_item) > 0 || + btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { + path.nodes[level] = root->node; + path.slots[level] = 0; + extent_buffer_get(root->node); + } else { + struct btrfs_key key; + + btrfs_disk_key_to_cpu(&key, &root_item->drop_progress); + level = root_item->drop_level; + path.lowest_level = level; + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) + goto out; + ret = 0; + } + + while (1) { + wret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref); + if (wret < 0) + ret = wret; + if (wret != 0) + break; + + wret = walk_up_tree_v2(root, &path, &level); + if (wret < 0) + ret = wret; + if (wret != 0) + break; + } + +out: + btrfs_release_path(&path); + return ret; +} + +/* + * Find the relative ref for root_ref and root_backref. + * + * @root: the root of the root tree. + * @ref_key: the key of the root ref. + * + * Return 0 if no error occurred. + */ +static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key, + struct extent_buffer *node, int slot) +{ + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_root_ref *ref; + struct btrfs_root_ref *backref; + char ref_name[BTRFS_NAME_LEN] = {0}; + char backref_name[BTRFS_NAME_LEN] = {0}; + u64 ref_dirid; + u64 ref_seq; + u32 ref_namelen; + u64 backref_dirid; + u64 backref_seq; + u32 backref_namelen; + u32 len; + int ret; + int err = 0; + + ref = btrfs_item_ptr(node, slot, struct btrfs_root_ref); + ref_dirid = btrfs_root_ref_dirid(node, ref); + ref_seq = btrfs_root_ref_sequence(node, ref); + ref_namelen = btrfs_root_ref_name_len(node, ref); + + if (ref_namelen <= BTRFS_NAME_LEN) { + len = ref_namelen; + } else { + len = BTRFS_NAME_LEN; + warning("%s[%llu %llu] ref_name too long", + ref_key->type == BTRFS_ROOT_REF_KEY ? + "ROOT_REF" : "ROOT_BACKREF", ref_key->objectid, + ref_key->offset); + } + read_extent_buffer(node, ref_name, (unsigned long)(ref + 1), len); + + /* Find relative root_ref */ + key.objectid = ref_key->offset; + key.type = BTRFS_ROOT_BACKREF_KEY + BTRFS_ROOT_REF_KEY - ref_key->type; + key.offset = ref_key->objectid; - free_corrupt_blocks_tree(&corrupt_blocks); - root->fs_info->corrupt_blocks = NULL; - free_orphan_data_extents(&root->orphan_data_extents); - return ret; -} + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret) { + err |= ROOT_REF_MISSING; + error("%s[%llu %llu] couldn't find relative ref", + ref_key->type == BTRFS_ROOT_REF_KEY ? + "ROOT_REF" : "ROOT_BACKREF", + ref_key->objectid, ref_key->offset); + goto out; + } -static int fs_root_objectid(u64 objectid) -{ - if (objectid == BTRFS_TREE_RELOC_OBJECTID || - objectid == BTRFS_DATA_RELOC_TREE_OBJECTID) - return 1; - return is_fstree(objectid); + backref = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_root_ref); + backref_dirid = btrfs_root_ref_dirid(path.nodes[0], backref); + backref_seq = btrfs_root_ref_sequence(path.nodes[0], backref); + backref_namelen = btrfs_root_ref_name_len(path.nodes[0], backref); + + if (backref_namelen <= BTRFS_NAME_LEN) { + len = backref_namelen; + } else { + len = BTRFS_NAME_LEN; + warning("%s[%llu %llu] ref_name too long", + key.type == BTRFS_ROOT_REF_KEY ? + "ROOT_REF" : "ROOT_BACKREF", + key.objectid, key.offset); + } + read_extent_buffer(path.nodes[0], backref_name, + (unsigned long)(backref + 1), len); + + if (ref_dirid != backref_dirid || ref_seq != backref_seq || + ref_namelen != backref_namelen || + strncmp(ref_name, backref_name, len)) { + err |= ROOT_REF_MISMATCH; + error("%s[%llu %llu] mismatch relative ref", + ref_key->type == BTRFS_ROOT_REF_KEY ? + "ROOT_REF" : "ROOT_BACKREF", + ref_key->objectid, ref_key->offset); + } +out: + btrfs_release_path(&path); + return err; } -static int check_fs_roots(struct btrfs_root *root, - struct cache_tree *root_cache) +/* + * Check all fs/file tree in low_memory mode. + * + * 1. for fs tree root item, call check_fs_root_v2() + * 2. for fs tree root ref/backref, call check_root_ref() + * + * Return 0 if no error occurred. + */ +static int check_fs_roots_v2(struct btrfs_fs_info *fs_info) { + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_root *cur_root = NULL; struct btrfs_path path; struct btrfs_key key; - struct walk_control wc; - struct extent_buffer *leaf, *tree_node; - struct btrfs_root *tmp_root; - struct btrfs_root *tree_root = root->fs_info->tree_root; + struct extent_buffer *node; + unsigned int ext_ref; + int slot; int ret; int err = 0; - if (ctx.progress_enabled) { - ctx.tp = TASK_FS_ROOTS; - task_start(ctx.info); - } + ext_ref = btrfs_fs_incompat(fs_info, EXTENDED_IREF); - /* - * Just in case we made any changes to the extent tree that weren't - * reflected into the free space cache yet. - */ - if (repair) - reset_cached_block_groups(root->fs_info); - memset(&wc, 0, sizeof(wc)); - cache_tree_init(&wc.shared); btrfs_init_path(&path); - -again: + key.objectid = BTRFS_FS_TREE_OBJECTID; key.offset = 0; - key.objectid = 0; key.type = BTRFS_ROOT_ITEM_KEY; + ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0); if (ret < 0) { - err = 1; + err = ret; + goto out; + } else if (ret > 0) { + err = -ENOENT; goto out; } - tree_node = tree_root->node; + while (1) { - if (tree_node != tree_root->node) { - free_root_recs_tree(root_cache); - btrfs_release_path(&path); - goto again; - } - leaf = path.nodes[0]; - if (path.slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(tree_root, &path); - if (ret) { - if (ret < 0) - err = 1; - break; - } - leaf = path.nodes[0]; - } - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + node = path.nodes[0]; + slot = path.slots[0]; + btrfs_item_key_to_cpu(node, &key, slot); + if (key.objectid > BTRFS_LAST_FREE_OBJECTID) + goto out; if (key.type == BTRFS_ROOT_ITEM_KEY && fs_root_objectid(key.objectid)) { if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) { - tmp_root = btrfs_read_fs_root_no_cache( - root->fs_info, &key); + cur_root = btrfs_read_fs_root_no_cache(fs_info, + &key); } else { key.offset = (u64)-1; - tmp_root = btrfs_read_fs_root( - root->fs_info, &key); + cur_root = btrfs_read_fs_root(fs_info, &key); } - if (IS_ERR(tmp_root)) { - err = 1; + + if (IS_ERR(cur_root)) { + error("Fail to read fs/subvol tree: %lld", + key.objectid); + err = -EIO; goto next; } - ret = check_fs_root(tmp_root, root_cache, &wc); - if (ret == -EAGAIN) { - free_root_recs_tree(root_cache); - btrfs_release_path(&path); - goto again; - } - if (ret) - err = 1; + + ret = check_fs_root_v2(cur_root, ext_ref); + err |= ret; + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) - btrfs_free_fs_root(tmp_root); + btrfs_free_fs_root(cur_root); } else if (key.type == BTRFS_ROOT_REF_KEY || - key.type == BTRFS_ROOT_BACKREF_KEY) { - process_root_ref(leaf, path.slots[0], &key, - root_cache); + key.type == BTRFS_ROOT_BACKREF_KEY) { + ret = check_root_ref(tree_root, &key, node, slot); + err |= ret; } next: - path.slots[0]++; + ret = btrfs_next_item(tree_root, &path); + if (ret > 0) + goto out; + if (ret < 0) { + err = ret; + goto out; + } } + out: btrfs_release_path(&path); - if (err) - free_extent_cache_tree(&wc.shared); - if (!cache_tree_empty(&wc.shared)) - fprintf(stderr, "warning line %d\n", __LINE__); - - task_stop(ctx.info); - return err; } @@ -4310,7 +5692,7 @@ static int try_to_fix_bad_block(struct btrfs_root *root, struct ulist *roots; struct ulist_node *node; struct btrfs_root *search_root; - struct btrfs_path *path; + struct btrfs_path path; struct ulist_iterator iter; struct btrfs_key root_key, key; int ret; @@ -4319,17 +5701,11 @@ static int try_to_fix_bad_block(struct btrfs_root *root, status != BTRFS_TREE_BLOCK_INVALID_OFFSETS) return -EIO; - path = btrfs_alloc_path(); - if (!path) - return -EIO; - - ret = btrfs_find_all_roots(NULL, root->fs_info, buf->start, - 0, &roots); - if (ret) { - btrfs_free_path(path); + ret = btrfs_find_all_roots(NULL, root->fs_info, buf->start, 0, &roots); + if (ret) return -EIO; - } + btrfs_init_path(&path); ULIST_ITER_INIT(&iter); while ((node = ulist_next(roots, &iter))) { root_key.objectid = node->val; @@ -4349,31 +5725,31 @@ static int try_to_fix_bad_block(struct btrfs_root *root, break; } - path->lowest_level = btrfs_header_level(buf); - path->skip_check_block = 1; - if (path->lowest_level) + path.lowest_level = btrfs_header_level(buf); + path.skip_check_block = 1; + if (path.lowest_level) btrfs_node_key_to_cpu(buf, &key, 0); else btrfs_item_key_to_cpu(buf, &key, 0); - ret = btrfs_search_slot(trans, search_root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, search_root, &key, &path, 0, 1); if (ret) { ret = -EIO; btrfs_commit_transaction(trans, search_root); break; } if (status == BTRFS_TREE_BLOCK_BAD_KEY_ORDER) - ret = fix_key_order(trans, search_root, path); + ret = fix_key_order(trans, search_root, &path); else if (status == BTRFS_TREE_BLOCK_INVALID_OFFSETS) - ret = fix_item_offset(trans, search_root, path); + ret = fix_item_offset(trans, search_root, &path); if (ret) { btrfs_commit_transaction(trans, search_root); break; } - btrfs_release_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, search_root); } ulist_free(roots); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -4643,12 +6019,15 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache, rec->cache.start = tmpl->start; rec->cache.size = tmpl->nr; ret = insert_cache_extent(extent_cache, &rec->cache); - BUG_ON(ret); + if (ret) { + free(rec); + return ret; + } bytes_used += rec->nr; if (tmpl->metadata) - rec->crossing_stripes = check_crossing_stripes(rec->start, - global_info->tree_root->nodesize); + rec->crossing_stripes = check_crossing_stripes(global_info, + rec->start, global_info->tree_root->nodesize); check_extent_type(rec); return ret; } @@ -4749,7 +6128,8 @@ static int add_extent_rec(struct cache_tree *extent_cache, */ if (tmpl->metadata) rec->crossing_stripes = check_crossing_stripes( - rec->start, global_info->tree_root->nodesize); + global_info, rec->start, + global_info->tree_root->nodesize); check_extent_type(rec); maybe_free_extent_rec(extent_cache, rec); return ret; @@ -4766,6 +6146,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr, struct extent_record *rec; struct tree_backref *back; struct cache_extent *cache; + int ret; cache = lookup_cache_extent(extent_cache, bytenr, 1); if (!cache) { @@ -4776,7 +6157,9 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr, tmpl.nr = 1; tmpl.metadata = 1; - add_extent_rec_nolookup(extent_cache, &tmpl); + ret = add_extent_rec_nolookup(extent_cache, &tmpl); + if (ret) + return ret; /* really a bug in cache_extent implement now */ cache = lookup_cache_extent(extent_cache, bytenr, 1); @@ -4830,6 +6213,7 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, struct extent_record *rec; struct data_backref *back; struct cache_extent *cache; + int ret; cache = lookup_cache_extent(extent_cache, bytenr, 1); if (!cache) { @@ -4840,7 +6224,9 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, tmpl.nr = 1; tmpl.max_size = max_size; - add_extent_rec_nolookup(extent_cache, &tmpl); + ret = add_extent_rec_nolookup(extent_cache, &tmpl); + if (ret) + return ret; cache = lookup_cache_extent(extent_cache, bytenr, 1); if (!cache) @@ -5474,7 +6860,7 @@ static int check_cache_range(struct btrfs_root *root, continue; if (logical[nr] == offset) { if (stripe_len >= bytes) { - kfree(logical); + free(logical); return 0; } bytes -= stripe_len; @@ -5482,7 +6868,7 @@ static int check_cache_range(struct btrfs_root *root, } else if (logical[nr] < offset) { if (logical[nr] + stripe_len >= offset + bytes) { - kfree(logical); + free(logical); return 0; } bytes = (offset + bytes) - @@ -5505,7 +6891,7 @@ static int check_cache_range(struct btrfs_root *root, offset, logical[nr] - offset); if (ret) { - kfree(logical); + free(logical); return ret; } @@ -5516,7 +6902,7 @@ static int check_cache_range(struct btrfs_root *root, } } - kfree(logical); + free(logical); } entry = btrfs_find_free_space(cache->free_space_ctl, offset, bytes); @@ -5546,31 +6932,27 @@ static int check_cache_range(struct btrfs_root *root, static int verify_space_cache(struct btrfs_root *root, struct btrfs_block_group_cache *cache) { - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key key; u64 last; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - root = root->fs_info->extent_root; last = max_t(u64, cache->key.objectid, BTRFS_SUPER_INFO_OFFSET); + btrfs_init_path(&path); key.objectid = last; key.offset = 0; key.type = BTRFS_EXTENT_ITEM_KEY; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) goto out; ret = 0; while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); if (ret < 0) goto out; if (ret > 0) { @@ -5578,13 +6960,13 @@ static int verify_space_cache(struct btrfs_root *root, break; } } - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.objectid >= cache->key.offset + cache->key.objectid) break; if (key.type != BTRFS_EXTENT_ITEM_KEY && key.type != BTRFS_METADATA_ITEM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } @@ -5593,7 +6975,7 @@ static int verify_space_cache(struct btrfs_root *root, last = key.objectid + key.offset; else last = key.objectid + root->nodesize; - path->slots[0]++; + path.slots[0]++; continue; } @@ -5605,7 +6987,7 @@ static int verify_space_cache(struct btrfs_root *root, last = key.objectid + key.offset; else last = key.objectid + root->nodesize; - path->slots[0]++; + path.slots[0]++; } if (last < cache->key.objectid + cache->key.offset) @@ -5614,7 +6996,7 @@ static int verify_space_cache(struct btrfs_root *root, cache->key.offset - last); out: - btrfs_free_path(path); + btrfs_release_path(&path); if (!ret && !RB_EMPTY_ROOT(&cache->free_space_ctl->free_space_offset)) { @@ -5662,8 +7044,7 @@ static int check_space_cache(struct btrfs_root *root) btrfs_remove_free_space_cache(cache); } - if (btrfs_fs_compat_ro(root->fs_info, - BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) { + if (btrfs_fs_compat_ro(root->fs_info, FREE_SPACE_TREE)) { ret = exclude_super_stripes(root, cache); if (ret) { fprintf(stderr, "could not exclude super stripes: %s\n", @@ -5740,7 +7121,7 @@ again: csum = btrfs_csum_data(NULL, (char *)data + tmp, csum, root->sectorsize); - btrfs_csum_final(csum, (char *)&csum); + btrfs_csum_final(csum, (u8 *)&csum); csum_offset = leaf_offset + tmp / root->sectorsize * csum_size; @@ -5771,33 +7152,28 @@ out: static int check_extent_exists(struct btrfs_root *root, u64 bytenr, u64 num_bytes) { - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key key; int ret; - path = btrfs_alloc_path(); - if (!path) { - fprintf(stderr, "Error allocating path\n"); - return -ENOMEM; - } - + btrfs_init_path(&path); key.objectid = bytenr; key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = (u64)-1; again: - ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key, path, + ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key, &path, 0, 0); if (ret < 0) { fprintf(stderr, "Error looking up extent record %d\n", ret); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } else if (ret) { - if (path->slots[0] > 0) { - path->slots[0]--; + if (path.slots[0] > 0) { + path.slots[0]--; } else { - ret = btrfs_prev_leaf(root, path); + ret = btrfs_prev_leaf(root, &path); if (ret < 0) { goto out; } else if (ret > 0) { @@ -5807,7 +7183,7 @@ again: } } - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); /* * Block group items come before extent items if they have the same @@ -5818,10 +7194,10 @@ again: * EXTENT_ITEM_KEY please? */ while (key.type > BTRFS_EXTENT_ITEM_KEY) { - if (path->slots[0] > 0) { - path->slots[0]--; + if (path.slots[0] > 0) { + path.slots[0]--; } else { - ret = btrfs_prev_leaf(root, path); + ret = btrfs_prev_leaf(root, &path); if (ret < 0) { goto out; } else if (ret > 0) { @@ -5829,29 +7205,29 @@ again: goto out; } } - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); } while (num_bytes) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); if (ret < 0) { fprintf(stderr, "Error going to next leaf " "%d\n", ret); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } else if (ret) { break; } } - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type != BTRFS_EXTENT_ITEM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } if (key.objectid + key.offset < bytenr) { - path->slots[0]++; + path.slots[0]++; continue; } if (key.objectid > bytenr + num_bytes) @@ -5884,7 +7260,7 @@ again: * in real life, but no harm in coding it up * anyway just in case. */ - btrfs_release_path(path); + btrfs_release_path(&path); ret = check_extent_exists(root, new_start, new_bytes); if (ret) { @@ -5897,7 +7273,7 @@ again: } num_bytes = key.objectid - bytenr; } - path->slots[0]++; + path.slots[0]++; } ret = 0; @@ -5908,13 +7284,13 @@ out: ret = 1; } - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } static int check_csums(struct btrfs_root *root) { - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key key; u64 offset = 0, num_bytes = 0; @@ -5930,28 +7306,24 @@ static int check_csums(struct btrfs_root *root) return -ENOENT; } + btrfs_init_path(&path); key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; key.type = BTRFS_EXTENT_CSUM_KEY; key.offset = 0; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) { fprintf(stderr, "Error searching csum tree %d\n", ret); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } - if (ret > 0 && path->slots[0]) - path->slots[0]--; + if (ret > 0 && path.slots[0]) + path.slots[0]--; ret = 0; while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); if (ret < 0) { fprintf(stderr, "Error going to next leaf " "%d\n", ret); @@ -5960,19 +7332,19 @@ static int check_csums(struct btrfs_root *root) if (ret) break; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type != BTRFS_EXTENT_CSUM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } - data_len = (btrfs_item_size_nr(leaf, path->slots[0]) / + data_len = (btrfs_item_size_nr(leaf, path.slots[0]) / csum_size) * root->sectorsize; if (!check_data_csum) goto skip_csum_check; - leaf_offset = btrfs_item_ptr_offset(leaf, path->slots[0]); + leaf_offset = btrfs_item_ptr_offset(leaf, path.slots[0]); ret = check_extent_csums(root, key.offset, data_len, leaf_offset, leaf); if (ret) @@ -5992,10 +7364,10 @@ skip_csum_check: num_bytes = 0; } num_bytes += data_len; - path->slots[0]++; + path.slots[0]++; } - btrfs_free_path(path); + btrfs_release_path(&path); return errors; } @@ -6047,7 +7419,9 @@ static int calc_extent_flag(struct btrfs_root *root, cache = lookup_cache_extent(extent_cache, buf->start, 1); /* we have added this extent before */ - BUG_ON(!cache); + if (!cache) + return -ENOENT; + rec = container_of(cache, struct extent_record, cache); /* @@ -6682,12 +8056,11 @@ static int record_extent(struct btrfs_trans_handle *trans, struct extent_backref *back, int allocated, u64 flags) { - int ret; + int ret = 0; struct btrfs_root *extent_root = info->extent_root; struct extent_buffer *leaf; struct btrfs_key ins_key; struct btrfs_extent_item *ei; - struct tree_backref *tback; struct data_backref *dback; struct btrfs_tree_block_info *bi; @@ -6723,7 +8096,6 @@ static int record_extent(struct btrfs_trans_handle *trans, } else { struct btrfs_disk_key copy_key;; - tback = to_tree_backref(back); bi = (struct btrfs_tree_block_info *)(ei + 1); memset_extent_buffer(leaf, 0, (unsigned long)bi, sizeof(*bi)); @@ -6789,6 +8161,7 @@ static int record_extent(struct btrfs_trans_handle *trans, dback->found_ref); } else { u64 parent; + struct tree_backref *tback; tback = to_tree_backref(back); if (back->full_backref) @@ -6826,11 +8199,6 @@ static struct extent_entry *find_most_right_entry(struct list_head *entries) struct extent_entry *entry, *best = NULL, *prev = NULL; list_for_each_entry(entry, entries, list) { - if (!prev) { - prev = entry; - continue; - } - /* * If there are as many broken entries as entries then we know * not to trust this particular entry. @@ -6838,6 +8206,16 @@ static struct extent_entry *find_most_right_entry(struct list_head *entries) if (entry->broken == entry->count) continue; + /* + * Special case, when there are only two entries and 'best' is + * the first one + */ + if (!prev) { + best = entry; + prev = entry; + continue; + } + /* * If our current entry == best then we can't be sure our best * is really the best, so we need to keep searching. @@ -7284,17 +8662,13 @@ static int delete_duplicate_records(struct btrfs_root *root, { struct btrfs_trans_handle *trans; LIST_HEAD(delete_list); - struct btrfs_path *path; + struct btrfs_path path; struct extent_record *tmp, *good, *n; int nr_del = 0; int ret = 0, err; struct btrfs_key key; - path = btrfs_alloc_path(); - if (!path) { - ret = -ENOMEM; - goto out; - } + btrfs_init_path(&path); good = rec; /* Find the record that covers all of the duplicates. */ @@ -7346,16 +8720,16 @@ static int delete_duplicate_records(struct btrfs_root *root, abort(); } - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret) { if (ret > 0) ret = -EINVAL; break; } - ret = btrfs_del_item(trans, root, path); + ret = btrfs_del_item(trans, root, &path); if (ret) break; - btrfs_release_path(path); + btrfs_release_path(&path); nr_del++; } err = btrfs_commit_transaction(trans, root); @@ -7376,7 +8750,7 @@ out: free(tmp); } - btrfs_free_path(path); + btrfs_release_path(&path); if (!ret && !nr_del) rec->num_duplicates = 0; @@ -7489,15 +8863,13 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info, struct extent_backref *back; struct data_backref *dback; struct orphan_data_extent *orphan; - struct btrfs_path *path; + struct btrfs_path path; int recorded_data_ref = 0; int ret = 0; if (rec->metadata) return 1; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + btrfs_init_path(&path); list_for_each_entry(back, &rec->backrefs, list) { if (back->full_backref || !back->is_data || !back->found_extent_tree) @@ -7519,7 +8891,8 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info, key.type = BTRFS_EXTENT_DATA_KEY; key.offset = dback->offset; - ret = btrfs_search_slot(NULL, dest_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, dest_root, &key, &path, 0, 0); + btrfs_release_path(&path); /* * For ret < 0, it's OK since the fs-tree may be corrupted, * we need to record it for inode/file extent rebuild. @@ -7546,7 +8919,7 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info, recorded_data_ref = 1; } out: - btrfs_free_path(path); + btrfs_release_path(&path); if (!ret) return !recorded_data_ref; else @@ -7564,7 +8937,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, { struct btrfs_trans_handle *trans = NULL; int ret; - struct btrfs_path *path; + struct btrfs_path path; struct list_head *cur = rec->backrefs.next; struct cache_extent *cache; struct extent_backref *back; @@ -7574,10 +8947,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, if (rec->flag_block_full_backref) flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); if (rec->refs != rec->extent_item_refs && !rec->metadata) { /* * Sometimes the backrefs themselves are so broken they don't @@ -7586,13 +8956,13 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, * them into the list if we find the backref so that * verify_backrefs can figure out what to do. */ - ret = find_possible_backrefs(info, path, extent_cache, rec); + ret = find_possible_backrefs(info, &path, extent_cache, rec); if (ret < 0) goto out; } /* step one, make sure all of the backrefs agree */ - ret = verify_backrefs(info, path, rec); + ret = verify_backrefs(info, &path, rec); if (ret < 0) goto out; @@ -7603,7 +8973,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, } /* step two, delete all the existing records */ - ret = delete_extent_records(trans, info->extent_root, path, + ret = delete_extent_records(trans, info->extent_root, &path, rec->start, rec->max_size); if (ret < 0) @@ -7630,7 +9000,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, continue; rec->bad_full_backref = 0; - ret = record_extent(trans, info, path, rec, back, allocated, flags); + ret = record_extent(trans, info, &path, rec, back, allocated, flags); allocated = 1; if (ret) @@ -7643,7 +9013,11 @@ out: ret = err; } - btrfs_free_path(path); + if (!ret) + fprintf(stderr, "Repaired extent references for %llu\n", + (unsigned long long)rec->start); + + btrfs_release_path(&path); return ret; } @@ -7652,7 +9026,7 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info, { struct btrfs_trans_handle *trans; struct btrfs_root *root = fs_info->extent_root; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_extent_item *ei; struct btrfs_key key; u64 flags; @@ -7667,32 +9041,27 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info, key.offset = rec->max_size; } - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 0); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + btrfs_init_path(&path); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); if (ret < 0) { - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); return ret; } else if (ret) { fprintf(stderr, "Didn't find extent for %llu\n", (unsigned long long)rec->start); - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); return -ENOENT; } - ei = btrfs_item_ptr(path->nodes[0], path->slots[0], + ei = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_extent_item); - flags = btrfs_extent_flags(path->nodes[0], ei); + flags = btrfs_extent_flags(path.nodes[0], ei); if (rec->flag_block_full_backref) { fprintf(stderr, "setting full backref on %llu\n", (unsigned long long)key.objectid); @@ -7702,10 +9071,15 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info, (unsigned long long)key.objectid); flags &= ~BTRFS_BLOCK_FLAG_FULL_BACKREF; } - btrfs_set_extent_flags(path->nodes[0], ei, flags); - btrfs_mark_buffer_dirty(path->nodes[0]); - btrfs_free_path(path); - return btrfs_commit_transaction(trans, root); + btrfs_set_extent_flags(path.nodes[0], ei, flags); + btrfs_mark_buffer_dirty(path.nodes[0]); + btrfs_release_path(&path); + ret = btrfs_commit_transaction(trans, root); + if (!ret) + fprintf(stderr, "Repaired extent flags for %llu\n", + (unsigned long long)rec->start); + + return ret; } /* right now we only prune from the extent allocation tree */ @@ -7832,11 +9206,8 @@ static int check_extent_refs(struct btrfs_root *root, { struct extent_record *rec; struct cache_extent *cache; - int err = 0; int ret = 0; - int fixed = 0; int had_dups = 0; - int recorded = 0; if (repair) { /* @@ -7905,9 +9276,8 @@ static int check_extent_refs(struct btrfs_root *root, while(1) { int cur_err = 0; + int fix = 0; - fixed = 0; - recorded = 0; cache = search_cache_extent(extent_cache, 0); if (!cache) break; @@ -7915,7 +9285,6 @@ static int check_extent_refs(struct btrfs_root *root, if (rec->num_duplicates) { fprintf(stderr, "extent item %llu has multiple extent " "items\n", (unsigned long long)rec->start); - err = 1; cur_err = 1; } @@ -7929,54 +9298,31 @@ static int check_extent_refs(struct btrfs_root *root, ret = record_orphan_data_extents(root->fs_info, rec); if (ret < 0) goto repair_abort; - if (ret == 0) { - recorded = 1; - } else { - /* - * we can't use the extent to repair file - * extent, let the fallback method handle it. - */ - if (!fixed && repair) { - ret = fixup_extent_refs( - root->fs_info, - extent_cache, rec); - if (ret) - goto repair_abort; - fixed = 1; - } - } - err = 1; + fix = ret; cur_err = 1; } if (all_backpointers_checked(rec, 1)) { fprintf(stderr, "backpointer mismatch on [%llu %llu]\n", (unsigned long long)rec->start, (unsigned long long)rec->nr); - - if (!fixed && !recorded && repair) { - ret = fixup_extent_refs(root->fs_info, - extent_cache, rec); - if (ret) - goto repair_abort; - fixed = 1; - } + fix = 1; cur_err = 1; - err = 1; } if (!rec->owner_ref_checked) { fprintf(stderr, "owner ref check failed [%llu %llu]\n", (unsigned long long)rec->start, (unsigned long long)rec->nr); - if (!fixed && !recorded && repair) { - ret = fixup_extent_refs(root->fs_info, - extent_cache, rec); - if (ret) - goto repair_abort; - fixed = 1; - } - err = 1; + fix = 1; cur_err = 1; } + + if (repair && fix) { + ret = fixup_extent_refs(root->fs_info, extent_cache, rec); + if (ret) + goto repair_abort; + } + + if (rec->bad_full_backref) { fprintf(stderr, "bad full backref, on [%llu]\n", (unsigned long long)rec->start); @@ -7984,9 +9330,8 @@ static int check_extent_refs(struct btrfs_root *root, ret = fixup_extent_flags(root->fs_info, rec); if (ret) goto repair_abort; - fixed = 1; + fix = 1; } - err = 1; cur_err = 1; } /* @@ -7998,7 +9343,6 @@ static int check_extent_refs(struct btrfs_root *root, fprintf(stderr, "bad metadata [%llu, %llu) crossing stripe boundary\n", rec->start, rec->start + rec->max_size); - err = 1; cur_err = 1; } @@ -8006,13 +9350,12 @@ static int check_extent_refs(struct btrfs_root *root, fprintf(stderr, "bad extent [%llu, %llu), type mismatch with chunk\n", rec->start, rec->start + rec->max_size); - err = 1; cur_err = 1; } remove_cache_extent(extent_cache, cache); free_all_extent_backrefs(rec); - if (!init_extent_tree && repair && (!cur_err || fixed)) + if (!init_extent_tree && repair && (!cur_err || fix)) clear_extent_dirty(root->fs_info->excluded_extents, rec->start, rec->start + rec->max_size - 1, @@ -8039,11 +9382,9 @@ repair_abort: if (ret) goto repair_abort; } - if (err) - fprintf(stderr, "repaired damaged extent references\n"); return ret; } - return err; + return 0; } u64 calc_stripe_length(u64 type, u64 length, int num_stripes) @@ -8486,7 +9827,7 @@ again: btrfs_init_path(&path); key.offset = 0; key.objectid = 0; - btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); + key.type = BTRFS_ROOT_ITEM_KEY; ret = btrfs_search_slot(NULL, root->fs_info->tree_root, &key, &path, 0, 0); if (ret < 0) @@ -8502,7 +9843,7 @@ again: slot = path.slots[0]; } btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); - if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { + if (found_key.type == BTRFS_ROOT_ITEM_KEY) { unsigned long offset; u64 last_snapshot; @@ -8649,14 +9990,18 @@ static int check_tree_block_ref(struct btrfs_root *root, u32 nodesize = root->nodesize; u32 item_size; u64 offset; + int tree_reloc_root = 0; int found_ref = 0; int err = 0; int ret; + if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID && + btrfs_header_bytenr(root->node) == bytenr) + tree_reloc_root = 1; + btrfs_init_path(&path); key.objectid = bytenr; - if (btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) + if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) key.type = BTRFS_METADATA_ITEM_KEY; else key.type = BTRFS_EXTENT_ITEM_KEY; @@ -8741,9 +10086,16 @@ static int check_tree_block_ref(struct btrfs_root *root, (offset == root->objectid || offset == owner)) { found_ref = 1; } else if (type == BTRFS_SHARED_BLOCK_REF_KEY) { + /* + * Backref of tree reloc root points to itself, no need + * to check backref any more. + */ + if (tree_reloc_root) + found_ref = 1; + else /* Check if the backref points to valid referencer */ - found_ref = !check_tree_block_ref(root, NULL, offset, - level + 1, owner); + found_ref = !check_tree_block_ref(root, NULL, + offset, level + 1, owner); } if (found_ref) @@ -8794,12 +10146,10 @@ static int check_extent_data_item(struct btrfs_root *root, struct btrfs_extent_inline_ref *iref; struct btrfs_extent_data_ref *dref; u64 owner; - u64 file_extent_gen; u64 disk_bytenr; u64 disk_num_bytes; u64 extent_num_bytes; u64 extent_flags; - u64 extent_gen; u32 item_size; unsigned long end; unsigned long ptr; @@ -8811,7 +10161,6 @@ static int check_extent_data_item(struct btrfs_root *root, btrfs_item_key_to_cpu(eb, &fi_key, slot); fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); - file_extent_gen = btrfs_file_extent_generation(eb, fi); /* Nothing to check for hole and inline data extents */ if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE || @@ -8860,7 +10209,6 @@ static int check_extent_data_item(struct btrfs_root *root, ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); extent_flags = btrfs_extent_flags(leaf, ei); - extent_gen = btrfs_extent_generation(leaf, ei); if (!(extent_flags & BTRFS_EXTENT_FLAG_DATA)) { error( @@ -8870,14 +10218,6 @@ static int check_extent_data_item(struct btrfs_root *root, err |= BACKREF_MISMATCH; } - if (file_extent_gen < extent_gen) { - error( -"extent[%llu %llu] backref generation mismatch, wanted: <=%llu, have: %llu", - disk_bytenr, disk_num_bytes, file_extent_gen, - extent_gen); - err |= BACKREF_MISMATCH; - } - /* Check data backref inside that extent item */ item_size = btrfs_item_size_nr(leaf, path.slots[0]); iref = (struct btrfs_extent_inline_ref *)(ei + 1); @@ -9103,6 +10443,34 @@ out: return err; } +/* + * Check if tree block @eb is tree reloc root. + * Return 0 if it's not or any problem happens + * Return 1 if it's a tree reloc root + */ +static int is_tree_reloc_root(struct btrfs_fs_info *fs_info, + struct extent_buffer *eb) +{ + struct btrfs_root *tree_reloc_root; + struct btrfs_key key; + u64 bytenr = btrfs_header_bytenr(eb); + u64 owner = btrfs_header_owner(eb); + int ret = 0; + + key.objectid = BTRFS_TREE_RELOC_OBJECTID; + key.offset = owner; + key.type = BTRFS_ROOT_ITEM_KEY; + + tree_reloc_root = btrfs_read_fs_root_no_cache(fs_info, &key); + if (IS_ERR(tree_reloc_root)) + return 0; + + if (bytenr == btrfs_header_bytenr(tree_reloc_root->node)) + ret = 1; + btrfs_free_fs_root(tree_reloc_root); + return ret; +} + /* * Check referencer for shared block backref * If level == -1, this function will resolve the level. @@ -9125,6 +10493,13 @@ static int check_shared_block_backref(struct btrfs_fs_info *fs_info, if (level < 0) goto out; + /* It's possible it's a tree reloc root */ + if (parent == bytenr) { + if (is_tree_reloc_root(fs_info, eb)) + found_parent = 1; + goto out; + } + if (level + 1 != btrfs_header_level(eb)) goto out; @@ -9184,7 +10559,7 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info, btrfs_release_path(&path); } key.objectid = root_id; - btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); + key.type = BTRFS_ROOT_ITEM_KEY; key.offset = (u64)-1; btrfs_init_path(&path); @@ -9336,7 +10711,8 @@ static int check_extent_item(struct btrfs_fs_info *fs_info, if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) metadata = 1; - if (metadata && check_crossing_stripes(key.objectid, eb->len)) { + if (metadata && check_crossing_stripes(global_info, key.objectid, + eb->len)) { error("bad metadata [%llu, %llu) crossing stripe boundary", key.objectid, key.objectid + nodesize); err |= CROSSING_STRIPE_BOUNDARY; @@ -9784,7 +11160,7 @@ static int check_leaf_items(struct btrfs_root *root, struct extent_buffer *eb) next: btrfs_item_key_to_cpu(eb, &key, slot); - type = btrfs_key_type(&key); + type = key.type; switch (type) { case BTRFS_EXTENT_DATA_KEY: @@ -10061,7 +11437,11 @@ static int check_chunks_and_extents_v2(struct btrfs_root *root) goto next; key.offset = (u64)-1; - cur_root = btrfs_read_fs_root(root->fs_info, &key); + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) + cur_root = btrfs_read_fs_root_no_cache(root->fs_info, + &key); + else + cur_root = btrfs_read_fs_root(root->fs_info, &key); if (IS_ERR(cur_root) || !cur_root) { error("failed to read tree: %lld", key.objectid); goto next; @@ -10070,6 +11450,8 @@ static int check_chunks_and_extents_v2(struct btrfs_root *root) ret = traverse_tree_block(cur_root, cur_root->node); err |= ret; + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) + btrfs_free_fs_root(cur_root); next: ret = btrfs_next_item(root1, &path); if (ret) @@ -10242,24 +11624,20 @@ static int pin_metadata_blocks(struct btrfs_fs_info *fs_info) static int reset_block_groups(struct btrfs_fs_info *fs_info) { struct btrfs_block_group_cache *cache; - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_chunk *chunk; struct btrfs_key key; int ret; u64 start; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = 0; key.type = BTRFS_CHUNK_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(NULL, fs_info->chunk_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, fs_info->chunk_root, &key, &path, 0, 0); if (ret < 0) { - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -10274,10 +11652,10 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info) /* First we need to create the in-memory block groups */ while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(fs_info->chunk_root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(fs_info->chunk_root, &path); if (ret < 0) { - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } if (ret) { @@ -10285,15 +11663,14 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info) break; } } - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type != BTRFS_CHUNK_ITEM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } - chunk = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_chunk); + chunk = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_chunk); btrfs_add_block_group(fs_info, 0, btrfs_chunk_type(leaf, chunk), key.objectid, key.offset, @@ -10301,7 +11678,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info) set_extent_dirty(&fs_info->free_space_cache, key.offset, key.offset + btrfs_chunk_length(leaf, chunk), GFP_NOFS); - path->slots[0]++; + path.slots[0]++; } start = 0; while (1) { @@ -10312,7 +11689,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info) start = cache->key.objectid + cache->key.offset; } - btrfs_free_path(path); + btrfs_release_path(&path); return 0; } @@ -10320,22 +11697,18 @@ static int reset_balance(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info) { struct btrfs_root *root = fs_info->tree_root; - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key key; int del_slot, del_nr = 0; int ret; int found = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = BTRFS_BALANCE_OBJECTID; key.type = BTRFS_BALANCE_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret) { if (ret > 0) ret = 0; @@ -10345,64 +11718,63 @@ static int reset_balance(struct btrfs_trans_handle *trans, goto out; } - ret = btrfs_del_item(trans, root, path); + ret = btrfs_del_item(trans, root, &path); if (ret) goto out; - btrfs_release_path(path); + btrfs_release_path(&path); key.objectid = BTRFS_TREE_RELOC_OBJECTID; key.type = BTRFS_ROOT_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret < 0) goto out; while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { if (!found) break; if (del_nr) { - ret = btrfs_del_items(trans, root, path, + ret = btrfs_del_items(trans, root, &path, del_slot, del_nr); del_nr = 0; if (ret) goto out; } key.offset++; - btrfs_release_path(path); + btrfs_release_path(&path); found = 0; - ret = btrfs_search_slot(trans, root, &key, path, + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret < 0) goto out; continue; } found = 1; - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.objectid > BTRFS_TREE_RELOC_OBJECTID) break; if (key.objectid != BTRFS_TREE_RELOC_OBJECTID) { - path->slots[0]++; + path.slots[0]++; continue; } if (!del_nr) { - del_slot = path->slots[0]; + del_slot = path.slots[0]; del_nr = 1; } else { del_nr++; } - path->slots[0]++; + path.slots[0]++; } if (del_nr) { - ret = btrfs_del_items(trans, root, path, del_slot, del_nr); + ret = btrfs_del_items(trans, root, &path, del_slot, del_nr); if (ret) goto out; } - btrfs_release_path(path); + btrfs_release_path(&path); reinit_data_reloc: key.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID; @@ -10420,7 +11792,7 @@ reinit_data_reloc: goto out; ret = btrfs_make_root_dir(trans, root, BTRFS_FIRST_FREE_OBJECTID); out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -10437,7 +11809,7 @@ static int reinit_extent_tree(struct btrfs_trans_handle *trans, * the leaves of any fs roots and pin down the bytes for any file * extents we find. Not hard but why do it if we don't have to? */ - if (btrfs_fs_incompat(fs_info, BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)) { + if (btrfs_fs_incompat(fs_info, MIXED_GROUPS)) { fprintf(stderr, "We don't support re-initing the extent tree " "for mixed block groups yet, please notify a btrfs " "developer you want to do this so they can add this " @@ -10510,7 +11882,7 @@ static int reinit_extent_tree(struct btrfs_trans_handle *trans, static int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_trans_handle *trans; struct btrfs_key key; int ret; @@ -10527,31 +11899,26 @@ static int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb return PTR_ERR(root); } - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } - path->lowest_level = btrfs_header_level(eb); - if (path->lowest_level) + btrfs_init_path(&path); + path.lowest_level = btrfs_header_level(eb); + if (path.lowest_level) btrfs_node_key_to_cpu(eb, &key, 0); else btrfs_item_key_to_cpu(eb, &key, 0); - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } static int delete_bad_item(struct btrfs_root *root, struct bad_item *bad) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_trans_handle *trans; struct btrfs_key key; int ret; @@ -10569,26 +11936,21 @@ static int delete_bad_item(struct btrfs_root *root, struct bad_item *bad) return PTR_ERR(root); } - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } - ret = btrfs_search_slot(trans, root, &bad->key, path, -1, 1); + btrfs_init_path(&path); + ret = btrfs_search_slot(trans, root, &bad->key, &path, -1, 1); if (ret) { if (ret > 0) ret = 0; goto out; } - ret = btrfs_del_item(trans, root, path); + ret = btrfs_del_item(trans, root, &path); out: btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -10635,7 +11997,7 @@ static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans, struct btrfs_root *csum_root, struct btrfs_root *cur_root) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_key key; struct extent_buffer *node; struct btrfs_file_extent_item *fi; @@ -10645,30 +12007,25 @@ static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans, int slot = 0; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; buf = malloc(cur_root->fs_info->csum_root->sectorsize); - if (!buf) { - ret = -ENOMEM; - goto out; - } + if (!buf) + return -ENOMEM; + btrfs_init_path(&path); key.objectid = 0; key.offset = 0; key.type = 0; - - ret = btrfs_search_slot(NULL, cur_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, cur_root, &key, &path, 0, 0); if (ret < 0) goto out; /* Iterate all regular file extents and fill its csum */ while (1) { - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); if (key.type != BTRFS_EXTENT_DATA_KEY) goto next; - node = path->nodes[0]; - slot = path->slots[0]; + node = path.nodes[0]; + slot = path.slots[0]; fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); if (btrfs_file_extent_type(node, fi) != BTRFS_FILE_EXTENT_REG) goto next; @@ -10685,7 +12042,7 @@ next: * TODO: if next leaf is corrupted, jump to nearest next valid * leaf. */ - ret = btrfs_next_item(cur_root, path); + ret = btrfs_next_item(cur_root, &path); if (ret < 0) goto out; if (ret > 0) { @@ -10695,7 +12052,7 @@ next: } out: - btrfs_free_path(path); + btrfs_release_path(&path); free(buf); return ret; } @@ -10704,7 +12061,7 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans, struct btrfs_root *csum_root) { struct btrfs_fs_info *fs_info = csum_root->fs_info; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *cur_root; struct extent_buffer *node; @@ -10712,15 +12069,11 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans, int slot = 0; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = BTRFS_FS_TREE_OBJECTID; key.offset = 0; key.type = BTRFS_ROOT_ITEM_KEY; - - ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0); if (ret < 0) goto out; if (ret > 0) { @@ -10729,8 +12082,8 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans, } while (1) { - node = path->nodes[0]; - slot = path->slots[0]; + node = path.nodes[0]; + slot = path.slots[0]; btrfs_item_key_to_cpu(node, &key, slot); if (key.objectid > BTRFS_LAST_FREE_OBJECTID) goto out; @@ -10751,7 +12104,7 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans, if (ret < 0) goto out; next: - ret = btrfs_next_item(tree_root, path); + ret = btrfs_next_item(tree_root, &path); if (ret > 0) { ret = 0; goto out; @@ -10761,7 +12114,7 @@ next: } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -10769,36 +12122,32 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans, struct btrfs_root *csum_root) { struct btrfs_root *extent_root = csum_root->fs_info->extent_root; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_extent_item *ei; struct extent_buffer *leaf; char *buf; struct btrfs_key key; int ret; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = 0; key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0); if (ret < 0) { - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } buf = malloc(csum_root->sectorsize); if (!buf) { - btrfs_free_path(path); + btrfs_release_path(&path); return -ENOMEM; } while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(extent_root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(extent_root, &path); if (ret < 0) break; if (ret) { @@ -10806,19 +12155,19 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans, break; } } - leaf = path->nodes[0]; + leaf = path.nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type != BTRFS_EXTENT_ITEM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } - ei = btrfs_item_ptr(leaf, path->slots[0], + ei = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_extent_item); if (!(btrfs_extent_flags(leaf, ei) & BTRFS_EXTENT_FLAG_DATA)) { - path->slots[0]++; + path.slots[0]++; continue; } @@ -10826,10 +12175,10 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans, key.offset); if (ret) break; - path->slots[0]++; + path.slots[0]++; } - btrfs_free_path(path); + btrfs_release_path(&path); free(buf); return ret; } @@ -10877,7 +12226,7 @@ static int build_roots_info_cache(struct btrfs_fs_info *info) int ret = 0; struct btrfs_key key; struct extent_buffer *leaf; - struct btrfs_path *path; + struct btrfs_path path; if (!roots_info_cache) { roots_info_cache = malloc(sizeof(*roots_info_cache)); @@ -10886,24 +12235,20 @@ static int build_roots_info_cache(struct btrfs_fs_info *info) cache_tree_init(roots_info_cache); } - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = 0; key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(NULL, info->extent_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, info->extent_root, &key, &path, 0, 0); if (ret < 0) goto out; - leaf = path->nodes[0]; + leaf = path.nodes[0]; while (1) { struct btrfs_key found_key; struct btrfs_extent_item *ei; struct btrfs_extent_inline_ref *iref; - int slot = path->slots[0]; + int slot = path.slots[0]; int type; u64 flags; u64 root_id; @@ -10912,18 +12257,18 @@ static int build_roots_info_cache(struct btrfs_fs_info *info) struct root_item_info *rii; if (slot >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(info->extent_root, path); + ret = btrfs_next_leaf(info->extent_root, &path); if (ret < 0) { break; } else if (ret) { ret = 0; break; } - leaf = path->nodes[0]; - slot = path->slots[0]; + leaf = path.nodes[0]; + slot = path.slots[0]; } - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); if (found_key.type != BTRFS_EXTENT_ITEM_KEY && found_key.type != BTRFS_METADATA_ITEM_KEY) @@ -10986,11 +12331,11 @@ static int build_roots_info_cache(struct btrfs_fs_info *info) rii->node_count++; } next: - path->slots[0]++; + path.slots[0]++; } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -11086,7 +12431,7 @@ static int maybe_repair_root_item(struct btrfs_fs_info *info, */ static int repair_root_items(struct btrfs_fs_info *info) { - struct btrfs_path *path = NULL; + struct btrfs_path path; struct btrfs_key key; struct extent_buffer *leaf; struct btrfs_trans_handle *trans = NULL; @@ -11094,16 +12439,12 @@ static int repair_root_items(struct btrfs_fs_info *info) int bad_roots = 0; int need_trans = 0; + btrfs_init_path(&path); + ret = build_roots_info_cache(info); if (ret) goto out; - path = btrfs_alloc_path(); - if (!path) { - ret = -ENOMEM; - goto out; - } - key.objectid = BTRFS_FIRST_FREE_OBJECTID; key.type = BTRFS_ROOT_ITEM_KEY; key.offset = 0; @@ -11122,19 +12463,19 @@ again: } } - ret = btrfs_search_slot(trans, info->tree_root, &key, path, + ret = btrfs_search_slot(trans, info->tree_root, &key, &path, 0, trans ? 1 : 0); if (ret < 0) goto out; - leaf = path->nodes[0]; + leaf = path.nodes[0]; while (1) { struct btrfs_key found_key; - if (path->slots[0] >= btrfs_header_nritems(leaf)) { - int no_more_keys = find_next_key(path, &key); + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + int no_more_keys = find_next_key(&path, &key); - btrfs_release_path(path); + btrfs_release_path(&path); if (trans) { ret = btrfs_commit_transaction(trans, info->tree_root); @@ -11148,14 +12489,14 @@ again: goto again; } - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); if (found_key.type != BTRFS_ROOT_ITEM_KEY) goto next; if (found_key.objectid == BTRFS_TREE_RELOC_OBJECTID) goto next; - ret = maybe_repair_root_item(info, path, &found_key, + ret = maybe_repair_root_item(info, &path, &found_key, trans ? 0 : 1); if (ret < 0) goto out; @@ -11163,18 +12504,18 @@ again: if (!trans && repair) { need_trans = 1; key = found_key; - btrfs_release_path(path); + btrfs_release_path(&path); goto again; } bad_roots++; } next: - path->slots[0]++; + path.slots[0]++; } ret = 0; out: free_roots_info_cache(); - btrfs_free_path(path); + btrfs_release_path(&path); if (trans) btrfs_commit_transaction(trans, info->tree_root); if (ret < 0) @@ -11183,6 +12524,36 @@ out: return bad_roots; } +static int clear_free_space_cache(struct btrfs_fs_info *fs_info) +{ + struct btrfs_trans_handle *trans; + struct btrfs_block_group_cache *bg_cache; + u64 current = 0; + int ret = 0; + + /* Clear all free space cache inodes and its extent data */ + while (1) { + bg_cache = btrfs_lookup_first_block_group(fs_info, current); + if (!bg_cache) + break; + ret = btrfs_clear_free_space_cache(fs_info, bg_cache); + if (ret < 0) + return ret; + current = bg_cache->key.objectid + bg_cache->key.offset; + } + + /* Don't forget to set cache_generation to -1 */ + trans = btrfs_start_transaction(fs_info->tree_root, 0); + if (IS_ERR(trans)) { + error("failed to update super block cache generation"); + return PTR_ERR(trans); + } + btrfs_set_super_cache_generation(fs_info->super_copy, (u64)-1); + btrfs_commit_transaction(trans, fs_info->tree_root); + + return ret; +} + const char * const cmd_check_usage[] = { "btrfs check [options] ", "Check structural integrity of a filesystem (unmounted).", @@ -11197,19 +12568,20 @@ const char * const cmd_check_usage[] = { "--readonly run in read-only mode (default)", "--init-csum-tree create a new CRC tree", "--init-extent-tree create a new extent tree", - "--mode select mode, allows to make some memory/IO", - " trade-offs, where MODE is one of:", + "--mode allows choice of memory/IO trade-offs", + " where MODE is one of:", " original - read inodes and extents to memory (requires", " more memory, does less IO)", " lowmem - try to use less memory but read blocks again", " when needed", "--check-data-csum verify checksums of data blocks", - "-Q|--qgroup-report print a report on qgroup consistency", + "-Q|--qgroup-report print a report on qgroup consistency", "-E|--subvol-extents ", " print subvolume extents and sharing state", "-r|--tree-root use the given bytenr for the tree root", "--chunk-root use the given bytenr for the chunk tree root", "-p|--progress indicate progress", + "--clear-space-cache v1|v2 clear space cache for v1 or v2", NULL }; @@ -11224,9 +12596,11 @@ int cmd_check(int argc, char **argv) u64 chunk_root_bytenr = 0; char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; int ret; + int err = 0; u64 num; int init_csum_tree = 0; int readonly = 0; + int clear_space_cache = 0; int qgroup_report = 0; int qgroups_repaired = 0; unsigned ctree_flags = OPEN_CTREE_EXCLUSIVE; @@ -11236,7 +12610,7 @@ int cmd_check(int argc, char **argv) enum { GETOPT_VAL_REPAIR = 257, GETOPT_VAL_INIT_CSUM, GETOPT_VAL_INIT_EXTENT, GETOPT_VAL_CHECK_CSUM, GETOPT_VAL_READONLY, GETOPT_VAL_CHUNK_TREE, - GETOPT_VAL_MODE }; + GETOPT_VAL_MODE, GETOPT_VAL_CLEAR_SPACE_CACHE }; static const struct option long_options[] = { { "super", required_argument, NULL, 's' }, { "repair", no_argument, NULL, GETOPT_VAL_REPAIR }, @@ -11256,6 +12630,8 @@ int cmd_check(int argc, char **argv) { "progress", no_argument, NULL, 'p' }, { "mode", required_argument, NULL, GETOPT_VAL_MODE }, + { "clear-space-cache", required_argument, NULL, + GETOPT_VAL_CLEAR_SPACE_CACHE}, { NULL, 0, NULL, 0} }; @@ -11270,8 +12646,8 @@ int cmd_check(int argc, char **argv) case 's': num = arg_strtou64(optarg); if (num >= BTRFS_SUPER_MIRROR_MAX) { - fprintf(stderr, - "ERROR: super mirror should be less than: %d\n", + error( + "super mirror should be less than %d", BTRFS_SUPER_MIRROR_MAX); exit(1); } @@ -11327,6 +12703,19 @@ int cmd_check(int argc, char **argv) exit(1); } break; + case GETOPT_VAL_CLEAR_SPACE_CACHE: + if (strcmp(optarg, "v1") == 0) { + clear_space_cache = 1; + } else if (strcmp(optarg, "v2") == 0) { + clear_space_cache = 2; + ctree_flags |= OPEN_CTREE_INVALIDATE_FST; + } else { + error( + "invalid argument to --clear-space-cache, must be v1 or v2"); + exit(1); + } + ctree_flags |= OPEN_CTREE_WRITES; + break; } } @@ -11340,7 +12729,7 @@ int cmd_check(int argc, char **argv) /* This check is the only reason for --readonly to exist */ if (readonly && repair) { - fprintf(stderr, "Repair options are not compatible with --readonly\n"); + error("repair options are not compatible with --readonly"); exit(1); } @@ -11348,7 +12737,7 @@ int cmd_check(int argc, char **argv) * Not supported yet */ if (repair && check_mode == CHECK_MODE_LOWMEM) { - error("Low memory mode doesn't support repair yet"); + error("low memory mode doesn't support repair yet"); exit(1); } @@ -11356,11 +12745,13 @@ int cmd_check(int argc, char **argv) cache_tree_init(&root_cache); if((ret = check_mounted(argv[optind])) < 0) { - fprintf(stderr, "Could not check mount status: %s\n", strerror(-ret)); + error("could not check mount status: %s", strerror(-ret)); + err |= !!ret; goto err_out; } else if(ret) { - fprintf(stderr, "%s is currently mounted. Aborting.\n", argv[optind]); + error("%s is currently mounted, aborting", argv[optind]); ret = -EBUSY; + err |= !!ret; goto err_out; } @@ -11371,27 +12762,62 @@ int cmd_check(int argc, char **argv) info = open_ctree_fs_info(argv[optind], bytenr, tree_root_bytenr, chunk_root_bytenr, ctree_flags); if (!info) { - fprintf(stderr, "Couldn't open file system\n"); + error("cannot open file system"); ret = -EIO; + err |= !!ret; goto err_out; } global_info = info; root = info->fs_root; + if (clear_space_cache == 1) { + if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) { + error( + "free space cache v2 detected, use --clear-space-cache v2"); + ret = 1; + goto close_out; + } + printf("Clearing free space cache\n"); + ret = clear_free_space_cache(info); + if (ret) { + error("failed to clear free space cache"); + ret = 1; + } else { + printf("Free space cache cleared\n"); + } + goto close_out; + } else if (clear_space_cache == 2) { + if (!btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) { + printf("no free space cache v2 to clear\n"); + ret = 0; + goto close_out; + } + printf("Clear free space cache v2\n"); + ret = btrfs_clear_free_space_tree(info); + if (ret) { + error("failed to clear free space cache v2: %d", ret); + ret = 1; + } else { + printf("free space cache v2 cleared\n"); + } + goto close_out; + } /* * repair mode will force us to commit transaction which * will make us fail to load log tree when mounting. */ if (repair && btrfs_super_log_root(info->super_copy)) { - ret = ask_user("repair mode will force to clear out log tree, Are you sure?"); + ret = ask_user("repair mode will force to clear out log tree, are you sure?"); if (!ret) { ret = 1; + err |= !!ret; goto close_out; } ret = zero_log_tree(root); + err |= !!ret; if (ret) { - fprintf(stderr, "fail to zero log tree\n"); + error("failed to zero log tree: %d", ret); goto close_out; } } @@ -11401,6 +12827,7 @@ int cmd_check(int argc, char **argv) printf("Print quota groups for %s\nUUID: %s\n", argv[optind], uuidbuf); ret = qgroup_verify_all(info); + err |= !!ret; if (ret == 0) report_qgroups(1); goto close_out; @@ -11409,6 +12836,7 @@ int cmd_check(int argc, char **argv) printf("Print extent state for subvolume %llu on %s\nUUID: %s\n", subvolid, argv[optind], uuidbuf); ret = print_extent_state(info, subvolid); + err |= !!ret; goto close_out; } printf("Checking filesystem on %s\nUUID: %s\n", argv[optind], uuidbuf); @@ -11416,7 +12844,8 @@ int cmd_check(int argc, char **argv) if (!extent_buffer_uptodate(info->tree_root->node) || !extent_buffer_uptodate(info->dev_root->node) || !extent_buffer_uptodate(info->chunk_root->node)) { - fprintf(stderr, "Critical roots corrupted, unable to fsck the FS\n"); + error("critical roots corrupted, unable to check the filesystem"); + err |= !!ret; ret = -EIO; goto close_out; } @@ -11426,31 +12855,36 @@ int cmd_check(int argc, char **argv) trans = btrfs_start_transaction(info->extent_root, 0); if (IS_ERR(trans)) { - fprintf(stderr, "Error starting transaction\n"); + error("error starting transaction"); ret = PTR_ERR(trans); + err |= !!ret; goto close_out; } if (init_extent_tree) { printf("Creating a new extent tree\n"); ret = reinit_extent_tree(trans, info); + err |= !!ret; if (ret) goto close_out; } if (init_csum_tree) { - fprintf(stderr, "Reinit crc root\n"); + printf("Reinitialize checksum tree\n"); ret = btrfs_fsck_reinit_root(trans, info->csum_root, 0); if (ret) { - fprintf(stderr, "crc root initialization failed\n"); + error("checksum tree initialization failed: %d", + ret); ret = -EIO; + err |= !!ret; goto close_out; } ret = fill_csum_tree(trans, info->csum_root, init_extent_tree); + err |= !!ret; if (ret) { - fprintf(stderr, "crc refilling failed\n"); + error("checksum tree refilling failed: %d", ret); return -EIO; } } @@ -11459,17 +12893,20 @@ int cmd_check(int argc, char **argv) * extent entries for all of the items it finds. */ ret = btrfs_commit_transaction(trans, info->extent_root); + err |= !!ret; if (ret) goto close_out; } if (!extent_buffer_uptodate(info->extent_root->node)) { - fprintf(stderr, "Critical roots corrupted, unable to fsck the FS\n"); + error("critical: extent_root, unable to check the filesystem"); ret = -EIO; + err |= !!ret; goto close_out; } if (!extent_buffer_uptodate(info->csum_root->node)) { - fprintf(stderr, "Checksum root corrupted, rerun with --init-csum-tree option\n"); + error("critical: csum_root, unable to check the filesystem"); ret = -EIO; + err |= !!ret; goto close_out; } @@ -11479,10 +12916,13 @@ int cmd_check(int argc, char **argv) ret = check_chunks_and_extents_v2(root); else ret = check_chunks_and_extents(root); + err |= !!ret; if (ret) - fprintf(stderr, "Errors found in extent allocation tree or chunk allocation\n"); + error( + "errors found in extent allocation tree or chunk allocation"); ret = repair_root_items(info); + err |= !!ret; if (ret < 0) goto close_out; if (repair) { @@ -11495,16 +12935,18 @@ int cmd_check(int argc, char **argv) fprintf(stderr, "Please run a filesystem check with the option --repair to fix them.\n"); ret = 1; + err |= !!ret; goto close_out; } if (!ctx.progress_enabled) { - if (btrfs_fs_compat_ro(info, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) + if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) fprintf(stderr, "checking free space tree\n"); else fprintf(stderr, "checking free space cache\n"); } ret = check_space_cache(root); + err |= !!ret; if (ret) goto out; @@ -11514,23 +12956,31 @@ int cmd_check(int argc, char **argv) * are no gaps in the file extents for inodes, otherwise we can just * ignore it when this happens. */ - no_holes = btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_NO_HOLES); + no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES); if (!ctx.progress_enabled) fprintf(stderr, "checking fs roots\n"); - ret = check_fs_roots(root, &root_cache); + if (check_mode == CHECK_MODE_LOWMEM) + ret = check_fs_roots_v2(root->fs_info); + else + ret = check_fs_roots(root, &root_cache); + err |= !!ret; if (ret) goto out; fprintf(stderr, "checking csums\n"); ret = check_csums(root); + err |= !!ret; if (ret) goto out; fprintf(stderr, "checking root refs\n"); - ret = check_root_refs(root, &root_cache); - if (ret) - goto out; + /* For low memory mode, check_fs_roots_v2 handles root refs */ + if (check_mode != CHECK_MODE_LOWMEM) { + ret = check_root_refs(root, &root_cache); + err |= !!ret; + if (ret) + goto out; + } while (repair && !list_empty(&root->fs_info->recow_ebs)) { struct extent_buffer *eb; @@ -11539,6 +12989,7 @@ int cmd_check(int argc, char **argv) struct extent_buffer, recow); list_del_init(&eb->recow); ret = recow_extent_buffer(root, eb); + err |= !!ret; if (ret) break; } @@ -11548,32 +12999,33 @@ int cmd_check(int argc, char **argv) bad = list_first_entry(&delete_items, struct bad_item, list); list_del_init(&bad->list); - if (repair) + if (repair) { ret = delete_bad_item(root, bad); + err |= !!ret; + } free(bad); } if (info->quota_enabled) { - int err; fprintf(stderr, "checking quota groups\n"); - err = qgroup_verify_all(info); - if (err) + ret = qgroup_verify_all(info); + err |= !!ret; + if (ret) goto out; report_qgroups(0); - err = repair_qgroups(info, &qgroups_repaired); + ret = repair_qgroups(info, &qgroups_repaired); + err |= !!ret; if (err) goto out; + ret = 0; } if (!list_empty(&root->fs_info->recow_ebs)) { - fprintf(stderr, "Transid errors in file system\n"); + error("transid errors in file system"); ret = 1; + err |= !!ret; } out: - /* Don't override original ret */ - if (!ret && qgroups_repaired) - ret = qgroups_repaired; - if (found_old_backref) { /* * there was a disk format change when mixed * backref was in testing tree. The old format @@ -11583,7 +13035,7 @@ out: "The old format is not supported! *" "\n * Please mount the FS in readonly mode, " "backup data and re-format the FS. *\n\n"); - ret = 1; + err |= 1; } printf("found %llu bytes used err is %d\n", (unsigned long long)bytes_used, ret); @@ -11608,5 +13060,5 @@ err_out: if (ctx.progress_enabled) task_deinit(ctx.info); - return ret; + return err; } -- cgit v1.2.3