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)
{
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);
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)
{
}
out:
err &= ~LAST_ITEM;
- /*
- * Convert any error bitmap to -EIO, as we should avoid
- * mixing positive and negative return value to represent
- * error
- */
if (err && !ret)
- ret = -EIO;
+ ret = err;
return ret;
}
* 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;
}
}
- ret = check_child_node(root, cur, path->slots[*level], next);
+ ret = check_child_node(cur, path->slots[*level], next);
if (ret) {
err = ret;
goto out;
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)
{
}
}
- ret = check_child_node(root, cur, path->slots[*level], next);
+ ret = check_child_node(cur, path->slots[*level], next);
if (ret < 0)
break;
}
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;
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;
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++;
((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++;
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++;
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;
}
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 */
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);
struct btrfs_path path;
struct node_refs nrefs;
struct btrfs_root_item *root_item = &root->root_item;
- int ret, wret;
+ int ret;
int level;
+ int err = 0;
/*
* We need to manually check the first inode item(256)
}
while (1) {
- wret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
- if (wret < 0)
- ret = wret;
- if (wret != 0)
+ 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;
+ }
- wret = walk_up_tree_v2(root, &path, &level);
- if (wret < 0)
- ret = wret;
- if (wret != 0)
+ ret = walk_up_tree_v2(root, &path, &level);
+ if (ret != 0) {
+ /* Normal exit, reset ret to err */
+ ret = err;
break;
+ }
}
out:
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;
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;
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)
{
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;
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 "
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");
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;
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);
* 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)
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;
}
} 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;
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;
&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;
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);
}
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);
* 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)
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:
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);
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];
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,
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",
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;
offset)
found_count++;
+next:
ret = btrfs_next_item(root, &path);
if (ret)
break;
}
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);
}
ptr += btrfs_extent_inline_ref_size(type);
- if (ptr < end)
- goto next;
+ goto next;
out:
return err;
/* 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)
/* 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;
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;
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)
{
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) {
ret = repair_root_items(info);
err |= !!ret;
- if (ret < 0)
+ 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;
}
ret = check_space_cache(root);
err |= !!ret;
- if (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
else
ret = check_fs_roots(root, &root_cache);
err |= !!ret;
- if (ret)
+ if (ret) {
+ error("errors found in fs roots");
goto out;
+ }
fprintf(stderr, "checking csums\n");
ret = check_csums(root);
err |= !!ret;
- if (ret)
+ if (ret) {
+ error("errors found in csum tree");
goto out;
+ }
fprintf(stderr, "checking root refs\n");
/* 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)
+ if (ret) {
+ error("errors found in root refs");
goto out;
+ }
}
while (repair && !list_empty(&root->fs_info->recow_ebs)) {
list_del_init(&eb->recow);
ret = recow_extent_buffer(root, eb);
err |= !!ret;
- if (ret)
+ if (ret) {
+ error("fails to fix transid errors");
break;
+ }
}
while (!list_empty(&delete_items)) {
fprintf(stderr, "checking quota groups\n");
ret = qgroup_verify_all(info);
err |= !!ret;
- if (ret)
+ if (ret) {
+ error("failed to check quota groups");
goto out;
+ }
report_qgroups(0);
ret = repair_qgroups(info, &qgroups_repaired);
err |= !!ret;
- if (err)
+ if (err) {
+ error("failed to repair quota groups");
goto out;
+ }
ret = 0;
}
"backup data and re-format the FS. *\n\n");
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);