X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-check.c;h=14816bd36f23ddce2b40c2c28077cb4e9a119e2a;hb=d1e8227ceb5adb8c3a40b0ab31d4787e400fd0df;hp=24c729bed832dfc86a36ddde4b12ed492db9b976;hpb=f26af74da43185897092adc054cf49b691936695;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-check.c b/cmds-check.c index 24c729b..14816bd 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, @@ -2250,31 +2251,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; @@ -2282,11 +2277,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; } @@ -2689,48 +2684,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; } @@ -2921,7 +2914,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 | @@ -2933,10 +2926,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 @@ -2945,27 +2934,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; } @@ -3498,7 +3486,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; @@ -3509,23 +3497,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; @@ -3536,22 +3521,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); @@ -3564,18 +3549,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; } @@ -3715,131 +3699,1219 @@ skip_walking: printf("Btree for root %llu is fixed\n", root->root_key.objectid); } - } + } + + err = merge_root_recs(root, &root_node.root_cache, root_cache); + if (err < 0) + ret = err; + + if (root_node.current) { + root_node.current->checked = 1; + maybe_free_inode_rec(&root_node.inode_cache, + root_node.current); + } + + 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; +} + +#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 */ + +/* + * 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("Warning: root %llu INODE %s[%llu %llu] name too long\n", + 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; +} + +/* + * 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. + * All internal error bitmap will be converted to -EIO, to avoid + * mixing negative and postive return value. + */ +static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref) +{ + struct btrfs_path *path; + struct btrfs_key key; + u64 inode_id; + int ret, err = 0; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + key.objectid = 0; + key.type = 0; + key.offset = 0; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) + goto out; + + while (1) { + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + + /* + * All check must start with inode item, skip if not + */ + if (key.type == BTRFS_INODE_ITEM_KEY) { + ret = check_inode_item(root, path, ext_ref); + err |= ret; + if (err & LAST_ITEM) + goto out; + continue; + } + error("root %llu ITEM[%llu %u %llu] isn't INODE_ITEM, skip to next inode", + root->objectid, key.objectid, key.type, + key.offset); - err = merge_root_recs(root, &root_node.root_cache, root_cache); - if (err < 0) - ret = err; + err |= NO_INODE_ITEM; + inode_id = key.objectid; - if (root_node.current) { - root_node.current->checked = 1; - maybe_free_inode_rec(&root_node.inode_cache, - root_node.current); + /* + * skip to next inode + * TODO: Maybe search_slot() will be faster? + */ + do { + ret = btrfs_next_item(root, path); + if (ret > 0) { + goto out; + } else if (ret < 0) { + err = ret; + goto out; + } + btrfs_item_key_to_cpu(path->nodes[0], &key, + path->slots[0]); + } while (inode_id == key.objectid); } - 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); +out: + err &= ~LAST_ITEM; + if (err && !ret) + ret = -EIO; + btrfs_free_path(path); return ret; } -static int fs_root_objectid(u64 objectid) +/* + * 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) { - if (objectid == BTRFS_TREE_RELOC_OBJECTID || - objectid == BTRFS_DATA_RELOC_TREE_OBJECTID) - return 1; - return is_fstree(objectid); + 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; + + path = btrfs_alloc_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; + } + + 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_free_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_path path; + 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, + BTRFS_FEATURE_INCOMPAT_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); + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; -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); + + 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); +out: + btrfs_free_path(path); return err; } @@ -4317,7 +5389,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; @@ -4326,17 +5398,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; @@ -4356,31 +5422,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; } @@ -5563,31 +6629,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) { @@ -5595,13 +6657,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; } @@ -5610,7 +6672,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; } @@ -5622,7 +6684,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) @@ -5631,7 +6693,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)) { @@ -5788,33 +6850,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) { @@ -5824,7 +6881,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 @@ -5835,10 +6892,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) { @@ -5846,29 +6903,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) @@ -5901,7 +6958,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) { @@ -5914,7 +6971,7 @@ again: } num_bytes = key.objectid - bytenr; } - path->slots[0]++; + path.slots[0]++; } ret = 0; @@ -5925,13 +6982,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; @@ -5947,28 +7004,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); @@ -5977,19 +7030,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) @@ -6009,10 +7062,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; } @@ -6706,7 +7759,6 @@ static int record_extent(struct btrfs_trans_handle *trans, 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; @@ -6742,7 +7794,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)); @@ -6808,6 +7859,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) @@ -6845,11 +7897,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. @@ -6858,6 +7905,16 @@ static struct extent_entry *find_most_right_entry(struct list_head *entries) 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. */ @@ -7303,17 +8360,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. */ @@ -7365,16 +8418,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); @@ -7395,7 +8448,7 @@ out: free(tmp); } - btrfs_free_path(path); + btrfs_release_path(&path); if (!ret && !nr_del) rec->num_duplicates = 0; @@ -7508,15 +8561,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) @@ -7538,8 +8589,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); - btrfs_release_path(path); + 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. @@ -7566,7 +8617,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 @@ -7584,7 +8635,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; @@ -7594,10 +8645,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 @@ -7606,13 +8654,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; @@ -7623,7 +8671,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) @@ -7650,7 +8698,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) @@ -7663,7 +8711,7 @@ out: ret = err; } - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -7672,7 +8720,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; @@ -7687,32 +8735,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); @@ -7722,9 +8765,9 @@ 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); + btrfs_set_extent_flags(path.nodes[0], ei, flags); + btrfs_mark_buffer_dirty(path.nodes[0]); + btrfs_release_path(&path); return btrfs_commit_transaction(trans, root); } @@ -10263,24 +11306,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; } @@ -10295,10 +11334,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) { @@ -10306,15 +11345,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, @@ -10322,7 +11360,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) { @@ -10333,7 +11371,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; } @@ -10341,22 +11379,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; @@ -10366,64 +11400,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; @@ -10441,7 +11474,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; } @@ -10531,7 +11564,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; @@ -10548,31 +11581,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; @@ -10590,26 +11618,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; } @@ -10656,7 +11679,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; @@ -10666,30 +11689,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; @@ -10706,7 +11724,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) { @@ -10716,7 +11734,7 @@ next: } out: - btrfs_free_path(path); + btrfs_release_path(&path); free(buf); return ret; } @@ -10725,7 +11743,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; @@ -10733,15 +11751,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) { @@ -10750,8 +11764,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; @@ -10772,7 +11786,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; @@ -10782,7 +11796,7 @@ next: } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -10790,36 +11804,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) { @@ -10827,19 +11837,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; } @@ -10847,10 +11857,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; } @@ -10898,7 +11908,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)); @@ -10907,24 +11917,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; @@ -10933,18 +11939,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) @@ -11007,11 +12013,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; } @@ -11107,7 +12113,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; @@ -11115,16 +12121,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; @@ -11143,19 +12145,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); @@ -11169,14 +12171,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; @@ -11184,18 +12186,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) @@ -11262,7 +12264,6 @@ const char * const cmd_check_usage[] = { "--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", - " NOTE: v1 support implemented", NULL }; @@ -11277,6 +12278,7 @@ 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; @@ -11384,13 +12386,16 @@ int cmd_check(int argc, char **argv) } break; case GETOPT_VAL_CLEAR_SPACE_CACHE: - if (strcmp(optarg, "v1") != 0) { + 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( - "only v1 support implmented, unrecognized value %s", - optarg); + "invalid argument to --clear-space-cache, must be v1 or v2"); exit(1); } - clear_space_cache = 1; ctree_flags |= OPEN_CTREE_WRITES; break; } @@ -11423,10 +12428,12 @@ int cmd_check(int argc, char **argv) if((ret = check_mounted(argv[optind])) < 0) { error("could not check mount status: %s", strerror(-ret)); + err |= !!ret; goto err_out; } else if(ret) { error("%s is currently mounted, aborting", argv[optind]); ret = -EBUSY; + err |= !!ret; goto err_out; } @@ -11439,16 +12446,17 @@ int cmd_check(int argc, char **argv) if (!info) { error("cannot open file system"); ret = -EIO; + err |= !!ret; goto err_out; } global_info = info; root = info->fs_root; - if (clear_space_cache) { + if (clear_space_cache == 1) { if (btrfs_fs_compat_ro(info, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) { error( - "free space cache v2 detected, clearing not implemented"); + "free space cache v2 detected, use --clear-space-cache v2"); ret = 1; goto close_out; } @@ -11461,6 +12469,22 @@ int cmd_check(int argc, char **argv) printf("Free space cache cleared\n"); } goto close_out; + } else if (clear_space_cache == 2) { + if (!btrfs_fs_compat_ro(info, + BTRFS_FEATURE_COMPAT_RO_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; } /* @@ -11471,9 +12495,11 @@ int cmd_check(int argc, char **argv) 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) { error("failed to zero log tree: %d", ret); goto close_out; @@ -11485,6 +12511,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; @@ -11493,6 +12520,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); @@ -11501,6 +12529,7 @@ int cmd_check(int argc, char **argv) !extent_buffer_uptodate(info->dev_root->node) || !extent_buffer_uptodate(info->chunk_root->node)) { error("critical roots corrupted, unable to check the filesystem"); + err |= !!ret; ret = -EIO; goto close_out; } @@ -11512,12 +12541,14 @@ int cmd_check(int argc, char **argv) if (IS_ERR(trans)) { 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; } @@ -11529,11 +12560,13 @@ int cmd_check(int argc, char **argv) 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) { error("checksum tree refilling failed: %d", ret); return -EIO; @@ -11544,30 +12577,36 @@ 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)) { error("critical: extent_root, unable to check the filesystem"); ret = -EIO; + err |= !!ret; goto close_out; } if (!extent_buffer_uptodate(info->csum_root->node)) { error("critical: csum_root, unable to check the filesystem"); ret = -EIO; + err |= !!ret; goto close_out; } if (!ctx.progress_enabled) - printf("checking extents"); + fprintf(stderr, "checking extents\n"); if (check_mode == CHECK_MODE_LOWMEM) ret = check_chunks_and_extents_v2(root); else ret = check_chunks_and_extents(root); + err |= !!ret; if (ret) - printf("Errors found in extent allocation tree or chunk allocation"); + 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) { @@ -11580,6 +12619,7 @@ 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; } @@ -11590,6 +12630,7 @@ int cmd_check(int argc, char **argv) fprintf(stderr, "checking free space cache\n"); } ret = check_space_cache(root); + err |= !!ret; if (ret) goto out; @@ -11603,19 +12644,28 @@ int cmd_check(int argc, char **argv) BTRFS_FEATURE_INCOMPAT_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; @@ -11624,6 +12674,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; } @@ -11633,32 +12684,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)) { 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 @@ -11668,7 +12720,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); @@ -11693,5 +12745,5 @@ err_out: if (ctx.progress_enabled) task_deinit(ctx.info); - return ret; + return err; }