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,
}
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,
}
/*
- * 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.
*
static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_key key;
int err = 0;
int ret;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ btrfs_init_path(&path);
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
key.type = BTRFS_INODE_ITEM_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)
goto out;
if (ret > 0) {
err |= INODE_ITEM_MISSING;
}
- err |= check_inode_item(root, path, ext_ref);
+ err |= check_inode_item(root, &path, ext_ref);
err &= ~LAST_ITEM;
if (err && !ret)
ret = -EIO;
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
*/
static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
{
- struct btrfs_path *path;
+ struct btrfs_path path;
struct node_refs nrefs;
struct btrfs_root_item *root_item = &root->root_item;
int ret, wret;
if (ret < 0)
return ret;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
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;
+ 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);
+ path.lowest_level = level;
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0)
goto out;
ret = 0;
}
while (1) {
- wret = walk_down_tree_v2(root, path, &level, &nrefs, ext_ref);
+ wret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
if (wret < 0)
ret = wret;
if (wret != 0)
break;
- wret = walk_up_tree_v2(root, path, &level);
+ wret = walk_up_tree_v2(root, &path, &level);
if (wret < 0)
ret = wret;
if (wret != 0)
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
{
struct btrfs_root *tree_root = fs_info->tree_root;
struct btrfs_root *cur_root = NULL;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_key key;
struct extent_buffer *node;
unsigned int ext_ref;
int ret;
int err = 0;
- ext_ref = btrfs_fs_incompat(fs_info,
- BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF);
-
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ ext_ref = btrfs_fs_incompat(fs_info, EXTENDED_IREF);
+ 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) {
err = ret;
goto out;
}
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;
err |= ret;
}
next:
- ret = btrfs_next_item(tree_root, path);
+ ret = btrfs_next_item(tree_root, &path);
if (ret > 0)
goto out;
if (ret < 0) {
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return err;
}
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",
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;
ret = err;
}
+ if (!ret)
+ fprintf(stderr, "Repaired extent references for %llu\n",
+ (unsigned long long)rec->start);
+
btrfs_release_path(&path);
return ret;
}
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 */
{
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) {
/*
while(1) {
int cur_err = 0;
+ int fix = 0;
- fixed = 0;
- recorded = 0;
cache = search_cache_extent(extent_cache, 0);
if (!cache)
break;
if (rec->num_duplicates) {
fprintf(stderr, "extent item %llu has multiple extent "
"items\n", (unsigned long long)rec->start);
- err = 1;
cur_err = 1;
}
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);
ret = fixup_extent_flags(root->fs_info, rec);
if (ret)
goto repair_abort;
- fixed = 1;
+ fix = 1;
}
- err = 1;
cur_err = 1;
}
/*
fprintf(stderr,
"bad metadata [%llu, %llu) crossing stripe boundary\n",
rec->start, rec->start + rec->max_size);
- err = 1;
cur_err = 1;
}
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,
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)
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;
(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)
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;
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 ||
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(
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);
}
/*
+ * 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.
*/
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;
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;
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)
* 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 "
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;
}
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;
}
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");
* 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");
if (check_mode == CHECK_MODE_LOWMEM)