X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-check.c;h=ad7c81b219581e58dfbd419c83ff99845b8f7f24;hb=c1c987503412a8e98e40da2548c6b78888535641;hp=b965f2e40f22e9742b137caca58472d6d10d7ecc;hpb=262425b6504af0b643c52c0dbfc0f26f79d7fe5b;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-check.c b/cmds-check.c index b965f2e..ad7c81b 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -40,8 +40,9 @@ #include "qgroup-verify.h" #include "rbtree-utils.h" #include "backref.h" -#include "ulist.h" +#include "kernel-shared/ulist.h" #include "hash.h" +#include "help.h" enum task_position { TASK_EXTENTS, @@ -113,6 +114,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); @@ -1459,8 +1478,7 @@ out: return has_parent ? 0 : 2; } -static int process_dir_item(struct btrfs_root *root, - struct extent_buffer *eb, +static int process_dir_item(struct extent_buffer *eb, int slot, struct btrfs_key *key, struct shared_node *active_node) { @@ -1494,13 +1512,19 @@ static int process_dir_item(struct btrfs_root *root, filetype = btrfs_dir_type(eb, di); rec->found_size += name_len; - if (name_len <= BTRFS_NAME_LEN) { + if (cur + sizeof(*di) + name_len > total || + name_len > BTRFS_NAME_LEN) { + error = REF_ERR_NAME_TOO_LONG; + + if (cur + sizeof(*di) > total) + break; + len = min_t(u32, total - cur - sizeof(*di), + BTRFS_NAME_LEN); + } else { len = name_len; error = 0; - } else { - len = BTRFS_NAME_LEN; - error = REF_ERR_NAME_TOO_LONG; } + read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len); if (location.type == BTRFS_INODE_ITEM_KEY) { @@ -1551,13 +1575,22 @@ static int process_inode_ref(struct extent_buffer *eb, while (cur < total) { name_len = btrfs_inode_ref_name_len(eb, ref); index = btrfs_inode_ref_index(eb, ref); - if (name_len <= BTRFS_NAME_LEN) { + + /* inode_ref + namelen should not cross item boundary */ + if (cur + sizeof(*ref) + name_len > total || + name_len > BTRFS_NAME_LEN) { + if (total < cur + sizeof(*ref)) + break; + + /* Still try to read out the remaining part */ + len = min_t(u32, total - cur - sizeof(*ref), + BTRFS_NAME_LEN); + error = REF_ERR_NAME_TOO_LONG; + } else { len = name_len; error = 0; - } else { - len = BTRFS_NAME_LEN; - error = REF_ERR_NAME_TOO_LONG; } + read_extent_buffer(eb, namebuf, (unsigned long)(ref + 1), len); add_inode_backref(inode_cache, key->objectid, key->offset, index, namebuf, len, 0, key->type, error); @@ -1817,7 +1850,7 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb, switch (key.type) { case BTRFS_DIR_ITEM_KEY: case BTRFS_DIR_INDEX_KEY: - ret = process_dir_item(root, eb, i, &key, active_node); + ret = process_dir_item(eb, i, &key, active_node); break; case BTRFS_INODE_REF_KEY: ret = process_inode_ref(eb, i, &key, active_node); @@ -1839,6 +1872,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); + +/* + * Returns >0 Found error, not fatal, should continue + * Returns <0 Fatal error, must exit the whole check + * Returns 0 No errors found + */ +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; + if (err && !ret) + ret = err; + return ret; +} + static void reada_walk_down(struct btrfs_root *root, struct extent_buffer *node, int slot) { @@ -1875,8 +1998,7 @@ static void reada_walk_down(struct btrfs_root *root, * which makes leaf owner check not so strong, key check should be * sufficient enough for that case. */ -static int check_child_node(struct btrfs_root *root, - struct extent_buffer *parent, int slot, +static int check_child_node(struct extent_buffer *parent, int slot, struct extent_buffer *child) { struct btrfs_key parent_key; @@ -1912,10 +2034,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, @@ -2020,8 +2198,9 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path, } } - ret = check_child_node(root, cur, path->slots[*level], next); + ret = check_child_node(cur, path->slots[*level], next); if (ret) { + free_extent_buffer(next); err = ret; goto out; } @@ -2046,6 +2225,115 @@ out: return err; } +static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path, + unsigned int ext_ref); + +/* + * Returns >0 Found error, should continue + * Returns <0 Fatal error, must exit the whole check + * Returns 0 No errors found + */ +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(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) { @@ -2070,6 +2358,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; @@ -2245,8 +2554,6 @@ static int add_missing_dir_index(struct btrfs_root *root, } static int delete_dir_index(struct btrfs_root *root, - struct cache_tree *inode_cache, - struct inode_record *rec, struct inode_backref *backref) { struct btrfs_trans_handle *trans; @@ -2288,7 +2595,7 @@ static int delete_dir_index(struct btrfs_root *root, static int create_inode_item(struct btrfs_root *root, struct inode_record *rec, - struct inode_backref *backref, int root_dir) + int root_dir) { struct btrfs_trans_handle *trans; struct btrfs_inode_item inode_item; @@ -2354,7 +2661,7 @@ static int repair_inode_backrefs(struct btrfs_root *root, list_for_each_entry_safe(backref, tmp, &rec->backrefs, list) { if (!delete && rec->ino == root_dirid) { if (!rec->found_inode_item) { - ret = create_inode_item(root, rec, backref, 1); + ret = create_inode_item(root, rec, 1); if (ret) break; repaired++; @@ -2369,12 +2676,13 @@ static int repair_inode_backrefs(struct btrfs_root *root, ((backref->found_dir_index && !backref->found_inode_ref) || (backref->found_dir_index && backref->found_inode_ref && (backref->errors & REF_ERR_INDEX_UNMATCH)))) { - ret = delete_dir_index(root, inode_cache, rec, backref); + ret = delete_dir_index(root, backref); if (ret) break; repaired++; list_del(&backref->list); free(backref); + continue; } if (!delete && !backref->found_dir_index && @@ -2385,12 +2693,12 @@ static int repair_inode_backrefs(struct btrfs_root *root, break; repaired++; if (backref->found_dir_item && - backref->found_dir_index && backref->found_dir_index) { if (!backref->errors && backref->found_inode_ref) { list_del(&backref->list); free(backref); + continue; } } } @@ -2440,7 +2748,7 @@ static int repair_inode_backrefs(struct btrfs_root *root, backref->found_dir_item && !(backref->errors & REF_ERR_INDEX_UNMATCH) && !rec->found_inode_item)) { - ret = create_inode_item(root, rec, backref, 0); + ret = create_inode_item(root, rec, 0); if (ret) break; repaired++; @@ -2564,6 +2872,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, @@ -2609,11 +2942,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, @@ -2977,21 +3308,6 @@ static int check_inode_recs(struct btrfs_root *root, } /* - * 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. * @@ -3528,8 +3844,7 @@ static int repair_btree(struct btrfs_root *root, path.slots[level]); /* Remove the ptr */ - ret = btrfs_del_ptr(trans, root, &path, level, - path.slots[level]); + ret = btrfs_del_ptr(root, &path, level, path.slots[level]); if (ret < 0) goto out; /* @@ -3827,22 +4142,6 @@ out: 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 */ - /* * Find DIR_ITEM/DIR_INDEX for the given key and check it with the specified * INODE_REF/INODE_EXTREF match. @@ -3942,16 +4241,22 @@ static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key, if (imode_to_type(mode) != filetype) goto next; - if (name_len <= BTRFS_NAME_LEN) { - len = name_len; - } else { - len = BTRFS_NAME_LEN; + if (cur + sizeof(*di) + name_len > total || + name_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); + root->objectid, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, name_len); + + if (cur + sizeof(*di) > total) + break; + len = min_t(u32, total - cur - sizeof(*di), + BTRFS_NAME_LEN); + } else { + len = name_len; } + read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len); if (len != namelen || strncmp(namebuf, name, len)) goto next; @@ -4012,12 +4317,16 @@ next: 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; + if (cur + sizeof(*ref) + name_len > total || + name_len > BTRFS_NAME_LEN) { warning("root %llu INODE_REF[%llu %llu] name too long", root->objectid, ref_key->objectid, ref_key->offset); + + if (total < cur + sizeof(*ref)) + goto out; + len = min_t(u32, total - cur - sizeof(*ref), BTRFS_NAME_LEN); + } else { + len = name_len; } read_extent_buffer(node, namebuf, (unsigned long)(ref + 1), len); @@ -4050,6 +4359,7 @@ next: if (cur < total) goto next; +out: return err; } @@ -4187,16 +4497,22 @@ static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key, if (index != (u64)-1 && index != ref_index) goto next_ref; - if (ref_namelen <= BTRFS_NAME_LEN) { - len = ref_namelen; - } else { - len = BTRFS_NAME_LEN; + if (cur + sizeof(*ref) + ref_namelen > total || + ref_namelen > 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); + + if (cur + sizeof(*ref) > total) + break; + len = min_t(u32, total - cur - sizeof(*ref), + BTRFS_NAME_LEN); + } else { + len = ref_namelen; } + read_extent_buffer(node, ref_namebuf, (unsigned long)(ref + 1), len); @@ -4253,7 +4569,7 @@ extref: len = ref_namelen; } else { len = BTRFS_NAME_LEN; - warning("Warning: root %llu INODE %s[%llu %llu] name too long\n", + warning("root %llu INODE %s[%llu %llu] name too long", root->objectid, key->type == BTRFS_INODE_REF_KEY ? "REF" : "EXTREF", @@ -4328,15 +4644,20 @@ static int check_dir_item(struct btrfs_root *root, struct btrfs_key *key, 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; + if (cur + sizeof(*di) + name_len > total || + name_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); + + if (cur + sizeof(*di) > total) + break; + len = min_t(u32, total - cur - sizeof(*di), + BTRFS_NAME_LEN); + } else { + len = name_len; } (*size) += name_len; @@ -4422,25 +4743,40 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey, u64 disk_bytenr; u64 disk_num_bytes; u64 extent_num_bytes; - u64 found; + u64 extent_offset; + u64 csum_found; /* In byte size, sectorsize aligned */ + u64 search_start; /* Logical range start we search for csum */ + u64 search_len; /* Logical range len we search for csum */ unsigned int extent_type; unsigned int is_hole; + int compressed = 0; int ret; int err = 0; fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); + /* Check inline extent */ 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) + compressed = btrfs_file_extent_compression(node, fi); + if (extent_num_bytes == 0) { + error( + "root %llu EXTENT_DATA[%llu %llu] has empty inline extent", + root->objectid, fkey->objectid, fkey->offset); + err |= FILE_EXTENT_ERROR; + } + if (!compressed && extent_num_bytes != item_inline_len) { + error( + "root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u", + root->objectid, fkey->objectid, fkey->offset, + extent_num_bytes, item_inline_len); err |= FILE_EXTENT_ERROR; + } *size += extent_num_bytes; return err; } @@ -4458,24 +4794,45 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey, 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); + extent_offset = btrfs_file_extent_offset(node, fi); + compressed = btrfs_file_extent_compression(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) { + /* + * Check EXTENT_DATA csum + * + * For plain (uncompressed) extent, we should only check the range + * we're referring to, as it's possible that part of prealloc extent + * has been written, and has csum: + * + * |<--- Original large preallocated extent A ---->| + * |<- Prealloc File Extent ->|<- Regular Extent ->| + * No csum Has csum + * + * For compressed extent, we should check the whole range. + */ + if (!compressed) { + search_start = disk_bytenr + extent_offset; + search_len = extent_num_bytes; + } else { + search_start = disk_bytenr; + search_len = disk_num_bytes; + } + ret = count_csum_range(root, search_start, search_len, &csum_found); + if (csum_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)) { + !is_hole && (ret < 0 || csum_found < search_len)) { 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) { + error("root %llu EXTENT_DATA[%llu %llu] csum missing, have: %llu, expected: %llu", + root->objectid, fkey->objectid, fkey->offset, + csum_found, search_len); + } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && csum_found > 0) { err |= ODD_CSUM_ITEM; - error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have datasum", - root->objectid, fkey->objectid, fkey->offset); + error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have csum, but has: %llu", + root->objectid, fkey->objectid, fkey->offset, csum_found); } /* Check EXTENT_DATA hole */ @@ -4643,95 +5000,305 @@ out: 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 && !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; + error("first inode item of root %llu is missing", + root->objectid); + } + + 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; + int level; + int err = 0; + + /* + * 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) { + ret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref); + err |= !!ret; + + /* if ret is negative, walk shall stop */ + if (ret < 0) { + ret = err; + break; + } + + ret = walk_up_tree_v2(root, &path, &level); + if (ret != 0) { + /* Normal exit, reset ret to err */ + ret = err; + 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 (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); - } + 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; + + 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; } + 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; } /* - * Iterate all item on the tree and call check_inode_item() to check. + * Check all fs/file tree in low_memory mode. * - * @root: the root of the tree to be checked. - * @ext_ref: the EXTENDED_IREF feature + * 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 found. - * Return <0 for error. - * All internal error bitmap will be converted to -EIO, to avoid - * mixing negative and postive return value. + * Return 0 if no error occurred. */ -static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref) +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; - u64 inode_id; - int ret, err = 0; + struct extent_buffer *node; + unsigned int ext_ref; + int slot; + int ret; + int err = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + ext_ref = btrfs_fs_incompat(fs_info, EXTENDED_IREF); - key.objectid = 0; - key.type = 0; + btrfs_init_path(&path); + key.objectid = BTRFS_FS_TREE_OBJECTID; key.offset = 0; + key.type = BTRFS_ROOT_ITEM_KEY; - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) + ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0); + if (ret < 0) { + err = ret; + goto out; + } else if (ret > 0) { + err = -ENOENT; goto out; + } while (1) { - btrfs_item_key_to_cpu(path->nodes[0], &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) { + cur_root = btrfs_read_fs_root_no_cache(fs_info, + &key); + } else { + key.offset = (u64)-1; + cur_root = btrfs_read_fs_root(fs_info, &key); + } - /* - * 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); + if (IS_ERR(cur_root)) { + error("Fail to read fs/subvol tree: %lld", + key.objectid); + err = -EIO; + goto next; + } - err |= NO_INODE_ITEM; - inode_id = key.objectid; + ret = check_fs_root_v2(cur_root, ext_ref); + err |= ret; - /* - * 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); + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) + btrfs_free_fs_root(cur_root); + } else if (key.type == BTRFS_ROOT_REF_KEY || + key.type == BTRFS_ROOT_BACKREF_KEY) { + ret = check_root_ref(tree_root, &key, node, slot); + err |= ret; + } +next: + ret = btrfs_next_item(tree_root, &path); + if (ret > 0) + goto out; + if (ret < 0) { + err = ret; + goto out; + } } out: - err &= ~LAST_ITEM; - if (err && !ret) - ret = -EIO; - btrfs_free_path(path); - return ret; + btrfs_release_path(&path); + return err; } static int all_backpointers_checked(struct extent_record *rec, int print_errs) @@ -4861,8 +5428,7 @@ static int free_all_extent_backrefs(struct extent_record *rec) return 0; } -static void free_extent_record_cache(struct btrfs_fs_info *fs_info, - struct cache_tree *extent_cache) +static void free_extent_record_cache(struct cache_tree *extent_cache) { struct cache_extent *cache; struct extent_record *rec; @@ -5061,9 +5627,7 @@ static int swap_values(struct btrfs_root *root, struct btrfs_path *path, return 0; } -static int fix_key_order(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path) +static int fix_key_order(struct btrfs_root *root, struct btrfs_path *path) { struct extent_buffer *buf; struct btrfs_key k1, k2; @@ -5091,8 +5655,7 @@ static int fix_key_order(struct btrfs_trans_handle *trans, return ret; } -static int delete_bogus_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, +static int delete_bogus_item(struct btrfs_root *root, struct btrfs_path *path, struct extent_buffer *buf, int slot) { @@ -5127,9 +5690,7 @@ static int delete_bogus_item(struct btrfs_trans_handle *trans, return 0; } -static int fix_item_offset(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_path *path) +static int fix_item_offset(struct btrfs_root *root, struct btrfs_path *path) { struct extent_buffer *buf; int i; @@ -5146,8 +5707,7 @@ again: BTRFS_LEAF_DATA_SIZE(root)) { if (btrfs_item_end_nr(buf, i) > BTRFS_LEAF_DATA_SIZE(root)) { - ret = delete_bogus_item(trans, root, path, - buf, i); + ret = delete_bogus_item(root, path, buf, i); if (!ret) goto again; fprintf(stderr, "item is off the end of the " @@ -5161,8 +5721,7 @@ again: btrfs_item_offset_nr(buf, i - 1)) { if (btrfs_item_end_nr(buf, i) > btrfs_item_offset_nr(buf, i - 1)) { - ret = delete_bogus_item(trans, root, path, - buf, i); + ret = delete_bogus_item(root, path, buf, i); if (!ret) goto again; fprintf(stderr, "items overlap, can't fix\n"); @@ -5254,9 +5813,9 @@ static int try_to_fix_bad_block(struct btrfs_root *root, break; } if (status == BTRFS_TREE_BLOCK_BAD_KEY_ORDER) - ret = fix_key_order(trans, search_root, &path); + ret = fix_key_order(search_root, &path); else if (status == BTRFS_TREE_BLOCK_INVALID_OFFSETS) - ret = fix_item_offset(trans, search_root, &path); + ret = fix_item_offset(search_root, &path); if (ret) { btrfs_commit_transaction(trans, search_root); break; @@ -5509,6 +6068,7 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache, struct extent_record *rec; int ret = 0; + BUG_ON(tmpl->max_size == 0); rec = malloc(sizeof(*rec)); if (!rec) return -ENOMEM; @@ -5672,6 +6232,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr, tmpl.start = bytenr; tmpl.nr = 1; tmpl.metadata = 1; + tmpl.max_size = 1; ret = add_extent_rec_nolookup(extent_cache, &tmpl); if (ret) @@ -6312,14 +6873,16 @@ static int process_extent_item(struct btrfs_root *root, ret = add_tree_backref(extent_cache, key.objectid, 0, offset, 0); if (ret < 0) - error("add_tree_backref failed: %s", + error( + "add_tree_backref failed (extent items tree block): %s", strerror(-ret)); break; case BTRFS_SHARED_BLOCK_REF_KEY: ret = add_tree_backref(extent_cache, key.objectid, offset, 0, 0); if (ret < 0) - error("add_tree_backref failed: %s", + error( + "add_tree_backref failed (extent items shared block): %s", strerror(-ret)); break; case BTRFS_EXTENT_DATA_REF_KEY: @@ -6560,8 +7123,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", @@ -6636,7 +7198,7 @@ again: csum = ~(u32)0; tmp = offset + data_checked; - csum = btrfs_csum_data(NULL, (char *)data + tmp, + csum = btrfs_csum_data((char *)data + tmp, csum, root->sectorsize); btrfs_csum_final(csum, (u8 *)&csum); @@ -6923,8 +7485,7 @@ static int is_dropped_key(struct btrfs_key *key, * assumption and simply indicate that we _think_ that the FULL BACKREF needs to * be set or not and then we can check later once we've gathered all the refs. */ -static int calc_extent_flag(struct btrfs_root *root, - struct cache_tree *extent_cache, +static int calc_extent_flag(struct cache_tree *extent_cache, struct extent_buffer *buf, struct root_item_record *ri, u64 *flags) @@ -7118,7 +7679,7 @@ static int run_next_block(struct btrfs_root *root, btrfs_header_level(buf), 1, NULL, &flags); if (ret < 0) { - ret = calc_extent_flag(root, extent_cache, buf, ri, &flags); + ret = calc_extent_flag(extent_cache, buf, ri, &flags); if (ret < 0) { fprintf(stderr, "Couldn't calc extent flags\n"); flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; @@ -7126,7 +7687,7 @@ static int run_next_block(struct btrfs_root *root, } } else { flags = 0; - ret = calc_extent_flag(root, extent_cache, buf, ri, &flags); + ret = calc_extent_flag(extent_cache, buf, ri, &flags); if (ret < 0) { fprintf(stderr, "Couldn't calc extent flags\n"); flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; @@ -7235,7 +7796,8 @@ static int run_next_block(struct btrfs_root *root, ret = add_tree_backref(extent_cache, key.objectid, 0, key.offset, 0); if (ret < 0) - error("add_tree_backref failed: %s", + error( + "add_tree_backref failed (leaf tree block): %s", strerror(-ret)); continue; } @@ -7243,7 +7805,8 @@ static int run_next_block(struct btrfs_root *root, ret = add_tree_backref(extent_cache, key.objectid, key.offset, 0, 0); if (ret < 0) - error("add_tree_backref failed: %s", + error( + "add_tree_backref failed (leaf shared block): %s", strerror(-ret)); continue; } @@ -7348,7 +7911,8 @@ static int run_next_block(struct btrfs_root *root, ret = add_tree_backref(extent_cache, ptr, parent, owner, 1); if (ret < 0) { - error("add_tree_backref failed: %s", + error( + "add_tree_backref failed (non-leaf block): %s", strerror(-ret)); continue; } @@ -7485,7 +8049,7 @@ out: static int delete_extent_records(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, - u64 bytenr, u64 new_len) + u64 bytenr) { struct btrfs_key key; struct btrfs_key found_key; @@ -7573,7 +8137,7 @@ 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; @@ -8095,8 +8659,7 @@ out: return ret; } -static int process_duplicates(struct btrfs_root *root, - struct cache_tree *extent_cache, +static int process_duplicates(struct cache_tree *extent_cache, struct extent_record *rec) { struct extent_record *good, *tmp; @@ -8491,7 +9054,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, - rec->start, rec->max_size); + rec->start); if (ret < 0) goto out; @@ -8530,6 +9093,10 @@ out: ret = err; } + if (!ret) + fprintf(stderr, "Repaired extent references for %llu\n", + (unsigned long long)rec->start); + btrfs_release_path(&path); return ret; } @@ -8587,7 +9154,12 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info, 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); + 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 */ @@ -8652,7 +9224,7 @@ again: del_ptr: printk("deleting pointer to block %Lu\n", corrupt->cache.start); - ret = btrfs_del_ptr(trans, info->extent_root, &path, level, slot); + ret = btrfs_del_ptr(info->extent_root, &path, level, slot); out: btrfs_release_path(&path); @@ -8694,8 +9266,7 @@ static void reset_cached_block_groups(struct btrfs_fs_info *fs_info) &start, &end, EXTENT_DIRTY); if (ret) break; - clear_extent_dirty(&fs_info->free_space_cache, start, end, - GFP_NOFS); + clear_extent_dirty(&fs_info->free_space_cache, start, end); } start = 0; @@ -8714,11 +9285,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) { /* @@ -8732,8 +9300,7 @@ static int check_extent_refs(struct btrfs_root *root, rec = container_of(cache, struct extent_record, cache); set_extent_dirty(root->fs_info->excluded_extents, rec->start, - rec->start + rec->max_size - 1, - GFP_NOFS); + rec->start + rec->max_size - 1); cache = next_cache_extent(cache); } @@ -8742,8 +9309,7 @@ static int check_extent_refs(struct btrfs_root *root, while(cache) { set_extent_dirty(root->fs_info->excluded_extents, cache->start, - cache->start + cache->size - 1, - GFP_NOFS); + cache->start + cache->size - 1); cache = next_cache_extent(cache); } prune_corrupt_blocks(root->fs_info); @@ -8768,7 +9334,7 @@ static int check_extent_refs(struct btrfs_root *root, * process_duplicates() will return 0, otherwise it will return * 1 and we */ - if (process_duplicates(root, extent_cache, rec)) + if (process_duplicates(extent_cache, rec)) continue; ret = delete_duplicate_records(root, rec); if (ret < 0) @@ -8787,9 +9353,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; @@ -8797,7 +9362,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; } @@ -8811,54 +9375,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); @@ -8866,9 +9407,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; } /* @@ -8880,7 +9420,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; } @@ -8888,17 +9427,15 @@ 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, - GFP_NOFS); + rec->start + rec->max_size - 1); free(rec); } repair_abort: @@ -8921,11 +9458,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) @@ -9483,6 +10018,8 @@ out: free_extent_cache_tree(&pending); free_extent_cache_tree(&reada); free_extent_cache_tree(&nodes); + free_root_item_list(&normal_trees); + free_root_item_list(&dropping_trees); return ret; loop: free_corrupt_blocks_tree(root->fs_info->corrupt_blocks); @@ -9494,7 +10031,7 @@ loop: free_block_group_tree(&block_group_cache); free_device_cache_tree(&dev_cache); free_device_extent_tree(&dev_extent_cache); - free_extent_record_cache(root->fs_info, &extent_cache); + free_extent_record_cache(&extent_cache); free_root_item_list(&normal_trees); free_root_item_list(&dropping_trees); extent_io_tree_cleanup(&excluded_extents); @@ -9531,14 +10068,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; @@ -9623,9 +10164,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) @@ -9676,12 +10224,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; @@ -9693,7 +10239,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 || @@ -9732,17 +10277,14 @@ static int check_extent_data_item(struct btrfs_root *root, dbref_key.offset = btrfs_file_extent_disk_num_bytes(eb, fi); ret = btrfs_search_slot(NULL, extent_root, &dbref_key, &path, 0, 0); - if (ret) { - err |= BACKREF_MISSING; - goto error; - } + if (ret) + goto out; leaf = path.nodes[0]; slot = path.slots[0]; 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( @@ -9752,14 +10294,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); @@ -9785,11 +10319,10 @@ static int check_extent_data_item(struct btrfs_root *root, ptr += btrfs_extent_inline_ref_size(type); } - /* Didn't found inlined data backref, try EXTENT_DATA_REF_KEY */ if (!found_dbackref) { btrfs_release_path(&path); - btrfs_init_path(&path); + /* Didn't find inlined data backref, try EXTENT_DATA_REF_KEY */ dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi); dbref_key.type = BTRFS_EXTENT_DATA_REF_KEY; dbref_key.offset = hash_extent_data_ref(root->objectid, @@ -9797,13 +10330,32 @@ static int check_extent_data_item(struct btrfs_root *root, ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &dbref_key, &path, 0, 0); - if (!ret) + if (!ret) { + found_dbackref = 1; + goto out; + } + + btrfs_release_path(&path); + + /* + * Neither inlined nor EXTENT_DATA_REF found, try + * SHARED_DATA_REF as last chance. + */ + dbref_key.objectid = disk_bytenr; + dbref_key.type = BTRFS_SHARED_DATA_REF_KEY; + dbref_key.offset = eb->start; + + ret = btrfs_search_slot(NULL, root->fs_info->extent_root, + &dbref_key, &path, 0, 0); + if (!ret) { found_dbackref = 1; + goto out; + } } +out: if (!found_dbackref) err |= BACKREF_MISSING; -error: btrfs_release_path(&path); if (err & BACKREF_MISSING) { error("data extent[%llu %llu] backref lost", @@ -9986,6 +10538,34 @@ out: } /* + * 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. */ @@ -10007,6 +10587,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; @@ -10098,6 +10685,8 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info, leaf = path.nodes[0]; slot = path.slots[0]; + if (slot >= btrfs_header_nritems(leaf)) + goto next; btrfs_item_key_to_cpu(leaf, &key, slot); if (key.objectid != objectid || key.type != BTRFS_EXTENT_DATA_KEY) break; @@ -10113,6 +10702,7 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info, offset) found_count++; +next: ret = btrfs_next_item(root, &path); if (ret) break; @@ -10240,13 +10830,20 @@ static int check_extent_item(struct btrfs_fs_info *fs_info, } end = (unsigned long)ei + item_size; - if (ptr >= end) { +next: + /* Reached extent item end normally */ + if (ptr == end) + goto out; + + /* Beyond extent item end, wrong item size */ + if (ptr > end) { err |= ITEM_SIZE_MISMATCH; + error("extent item at bytenr %llu slot %d has wrong size", + eb->start, slot); goto out; } /* Now check every backref in this extent item */ -next: iref = (struct btrfs_extent_inline_ref *)ptr; type = btrfs_extent_inline_ref_type(eb, iref); offset = btrfs_extent_inline_ref_offset(eb, iref); @@ -10283,8 +10880,7 @@ next: } ptr += btrfs_extent_inline_ref_size(type); - if (ptr < end) - goto next; + goto next; out: return err; @@ -10385,8 +10981,10 @@ static int check_dev_item(struct btrfs_fs_info *fs_info, /* Iterate dev_extents to calculate the used space of a device */ while (1) { - btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) + goto next; + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); if (key.objectid > dev_id) break; if (key.type != BTRFS_DEV_EXTENT_KEY || key.objectid != dev_id) @@ -10483,6 +11081,11 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info, /* Iterate extent tree to account used space */ while (1) { leaf = path.nodes[0]; + + /* Search slot can point to the last item beyond leaf nritems */ + if (path.slots[0] >= btrfs_header_nritems(leaf)) + goto next; + btrfs_item_key_to_cpu(leaf, &extent_key, path.slots[0]); if (extent_key.objectid >= bg_key.objectid + bg_key.offset) break; @@ -10944,7 +11547,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; @@ -10953,6 +11560,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) @@ -11177,8 +11786,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info) key.objectid, key.offset, btrfs_chunk_length(leaf, chunk)); set_extent_dirty(&fs_info->free_space_cache, key.offset, - key.offset + btrfs_chunk_length(leaf, chunk), - GFP_NOFS); + key.offset + btrfs_chunk_length(leaf, chunk)); path.slots[0]++; } start = 0; @@ -11310,7 +11918,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 " @@ -11841,8 +12449,7 @@ out: return ret; } -static int maybe_repair_root_item(struct btrfs_fs_info *info, - struct btrfs_path *path, +static int maybe_repair_root_item(struct btrfs_path *path, const struct btrfs_key *root_key, const int read_only_mode) { @@ -11997,8 +12604,7 @@ again: if (found_key.objectid == BTRFS_TREE_RELOC_OBJECTID) goto next; - ret = maybe_repair_root_item(info, &path, &found_key, - trans ? 0 : 1); + ret = maybe_repair_root_item(&path, &found_key, trans ? 0 : 1); if (ret < 0) goto out; if (ret) { @@ -12097,6 +12703,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; @@ -12246,10 +12853,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; } @@ -12262,14 +12871,14 @@ 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 == 1) { - if (btrfs_fs_compat_ro(info, - BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) { + if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) { error( "free space cache v2 detected, use --clear-space-cache v2"); ret = 1; @@ -12285,8 +12894,7 @@ int cmd_check(int argc, char **argv) } goto close_out; } else if (clear_space_cache == 2) { - if (!btrfs_fs_compat_ro(info, - BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) { + if (!btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) { printf("no free space cache v2 to clear\n"); ret = 0; goto close_out; @@ -12310,9 +12918,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; @@ -12324,6 +12934,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; @@ -12332,6 +12943,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); @@ -12340,6 +12952,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; } @@ -12351,12 +12964,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; } @@ -12368,11 +12983,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; @@ -12383,17 +13000,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)) { 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; } @@ -12403,13 +13023,17 @@ 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) error( "errors found in extent allocation tree or chunk allocation"); ret = repair_root_items(info); - if (ret < 0) + err |= !!ret; + if (ret < 0) { + error("failed to repair root items: %s", strerror(-ret)); goto close_out; + } if (repair) { fprintf(stderr, "Fixed %d roots.\n", ret); ret = 0; @@ -12420,18 +13044,25 @@ 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); - if (ret) + err |= !!ret; + if (ret) { + if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) + error("errors found in free space tree"); + else + error("errors found in free space cache"); goto out; + } /* * We used to have to have these hole extents in between our real @@ -12439,23 +13070,37 @@ 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 (ret) + 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) { + error("errors found in fs roots"); goto out; + } fprintf(stderr, "checking csums\n"); ret = check_csums(root); - if (ret) + err |= !!ret; + if (ret) { + error("errors found in csum tree"); 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) { + error("errors found in root refs"); + goto out; + } + } while (repair && !list_empty(&root->fs_info->recow_ebs)) { struct extent_buffer *eb; @@ -12464,8 +13109,11 @@ int cmd_check(int argc, char **argv) struct extent_buffer, recow); list_del_init(&eb->recow); ret = recow_extent_buffer(root, eb); - if (ret) + err |= !!ret; + if (ret) { + error("fails to fix transid errors"); break; + } } while (!list_empty(&delete_items)) { @@ -12473,32 +13121,37 @@ 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) { + error("failed to check quota groups"); goto out; + } report_qgroups(0); - err = repair_qgroups(info, &qgroups_repaired); - if (err) + ret = repair_qgroups(info, &qgroups_repaired); + err |= !!ret; + if (err) { + error("failed to repair quota groups"); 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 @@ -12508,10 +13161,14 @@ 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); + printf("found %llu bytes used, ", + (unsigned long long)bytes_used); + if (err) + printf("error(s) found\n"); + else + printf("no error found\n"); printf("total csum bytes: %llu\n",(unsigned long long)total_csum_bytes); printf("total tree bytes: %llu\n", (unsigned long long)total_btree_bytes); @@ -12533,5 +13190,5 @@ err_out: if (ctx.progress_enabled) task_deinit(ctx.info); - return ret; + return err; }