Merge branch 'pull/qu/v2' into devel from Qu Wenruo (https://github.com/adam900710/btrfs-progs/tree/for_devel)

The branch passes all selftests, except:
- misc/035
  Known bug as the fix is reverted.
- fuzz/003
- fuzz/009
  Not a regression, as stable tags also triggers them.
  BUG_ON() in commit_transaction get triggered due to ENOSPC.
  These two bugs will be addressed soon. but not in this pull.

This pull request include the following features:

Core change:
- check --repair
  * Flush/FUA support to avoid breaking metadata CoW
      Now btrfs-progs crashing or transaction aborted won't cause
      new transid error.

Fixes and Enhancement:
- generic
  * Try best copy when reading tree blocks.
  * Skip unnecessary retry when one tree block copy fails.
  * Avoid back tree block to populate tree block cache.
  * Don't BUG_ON() when failed to flush/write super blocks
- check
  * File extents repair no longer relies data in extent tree.
  * New ability to check and repair free space cache invalid inode mode.
  * Update backup roots when commit transaction.

- Misc
  * fs_info <-> root parameters cleanup for btrfs_check_leaf/node()
master
David Sterba 2019-04-26 18:10:32 +02:00
commit 5ca28fc25f
15 changed files with 967 additions and 434 deletions

View File

@ -460,10 +460,10 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
struct inode_backref *backref;
struct inode_backref *orig;
struct inode_backref *tmp;
struct orphan_data_extent *src_orphan;
struct orphan_data_extent *dst_orphan;
struct mismatch_dir_hash_record *hash_record;
struct mismatch_dir_hash_record *new_record;
struct unaligned_extent_rec_t *src;
struct unaligned_extent_rec_t *dst;
struct rb_node *rb;
size_t size;
int ret;
@ -474,8 +474,8 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
memcpy(rec, orig_rec, sizeof(*rec));
rec->refs = 1;
INIT_LIST_HEAD(&rec->backrefs);
INIT_LIST_HEAD(&rec->orphan_extents);
INIT_LIST_HEAD(&rec->mismatch_dir_hash);
INIT_LIST_HEAD(&rec->unaligned_extent_recs);
rec->holes = RB_ROOT;
list_for_each_entry(orig, &orig_rec->backrefs, list) {
@ -488,15 +488,6 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
memcpy(backref, orig, size);
list_add_tail(&backref->list, &rec->backrefs);
}
list_for_each_entry(src_orphan, &orig_rec->orphan_extents, list) {
dst_orphan = malloc(sizeof(*dst_orphan));
if (!dst_orphan) {
ret = -ENOMEM;
goto cleanup;
}
memcpy(dst_orphan, src_orphan, sizeof(*src_orphan));
list_add_tail(&dst_orphan->list, &rec->orphan_extents);
}
list_for_each_entry(hash_record, &orig_rec->mismatch_dir_hash, list) {
size = sizeof(*hash_record) + hash_record->namelen;
new_record = malloc(size);
@ -507,6 +498,17 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
memcpy(&new_record, hash_record, size);
list_add_tail(&new_record->list, &rec->mismatch_dir_hash);
}
list_for_each_entry(src, &orig_rec->unaligned_extent_recs, list) {
size = sizeof(*src);
dst = malloc(size);
if (!dst) {
ret = -ENOMEM;
goto cleanup;
}
memcpy(dst, src, size);
list_add_tail(&dst->list, &rec->unaligned_extent_recs);
}
ret = copy_file_extent_holes(&rec->holes, &orig_rec->holes);
if (ret < 0)
goto cleanup_rb;
@ -530,11 +532,6 @@ cleanup:
free(orig);
}
if (!list_empty(&rec->orphan_extents))
list_for_each_entry_safe(orig, tmp, &rec->orphan_extents, list) {
list_del(&orig->list);
free(orig);
}
if (!list_empty(&rec->mismatch_dir_hash)) {
list_for_each_entry_safe(hash_record, new_record,
&rec->mismatch_dir_hash, list) {
@ -542,28 +539,18 @@ cleanup:
free(hash_record);
}
}
if (!list_empty(&rec->unaligned_extent_recs))
list_for_each_entry_safe(src, dst, &rec->unaligned_extent_recs,
list) {
list_del(&src->list);
free(src);
}
free(rec);
return ERR_PTR(ret);
}
static void print_orphan_data_extents(struct list_head *orphan_extents,
u64 objectid)
{
struct orphan_data_extent *orphan;
if (list_empty(orphan_extents))
return;
printf("The following data extent is lost in tree %llu:\n",
objectid);
list_for_each_entry(orphan, orphan_extents, list) {
printf("\tinode: %llu, offset:%llu, disk_bytenr: %llu, disk_len: %llu\n",
orphan->objectid, orphan->offset, orphan->disk_bytenr,
orphan->disk_len);
}
}
static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
{
u64 root_objectid = root->root_key.objectid;
@ -610,16 +597,14 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
fprintf(stderr, ", some csum missing");
if (errors & I_ERR_LINK_COUNT_WRONG)
fprintf(stderr, ", link count wrong");
if (errors & I_ERR_FILE_EXTENT_ORPHAN)
fprintf(stderr, ", orphan file extent");
if (errors & I_ERR_ODD_INODE_FLAGS)
fprintf(stderr, ", odd inode flags");
if (errors & I_ERR_INLINE_RAM_BYTES_WRONG)
fprintf(stderr, ", invalid inline ram bytes");
if (errors & I_ERR_INVALID_IMODE)
fprintf(stderr, ", invalid inode mode bit 0%o",
rec->imode & ~07777);
fprintf(stderr, "\n");
/* Print the orphan extents if needed */
if (errors & I_ERR_FILE_EXTENT_ORPHAN)
print_orphan_data_extents(&rec->orphan_extents, root->objectid);
/* Print the holes if needed */
if (errors & I_ERR_FILE_EXTENT_DISCOUNT) {
@ -720,8 +705,8 @@ static struct inode_record *get_inode_rec(struct cache_tree *inode_cache,
rec->extent_start = (u64)-1;
rec->refs = 1;
INIT_LIST_HEAD(&rec->backrefs);
INIT_LIST_HEAD(&rec->orphan_extents);
INIT_LIST_HEAD(&rec->mismatch_dir_hash);
INIT_LIST_HEAD(&rec->unaligned_extent_recs);
rec->holes = RB_ROOT;
node = malloc(sizeof(*node));
@ -743,15 +728,15 @@ static struct inode_record *get_inode_rec(struct cache_tree *inode_cache,
return rec;
}
static void free_orphan_data_extents(struct list_head *orphan_extents)
static void free_unaligned_extent_recs(struct list_head *unaligned_extent_recs)
{
struct orphan_data_extent *orphan;
struct unaligned_extent_rec_t *urec;
while (!list_empty(orphan_extents)) {
orphan = list_entry(orphan_extents->next,
struct orphan_data_extent, list);
list_del(&orphan->list);
free(orphan);
while (!list_empty(unaligned_extent_recs)) {
urec = list_entry(unaligned_extent_recs->next,
struct unaligned_extent_rec_t, list);
list_del(&urec->list);
free(urec);
}
}
@ -771,7 +756,7 @@ static void free_inode_rec(struct inode_record *rec)
}
list_for_each_entry_safe(hash, next, &rec->mismatch_dir_hash, list)
free(hash);
free_orphan_data_extents(&rec->orphan_extents);
free_unaligned_extent_recs(&rec->unaligned_extent_recs);
free_file_extent_holes(&rec->holes);
free(rec);
}
@ -811,6 +796,8 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache,
if (!rec->checked || rec->merging)
return;
if (!is_valid_imode(rec->imode))
rec->errors |= I_ERR_INVALID_IMODE;
if (S_ISDIR(rec->imode)) {
if (rec->found_size != rec->isize)
rec->errors |= I_ERR_DIR_ISIZE_WRONG;
@ -1808,9 +1795,9 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
}
if (btrfs_is_leaf(next))
status = btrfs_check_leaf(root, NULL, next);
status = btrfs_check_leaf(fs_info, NULL, next);
else
status = btrfs_check_node(root, NULL, next);
status = btrfs_check_node(fs_info, NULL, next);
if (status != BTRFS_TREE_BLOCK_CLEAN) {
free_extent_buffer(next);
err = -EIO;
@ -2488,9 +2475,6 @@ static int repair_inode_no_item(struct btrfs_trans_handle *trans,
} else if (rec->found_dir_item) {
type_recovered = 1;
filetype = BTRFS_FT_DIR;
} else if (!list_empty(&rec->orphan_extents)) {
type_recovered = 1;
filetype = BTRFS_FT_REG_FILE;
} else{
printf("Can't determine the filetype for inode %llu, assume it is a normal file\n",
rec->ino);
@ -2521,67 +2505,6 @@ out:
return ret;
}
static int repair_inode_orphan_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct inode_record *rec)
{
struct orphan_data_extent *orphan;
struct orphan_data_extent *tmp;
int ret = 0;
list_for_each_entry_safe(orphan, tmp, &rec->orphan_extents, list) {
/*
* Check for conflicting file extents
*
* Here we don't know whether the extents is compressed or not,
* so we can only assume it not compressed nor data offset,
* and use its disk_len as extent length.
*/
ret = btrfs_get_extent(NULL, root, path, orphan->objectid,
orphan->offset, orphan->disk_len, 0);
btrfs_release_path(path);
if (ret < 0)
goto out;
if (!ret) {
fprintf(stderr,
"orphan extent (%llu, %llu) conflicts, delete the orphan\n",
orphan->disk_bytenr, orphan->disk_len);
ret = btrfs_free_extent(trans,
root->fs_info->extent_root,
orphan->disk_bytenr, orphan->disk_len,
0, root->objectid, orphan->objectid,
orphan->offset);
if (ret < 0)
goto out;
}
ret = btrfs_insert_file_extent(trans, root, orphan->objectid,
orphan->offset, orphan->disk_bytenr,
orphan->disk_len, orphan->disk_len);
if (ret < 0)
goto out;
/* Update file size info */
rec->found_size += orphan->disk_len;
if (rec->found_size == rec->nbytes)
rec->errors &= ~I_ERR_FILE_NBYTES_WRONG;
/* Update the file extent hole info too */
ret = del_file_extent_hole(&rec->holes, orphan->offset,
orphan->disk_len);
if (ret < 0)
goto out;
if (RB_EMPTY_ROOT(&rec->holes))
rec->errors &= ~I_ERR_FILE_EXTENT_DISCOUNT;
list_del(&orphan->list);
free(orphan);
}
rec->errors &= ~I_ERR_FILE_EXTENT_ORPHAN;
out:
return ret;
}
static int repair_inode_discount_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
@ -2693,21 +2616,178 @@ static int repair_mismatch_dir_hash(struct btrfs_trans_handle *trans,
return ret;
}
static int btrfs_delete_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_key *key)
{
struct btrfs_path path;
int ret = 0;
btrfs_init_path(&path);
ret = btrfs_search_slot(trans, root, key, &path, -1, 1);
if (ret) {
if (ret > 0)
ret = -ENOENT;
btrfs_release_path(&path);
return ret;
}
ret = btrfs_del_item(trans, root, &path);
btrfs_release_path(&path);
return ret;
}
static int find_file_extent_offset_by_bytenr(struct btrfs_root *root,
u64 owner, u64 bytenr, u64 *offset_ret)
{
int ret = 0;
struct btrfs_path path;
struct btrfs_key key;
struct btrfs_key found_key;
struct btrfs_file_extent_item *fi;
struct extent_buffer *leaf;
u64 disk_bytenr;
int slot;
btrfs_init_path(&path);
key.objectid = owner;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret) {
if (ret > 0)
ret = -ENOENT;
btrfs_release_path(&path);
return ret;
}
btrfs_release_path(&path);
key.objectid = owner;
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = 0;
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0) {
btrfs_release_path(&path);
return ret;
}
while (1) {
leaf = path.nodes[0];
slot = path.slots[0];
if (slot >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(root, &path);
if (ret) {
if (ret > 0)
ret = 0;
break;
}
leaf = path.nodes[0];
slot = path.slots[0];
}
btrfs_item_key_to_cpu(leaf, &found_key, slot);
if ((found_key.objectid != owner) ||
(found_key.type != BTRFS_EXTENT_DATA_KEY))
break;
fi = btrfs_item_ptr(leaf, slot,
struct btrfs_file_extent_item);
disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
if (disk_bytenr == bytenr) {
*offset_ret = found_key.offset;
ret = 0;
break;
}
path.slots[0]++;
}
btrfs_release_path(&path);
return ret;
}
static int repair_unaligned_extent_recs(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct inode_record *rec)
{
int ret = 0;
struct btrfs_key key;
struct unaligned_extent_rec_t *urec;
struct unaligned_extent_rec_t *tmp;
list_for_each_entry_safe(urec, tmp, &rec->unaligned_extent_recs, list) {
key.objectid = urec->owner;
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = urec->offset;
fprintf(stderr, "delete file extent item [%llu,%llu]\n",
urec->owner, urec->offset);
ret = btrfs_delete_item(trans, root, &key);
if (ret)
return ret;
list_del(&urec->list);
free(urec);
}
rec->errors &= ~I_ERR_UNALIGNED_EXTENT_REC;
return ret;
}
static int repair_imode_original(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct inode_record *rec)
{
int ret;
u32 imode;
if (root->root_key.objectid != BTRFS_ROOT_TREE_OBJECTID)
return -ENOTTY;
if (rec->ino != BTRFS_ROOT_TREE_DIR_OBJECTID || !is_fstree(rec->ino))
return -ENOTTY;
if (rec->ino == BTRFS_ROOT_TREE_DIR_OBJECTID)
imode = 040755;
else
imode = 0100600;
ret = reset_imode(trans, root, path, rec->ino, imode);
if (ret < 0)
return ret;
rec->errors &= ~I_ERR_INVALID_IMODE;
return ret;
}
static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
{
struct btrfs_trans_handle *trans;
struct btrfs_path path;
int ret = 0;
/* unaligned extent recs always lead to csum missing error, clean it */
if ((rec->errors & I_ERR_SOME_CSUM_MISSING) &&
(rec->errors & I_ERR_UNALIGNED_EXTENT_REC))
rec->errors &= ~I_ERR_SOME_CSUM_MISSING;
if (!(rec->errors & (I_ERR_DIR_ISIZE_WRONG |
I_ERR_NO_ORPHAN_ITEM |
I_ERR_LINK_COUNT_WRONG |
I_ERR_NO_INODE_ITEM |
I_ERR_FILE_EXTENT_ORPHAN |
I_ERR_FILE_EXTENT_DISCOUNT |
I_ERR_FILE_NBYTES_WRONG |
I_ERR_INLINE_RAM_BYTES_WRONG |
I_ERR_MISMATCH_DIR_HASH)))
I_ERR_MISMATCH_DIR_HASH |
I_ERR_UNALIGNED_EXTENT_REC)))
return rec->errors;
/*
@ -2726,8 +2806,6 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
ret = repair_mismatch_dir_hash(trans, root, rec);
if (rec->errors & I_ERR_NO_INODE_ITEM)
ret = repair_inode_no_item(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_FILE_EXTENT_ORPHAN)
ret = repair_inode_orphan_extent(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_FILE_EXTENT_DISCOUNT)
ret = repair_inode_discount_extent(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_DIR_ISIZE_WRONG)
@ -2740,6 +2818,10 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
ret = repair_inode_nbytes(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_INLINE_RAM_BYTES_WRONG)
ret = repair_inline_ram_bytes(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_UNALIGNED_EXTENT_REC)
ret = repair_unaligned_extent_recs(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_INVALID_IMODE)
ret = repair_imode_original(trans, root, &path, rec);
btrfs_commit_transaction(trans, root);
btrfs_release_path(&path);
return ret;
@ -3356,10 +3438,10 @@ static int check_fs_root(struct btrfs_root *root,
struct root_record *rec;
struct btrfs_root_item *root_item = &root->root_item;
struct cache_tree corrupt_blocks;
struct orphan_data_extent *orphan;
struct orphan_data_extent *tmp;
enum btrfs_tree_block_status status;
struct node_refs nrefs;
struct unaligned_extent_rec_t *urec;
struct unaligned_extent_rec_t *tmp;
/*
* Reuse the corrupt_block cache tree to record corrupted tree block
@ -3383,16 +3465,26 @@ static int check_fs_root(struct btrfs_root *root,
cache_tree_init(&root_node.inode_cache);
memset(&nrefs, 0, sizeof(nrefs));
/* Move the orphan extent record to corresponding inode_record */
list_for_each_entry_safe(orphan, tmp,
&root->orphan_data_extents, list) {
/* Mode unaligned extent recs to corresponding inode record */
list_for_each_entry_safe(urec, tmp,
&root->unaligned_extent_recs, list) {
struct inode_record *inode;
inode = get_inode_rec(&root_node.inode_cache, orphan->objectid,
1);
BUG_ON(IS_ERR(inode));
inode->errors |= I_ERR_FILE_EXTENT_ORPHAN;
list_move(&orphan->list, &inode->orphan_extents);
inode = get_inode_rec(&root_node.inode_cache, urec->owner, 1);
if (IS_ERR_OR_NULL(inode)) {
fprintf(stderr,
"fail to get inode rec on [%llu,%llu]\n",
urec->objectid, urec->owner);
list_del(&urec->list);
free(urec);
continue;
}
inode->errors |= I_ERR_UNALIGNED_EXTENT_REC;
list_move(&urec->list, &inode->unaligned_extent_recs);
}
level = btrfs_header_level(root->node);
@ -3403,9 +3495,9 @@ static int check_fs_root(struct btrfs_root *root,
/* We may not have checked the root block, lets do that now */
if (btrfs_is_leaf(root->node))
status = btrfs_check_leaf(root, NULL, root->node);
status = btrfs_check_leaf(root->fs_info, NULL, root->node);
else
status = btrfs_check_node(root, NULL, root->node);
status = btrfs_check_node(root->fs_info, NULL, root->node);
if (status != BTRFS_TREE_BLOCK_CLEAN)
return -EIO;
@ -3499,7 +3591,6 @@ skip_walking:
free_corrupt_blocks_tree(&corrupt_blocks);
root->fs_info->corrupt_blocks = NULL;
free_orphan_data_extents(&root->orphan_data_extents);
return ret;
}
@ -3595,6 +3686,17 @@ again:
key.type == BTRFS_ROOT_BACKREF_KEY) {
process_root_ref(leaf, path.slots[0], &key,
root_cache);
} else if (key.type == BTRFS_INODE_ITEM_KEY &&
is_fstree(key.objectid)) {
ret = check_repair_free_space_inode(fs_info, &path);
if (ret < 0 && !path.nodes[0]) {
err = 1;
goto out;
}
if (ret < 0 && path.nodes[0]) {
err = 1;
goto next;
}
}
next:
path.slots[0]++;
@ -4244,9 +4346,9 @@ static int check_block(struct btrfs_root *root,
rec->info_level = level;
if (btrfs_is_leaf(buf))
status = btrfs_check_leaf(root, &rec->parent_key, buf);
status = btrfs_check_leaf(root->fs_info, &rec->parent_key, buf);
else
status = btrfs_check_node(root, &rec->parent_key, buf);
status = btrfs_check_node(root->fs_info, &rec->parent_key, buf);
if (status != BTRFS_TREE_BLOCK_CLEAN) {
if (repair)
@ -7266,6 +7368,89 @@ out:
return ret ? ret : nr_del;
}
/*
* Based extent backref item, we find all file extent items in the fs tree. By
* the info we can rebuild the extent backref item
*/
static int __find_possible_backrefs(struct btrfs_root *root,
u64 owner, u64 offset, u64 bytenr, u64 *refs_ret,
u64 *bytes_ret)
{
int ret = 0;
struct btrfs_path path;
struct btrfs_key key;
struct btrfs_key found_key;
struct btrfs_file_extent_item *fi;
struct extent_buffer *leaf;
u64 backref_offset, disk_bytenr;
int slot;
btrfs_init_path(&path);
key.objectid = owner;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret > 0)
ret = -ENOENT;
if (ret) {
btrfs_release_path(&path);
return ret;
}
btrfs_release_path(&path);
key.objectid = owner;
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = 0;
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0) {
btrfs_release_path(&path);
return ret;
}
while (1) {
leaf = path.nodes[0];
slot = path.slots[0];
if (slot >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(root, &path);
if (ret) {
if (ret > 0)
ret = 0;
break;
}
leaf = path.nodes[0];
slot = path.slots[0];
}
btrfs_item_key_to_cpu(leaf, &found_key, slot);
if ((found_key.objectid != owner) ||
(found_key.type != BTRFS_EXTENT_DATA_KEY))
break;
fi = btrfs_item_ptr(leaf, slot,
struct btrfs_file_extent_item);
backref_offset = found_key.offset -
btrfs_file_extent_offset(leaf, fi);
disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
*bytes_ret = btrfs_file_extent_disk_num_bytes(leaf,
fi);
if ((disk_bytenr == bytenr) &&
(backref_offset == offset)) {
(*refs_ret)++;
}
path.slots[0]++;
}
btrfs_release_path(&path);
return ret;
}
static int find_possible_backrefs(struct btrfs_fs_info *info,
struct btrfs_path *path,
struct cache_tree *extent_cache,
@ -7275,9 +7460,9 @@ static int find_possible_backrefs(struct btrfs_fs_info *info,
struct extent_backref *back, *tmp;
struct data_backref *dback;
struct cache_extent *cache;
struct btrfs_file_extent_item *fi;
struct btrfs_key key;
u64 bytenr, bytes;
u64 refs;
int ret;
rbtree_postorder_for_each_entry_safe(back, tmp,
@ -7305,24 +7490,15 @@ static int find_possible_backrefs(struct btrfs_fs_info *info,
if (IS_ERR(root))
return PTR_ERR(root);
key.objectid = dback->owner;
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = dback->offset;
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret) {
btrfs_release_path(path);
if (ret < 0)
return ret;
/* Didn't find it, we can carry on */
ret = 0;
refs = 0;
bytes = 0;
ret = __find_possible_backrefs(root, dback->owner,
dback->offset, rec->start, &refs, &bytes);
if (ret)
continue;
}
fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_file_extent_item);
bytenr = btrfs_file_extent_disk_bytenr(path->nodes[0], fi);
bytes = btrfs_file_extent_disk_num_bytes(path->nodes[0], fi);
btrfs_release_path(path);
bytenr = rec->start;
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (cache) {
struct extent_record *tmp;
@ -7341,7 +7517,7 @@ static int find_possible_backrefs(struct btrfs_fs_info *info,
continue;
}
dback->found_ref += 1;
dback->found_ref += refs;
dback->disk_bytenr = bytenr;
dback->bytes = bytes;
@ -7355,88 +7531,6 @@ static int find_possible_backrefs(struct btrfs_fs_info *info,
return 0;
}
/*
* Record orphan data ref into corresponding root.
*
* Return 0 if the extent item contains data ref and recorded.
* Return 1 if the extent item contains no useful data ref
* On that case, it may contains only shared_dataref or metadata backref
* or the file extent exists(this should be handled by the extent bytenr
* recovery routine)
* Return <0 if something goes wrong.
*/
static int record_orphan_data_extents(struct btrfs_fs_info *fs_info,
struct extent_record *rec)
{
struct btrfs_key key;
struct btrfs_root *dest_root;
struct extent_backref *back, *tmp;
struct data_backref *dback;
struct orphan_data_extent *orphan;
struct btrfs_path path;
int recorded_data_ref = 0;
int ret = 0;
if (rec->metadata)
return 1;
btrfs_init_path(&path);
rbtree_postorder_for_each_entry_safe(back, tmp,
&rec->backref_tree, node) {
if (back->full_backref || !back->is_data ||
!back->found_extent_tree)
continue;
dback = to_data_backref(back);
if (dback->found_ref)
continue;
key.objectid = dback->root;
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1;
dest_root = btrfs_read_fs_root(fs_info, &key);
/* For non-exist root we just skip it */
if (IS_ERR(dest_root) || !dest_root)
continue;
key.objectid = dback->owner;
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = dback->offset;
ret = btrfs_search_slot(NULL, dest_root, &key, &path, 0, 0);
btrfs_release_path(&path);
/*
* For ret < 0, it's OK since the fs-tree may be corrupted,
* we need to record it for inode/file extent rebuild.
* For ret > 0, we record it only for file extent rebuild.
* For ret == 0, the file extent exists but only bytenr
* mismatch, let the original bytenr fix routine to handle,
* don't record it.
*/
if (ret == 0)
continue;
ret = 0;
orphan = malloc(sizeof(*orphan));
if (!orphan) {
ret = -ENOMEM;
goto out;
}
INIT_LIST_HEAD(&orphan->list);
orphan->root = dback->root;
orphan->objectid = dback->owner;
orphan->offset = dback->offset;
orphan->disk_bytenr = rec->cache.start;
orphan->disk_len = rec->cache.size;
list_add(&dest_root->orphan_data_extents, &orphan->list);
recorded_data_ref = 1;
}
out:
btrfs_release_path(&path);
if (!ret)
return !recorded_data_ref;
else
return ret;
}
/*
* when an incorrect extent item is found, this will delete
* all of the existing entries for it and recreate them
@ -7684,6 +7778,66 @@ static int prune_corrupt_blocks(struct btrfs_fs_info *info)
return 0;
}
static int record_unaligned_extent_rec(struct btrfs_fs_info *fs_info,
struct extent_record *rec)
{
struct extent_backref *back, *tmp;
struct data_backref *dback;
struct btrfs_root *dest_root;
struct btrfs_key key;
struct unaligned_extent_rec_t *urec;
LIST_HEAD(entries);
int ret = 0;
fprintf(stderr, "record unaligned extent record on %llu %llu\n",
rec->start, rec->nr);
/*
* Metadata is easy and the backrefs should always agree on bytenr and
* size, if not we've got bigger issues.
*/
if (rec->metadata)
return 0;
rbtree_postorder_for_each_entry_safe(back, tmp,
&rec->backref_tree, node) {
if (back->full_backref || !back->is_data)
continue;
dback = to_data_backref(back);
key.objectid = dback->root;
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1;
dest_root = btrfs_read_fs_root(fs_info, &key);
/* For non-exist root we just skip it */
if (IS_ERR_OR_NULL(dest_root))
continue;
urec = malloc(sizeof(struct unaligned_extent_rec_t));
if (!urec)
return -ENOMEM;
INIT_LIST_HEAD(&urec->list);
urec->objectid = dest_root->objectid;
urec->owner = dback->owner;
urec->offset = 0;
urec->bytenr = rec->start;
ret = find_file_extent_offset_by_bytenr(dest_root,
dback->owner, rec->start, &urec->offset);
if (ret) {
free(urec);
return ret;
}
list_add(&urec->list, &dest_root->unaligned_extent_recs);
}
return ret;
}
static int check_extent_refs(struct btrfs_root *root,
struct cache_tree *extent_cache)
{
@ -7778,12 +7932,22 @@ static int check_extent_refs(struct btrfs_root *root,
fprintf(stderr, "extent item %llu, found %llu\n",
(unsigned long long)rec->extent_item_refs,
(unsigned long long)rec->refs);
ret = record_orphan_data_extents(root->fs_info, rec);
if (ret < 0)
goto repair_abort;
fix = ret;
fix = 1;
cur_err = 1;
}
if (!IS_ALIGNED(rec->start, root->fs_info->sectorsize)) {
fprintf(stderr, "unaligned extent rec on [%llu %llu]\n",
(unsigned long long)rec->start,
(unsigned long long)rec->nr);
ret = record_unaligned_extent_rec(root->fs_info, rec);
if (ret)
goto repair_abort;
/* No need to check backref */
goto next;
}
if (all_backpointers_checked(rec, 1)) {
fprintf(stderr, "backpointer mismatch on [%llu %llu]\n",
(unsigned long long)rec->start,
@ -7836,7 +8000,7 @@ static int check_extent_refs(struct btrfs_root *root,
rec->start, rec->start + rec->max_size);
cur_err = 1;
}
next:
err = cur_err;
remove_cache_extent(extent_cache, cache);
free_all_extent_backrefs(rec);

View File

@ -21,6 +21,7 @@
#include "transaction.h"
#include "utils.h"
#include "disk-io.h"
#include "repair.h"
#include "check/mode-common.h"
/*
@ -795,3 +796,131 @@ out:
btrfs_release_path(&path);
return ret;
}
/*
* Reset the mode of inode (specified by @root and @ino) to @mode.
*
* Caller should ensure @path is not populated, the @path is mainly for caller
* to grab the correct new path of the inode.
*
* Return 0 if repair is done, @path will point to the correct inode item.
* Return <0 for errors.
*/
int reset_imode(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_path *path, u64 ino, u32 mode)
{
struct btrfs_inode_item *iitem;
struct extent_buffer *leaf;
struct btrfs_key key;
int slot;
int ret;
key.objectid = ino;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
if (ret > 0)
ret = -ENOENT;
if (ret < 0) {
errno = -ret;
error("failed to search tree %llu: %m",
root->root_key.objectid);
return ret;
}
leaf = path->nodes[0];
slot = path->slots[0];
iitem = btrfs_item_ptr(leaf, slot, struct btrfs_inode_item);
btrfs_set_inode_mode(leaf, iitem, mode);
btrfs_mark_buffer_dirty(leaf);
return ret;
}
/*
* Reset the inode mode of the inode specified by @path.
*
* Caller should ensure the @path is pointing to an INODE_ITEM and root is tree
* root. Repair imode for other trees is not supported yet.
*
* Return 0 if repair is successful.
* Return <0 if error happens.
*/
int repair_imode_common(struct btrfs_root *root, struct btrfs_path *path)
{
struct btrfs_trans_handle *trans;
struct btrfs_key key;
u32 imode;
int ret;
if (root->root_key.objectid != BTRFS_ROOT_TREE_OBJECTID) {
error(
"repair inode mode outside of root tree is not supported yet");
return -ENOTTY;
}
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
ASSERT(key.type == BTRFS_INODE_ITEM_KEY);
if (key.objectid != BTRFS_ROOT_TREE_DIR_OBJECTID &&
!is_fstree(key.objectid)) {
error("unsupported ino %llu", key.objectid);
return -ENOTTY;
}
if (key.objectid == BTRFS_ROOT_TREE_DIR_OBJECTID)
imode = 040755;
else
imode = 0100600;
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
errno = -ret;
error("failed to start transaction: %m");
return ret;
}
btrfs_release_path(path);
ret = reset_imode(trans, root, path, key.objectid, imode);
if (ret < 0)
goto abort;
ret = btrfs_commit_transaction(trans, root);
if (!ret)
printf("reset mode for inode %llu root %llu\n",
key.objectid, root->root_key.objectid);
return ret;
abort:
btrfs_abort_transaction(trans, ret);
return ret;
}
/*
* For free space inodes, we can't call check_inode_item() as free space
* cache inode doesn't have INODE_REF.
* We just check its inode mode.
*/
int check_repair_free_space_inode(struct btrfs_fs_info *fs_info,
struct btrfs_path *path)
{
struct btrfs_inode_item *iitem;
struct btrfs_key key;
u32 mode;
int ret = 0;
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
ASSERT(key.type == BTRFS_INODE_ITEM_KEY && is_fstree(key.objectid));
iitem = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_inode_item);
mode = btrfs_inode_mode(path->nodes[0], iitem);
if (mode != FREE_SPACE_CACHE_INODE_MODE) {
error(
"free space cache inode %llu has invalid mode: has 0%o expect 0%o",
key.objectid, mode, FREE_SPACE_CACHE_INODE_MODE);
ret = -EUCLEAN;
if (repair) {
ret = repair_imode_common(fs_info->tree_root,
path);
if (ret < 0)
return ret;
return ret;
}
}
return ret;
}

View File

@ -24,6 +24,7 @@
#include <sys/stat.h>
#include "ctree.h"
#define FREE_SPACE_CACHE_INODE_MODE (0100600)
/*
* Use for tree walk to walk through trees whose leaves/nodes can be shared
* between different trees. (Namely subvolume/fs trees)
@ -125,5 +126,33 @@ int delete_corrupted_dir_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_key *di_key, char *namebuf,
u32 namelen);
int reset_imode(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_path *path, u64 ino, u32 mode);
int repair_imode_common(struct btrfs_root *root, struct btrfs_path *path);
int check_repair_free_space_inode(struct btrfs_fs_info *fs_info,
struct btrfs_path *path);
/*
* Check if the inode mode @imode is valid
*
* This check focuses on S_FTMT bits and unused bits.
* Sticky/setuid/setgid and regular owner/group/other bits won't cause
* any problem.
*/
static inline bool is_valid_imode(u32 imode)
{
if (imode & ~(S_IFMT | 07777))
return false;
/*
* S_IFMT is not bitmap, nor pure numbering sequence. Need per valid
* number check.
*/
imode &= S_IFMT;
if (imode != S_IFDIR && imode != S_IFCHR && imode != S_IFBLK &&
imode != S_IFREG && imode != S_IFIFO && imode != S_IFLNK &&
imode != S_IFSOCK)
return false;
return true;
}
#endif

View File

@ -543,6 +543,54 @@ static int end_avoid_extents_overwrite(struct btrfs_fs_info *fs_info)
return ret;
}
/*
* Delete the item @path point to. A wrapper of btrfs_del_item().
*
* If deleted successfully, @path will point to the previous item of the
* deleted item.
*/
static int delete_item(struct btrfs_root *root, struct btrfs_path *path)
{
struct btrfs_key key;
struct btrfs_trans_handle *trans;
int ret = 0;
ret = avoid_extents_overwrite(root->fs_info);
if (ret)
return ret;
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
error("fail to start transaction %s", strerror(-ret));
goto out;
}
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
btrfs_release_path(path);
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret) {
ret = -ENOENT;
goto out;
}
ret = btrfs_del_item(trans, root, path);
if (ret)
goto out;
if (path->slots[0] == 0)
btrfs_prev_leaf(root, path);
else
path->slots[0]--;
out:
btrfs_commit_transaction(trans, root);
if (ret)
error("failed to delete root %llu item[%llu, %u, %llu]",
root->objectid, key.objectid, key.type, key.offset);
else
printf("Deleted root %llu item[%llu, %u, %llu]\n",
root->objectid, key.objectid, key.type, key.offset);
return ret;
}
/*
* Wrapper function for btrfs_fix_block_accounting().
*
@ -1756,28 +1804,37 @@ out:
/*
* Wrapper function of btrfs_punch_hole.
*
* @path: The path holder, will point to the same key after hole punching.
*
* Returns 0 means success.
* Returns not 0 means error.
*/
static int punch_extent_hole(struct btrfs_root *root, u64 ino, u64 start,
u64 len)
static int punch_extent_hole(struct btrfs_root *root, struct btrfs_path *path,
u64 ino, u64 start, u64 len)
{
struct btrfs_trans_handle *trans;
int ret = 0;
struct btrfs_key key;
int ret;
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans))
return PTR_ERR(trans);
ret = btrfs_punch_hole(trans, root, ino, start, len);
if (ret)
if (ret) {
error("failed to add hole [%llu, %llu] in inode [%llu]",
start, len, ino);
else
printf("Add a hole [%llu, %llu] in inode [%llu]\n", start, len,
ino);
btrfs_abort_transaction(trans, ret);
return ret;
}
printf("Add a hole [%llu, %llu] in inode [%llu]\n", start, len, ino);
btrfs_commit_transaction(trans, root);
btrfs_release_path(path);
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret > 0)
ret = -ENOENT;
return ret;
}
@ -2028,7 +2085,7 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_path *path,
/* Check EXTENT_DATA hole */
if (!no_holes && *end != fkey.offset) {
if (repair)
ret = punch_extent_hole(root, fkey.objectid,
ret = punch_extent_hole(root, path, fkey.objectid,
*end, fkey.offset - *end);
if (!repair || ret) {
err |= FILE_EXTENT_ERROR;
@ -2039,7 +2096,7 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_path *path,
}
}
*end += extent_num_bytes;
*end = fkey.offset + extent_num_bytes;
if (!is_hole)
*size += extent_num_bytes;
@ -2452,6 +2509,17 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path)
nlink = btrfs_inode_nlink(node, ii);
nodatasum = btrfs_inode_flags(node, ii) & BTRFS_INODE_NODATASUM;
if (!is_valid_imode(mode)) {
error("invalid imode mode bits: 0%o", mode);
if (repair) {
ret = repair_imode_common(root, path);
if (ret < 0)
err |= INODE_MODE_ERROR;
} else {
err |= INODE_MODE_ERROR;
}
}
if (S_ISLNK(mode) &&
flags & (BTRFS_INODE_IMMUTABLE | BTRFS_INODE_APPEND)) {
err |= INODE_FLAGS_ERROR;
@ -2597,22 +2665,13 @@ out:
}
}
if (!nbytes && !no_holes && extent_end < isize) {
if (repair)
ret = punch_extent_hole(root, inode_id,
extent_end, isize - extent_end);
if (!repair || ret) {
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) {
if (repair)
if (repair) {
ret = repair_inode_nbytes_lowmem(root, path,
inode_id, extent_size);
if (!ret)
nbytes = extent_size;
}
if (!repair || ret) {
err |= NBYTES_ERROR;
error(
@ -2621,6 +2680,18 @@ out:
extent_size);
}
}
if (!nbytes && !no_holes && extent_end < isize) {
if (repair)
ret = punch_extent_hole(root, path, inode_id,
extent_end, isize - extent_end);
if (!repair || ret) {
err |= NBYTES_ERROR;
error(
"root %llu INODE[%llu] size %llu should have a file extent hole",
root->objectid, inode_id, isize);
}
}
}
if (err & LAST_ITEM)
@ -2977,6 +3048,7 @@ out:
}
/*
* If @err contains BYTES_UNALIGNED then delete the extent data item.
* If @err contains BACKREF_MISSING then add extent of the
* file_extent_data_item.
*
@ -3028,6 +3100,13 @@ static int repair_extent_data_item(struct btrfs_root *root,
else
parent = 0;
if (err & BYTES_UNALIGNED) {
ret = delete_item(root, pathp);
if (!ret)
err = 0;
goto out;
}
/* now repair only adds backref */
if ((err & BACKREF_MISSING) == 0)
return err;
@ -3155,7 +3234,14 @@ static int check_extent_data_item(struct btrfs_root *root,
extent_num_bytes = btrfs_file_extent_num_bytes(eb, fi);
offset = btrfs_file_extent_offset(eb, fi);
/* Check unaligned disk_num_bytes and num_bytes */
/* Check unaligned disk_bytenr, disk_num_bytes and num_bytes */
if (!IS_ALIGNED(disk_bytenr, root->fs_info->sectorsize)) {
error(
"file extent [%llu, %llu] has unaligned disk bytenr: %llu, should be aligned to %u",
fi_key.objectid, fi_key.offset, disk_bytenr,
root->fs_info->sectorsize);
err |= BYTES_UNALIGNED;
}
if (!IS_ALIGNED(disk_num_bytes, root->fs_info->sectorsize)) {
error(
"file extent [%llu, %llu] has unaligned disk num bytes: %llu, should be aligned to %u",
@ -3838,62 +3924,70 @@ out:
}
/*
* Only delete backref if REFERENCER_MISSING now
* Only delete backref if REFERENCER_MISSING or REFERENCER_MISMATCH.
*
* Returns <0 the extent was deleted
* Returns >0 the backref was deleted but extent still exists, returned value
* means error after repair
* Returns 0 nothing happened
* Returns <0 error
* Returns >0 the backref was deleted but extent still exists
* Returns =0 the whole extent item was deleted
*/
static int repair_extent_item(struct btrfs_root *root, struct btrfs_path *path,
u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid,
u64 owner, u64 offset, int err)
u64 owner, u64 offset)
{
struct btrfs_trans_handle *trans;
struct btrfs_root *extent_root = root->fs_info->extent_root;
struct btrfs_key old_key;
int freed = 0;
int ret;
btrfs_item_key_to_cpu(path->nodes[0], &old_key, path->slots[0]);
if ((err & (REFERENCER_MISSING | REFERENCER_MISMATCH)) == 0)
return err;
ret = avoid_extents_overwrite(root->fs_info);
if (ret)
return err;
return ret;
trans = btrfs_start_transaction(extent_root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
errno = -ret;
error("fail to start transaction: %m");
/* nothing happened */
ret = 0;
goto out;
}
/* delete the backref */
ret = btrfs_free_extent(trans, root->fs_info->fs_root, bytenr,
num_bytes, parent, root_objectid, owner, offset);
if (!ret) {
freed = 1;
err &= ~REFERENCER_MISSING;
if (!ret)
printf("Delete backref in extent [%llu %llu]\n",
bytenr, num_bytes);
} else {
else {
error("fail to delete backref in extent [%llu %llu]",
bytenr, num_bytes);
btrfs_abort_transaction(trans, ret);
goto out;
}
btrfs_commit_transaction(trans, extent_root);
/* btrfs_free_extent may delete the extent */
btrfs_release_path(path);
ret = btrfs_search_slot(NULL, root, &old_key, path, 0, 0);
if (ret)
ret = -ENOENT;
else if (freed)
ret = err;
if (ret > 0) {
/* odd, there must be one block group before at least */
if (path->slots[0] == 0) {
ret = -EUCLEAN;
goto out;
}
/*
* btrfs_free_extent() has deleted the extent item,
* let path point to last checked item.
*/
if (path->slots[0] >= btrfs_header_nritems(path->nodes[0]))
path->slots[0] = btrfs_header_nritems(path->nodes[0]) - 1;
else
path->slots[0]--;
ret = 0;
} else if (ret == 0) {
ret = 1;
}
out:
return ret;
}
@ -3911,7 +4005,6 @@ static int check_extent_item(struct btrfs_fs_info *fs_info,
struct btrfs_extent_inline_ref *iref;
struct btrfs_extent_data_ref *dref;
struct extent_buffer *eb = path->nodes[0];
unsigned long end;
unsigned long ptr;
int slot = path->slots[0];
int type;
@ -3929,6 +4022,8 @@ static int check_extent_item(struct btrfs_fs_info *fs_info,
struct btrfs_key key;
int ret;
int err = 0;
int tmp_err;
u32 ptr_offset;
btrfs_item_key_to_cpu(eb, &key, slot);
if (key.type == BTRFS_EXTENT_ITEM_KEY) {
@ -3974,21 +4069,22 @@ static int check_extent_item(struct btrfs_fs_info *fs_info,
/* New METADATA_ITEM */
level = key.offset;
}
end = (unsigned long)ei + item_size;
ptr_offset = ptr - (unsigned long)ei;
next:
/* Reached extent item end normally */
if (ptr == end)
if (ptr_offset == item_size)
goto out;
/* Beyond extent item end, wrong item size */
if (ptr > end) {
if (ptr_offset > item_size) {
err |= ITEM_SIZE_MISMATCH;
error("extent item at bytenr %llu slot %d has wrong size",
eb->start, slot);
goto out;
}
ptr = (unsigned long)ei + ptr_offset;
parent = 0;
root_objectid = 0;
owner = 0;
@ -4001,52 +4097,68 @@ next:
case BTRFS_TREE_BLOCK_REF_KEY:
root_objectid = offset;
owner = level;
ret = check_tree_block_backref(fs_info, offset, key.objectid,
level);
err |= ret;
tmp_err = check_tree_block_backref(fs_info, offset,
key.objectid, level);
break;
case BTRFS_SHARED_BLOCK_REF_KEY:
parent = offset;
ret = check_shared_block_backref(fs_info, offset, key.objectid,
level);
err |= ret;
tmp_err = check_shared_block_backref(fs_info, offset,
key.objectid, level);
break;
case BTRFS_EXTENT_DATA_REF_KEY:
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
root_objectid = btrfs_extent_data_ref_root(eb, dref);
owner = btrfs_extent_data_ref_objectid(eb, dref);
owner_offset = btrfs_extent_data_ref_offset(eb, dref);
ret = check_extent_data_backref(fs_info, root_objectid, owner,
owner_offset, key.objectid, key.offset,
btrfs_extent_data_ref_count(eb, dref));
err |= ret;
tmp_err = check_extent_data_backref(fs_info, root_objectid,
owner, owner_offset, key.objectid, key.offset,
btrfs_extent_data_ref_count(eb, dref));
break;
case BTRFS_SHARED_DATA_REF_KEY:
parent = offset;
ret = check_shared_data_backref(fs_info, offset, key.objectid);
err |= ret;
tmp_err = check_shared_data_backref(fs_info, offset,
key.objectid);
break;
default:
error("extent[%llu %d %llu] has unknown ref type: %d",
key.objectid, key.type, key.offset, type);
ret = UNKNOWN_TYPE;
err |= ret;
err |= UNKNOWN_TYPE;
goto out;
}
if (err && repair) {
if ((tmp_err & (REFERENCER_MISSING | REFERENCER_MISMATCH))
&& repair) {
ret = repair_extent_item(fs_info->extent_root, path,
key.objectid, num_bytes, parent, root_objectid,
owner, owner_offset, ret);
if (ret < 0)
owner, owner_offset);
if (ret < 0) {
err |= tmp_err;
err |= FATAL_ERROR;
goto out;
if (ret) {
} else if (ret == 0) {
err = 0;
goto out;
} else if (ret > 0) {
/*
* The error has been repaired which means the
* extent item is still existed with other backrefs,
* go to check next.
*/
tmp_err &= ~REFERENCER_MISSING;
tmp_err &= ~REFERENCER_MISMATCH;
err |= tmp_err;
eb = path->nodes[0];
slot = path->slots[0];
ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
item_size = btrfs_item_size_nr(eb, slot);
goto next;
err = ret;
}
} else {
err |= tmp_err;
}
ptr += btrfs_extent_inline_ref_size(type);
ptr_offset += btrfs_extent_inline_ref_size(type);
goto next;
out:
@ -4388,50 +4500,6 @@ static int repair_chunk_item(struct btrfs_root *chunk_root,
return err;
}
static int delete_extent_tree_item(struct btrfs_root *root,
struct btrfs_path *path)
{
struct btrfs_key key;
struct btrfs_trans_handle *trans;
int ret = 0;
ret = avoid_extents_overwrite(root->fs_info);
if (ret)
return ret;
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
errno = -ret;
error("fail to start transaction: %m");
goto out;
}
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
btrfs_release_path(path);
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret) {
ret = -ENOENT;
goto out;
}
ret = btrfs_del_item(trans, root, path);
if (ret)
goto out;
if (path->slots[0] == 0)
btrfs_prev_leaf(root, path);
else
path->slots[0]--;
out:
btrfs_commit_transaction(trans, root);
if (ret)
error("failed to delete root %llu item[%llu, %u, %llu]",
root->objectid, key.objectid, key.type, key.offset);
else
printf("Deleted root %llu item[%llu, %u, %llu]\n",
root->objectid, key.objectid, key.type, key.offset);
return ret;
}
/*
* Main entry function to check known items and update related accounting info
*/
@ -4473,7 +4541,7 @@ again:
ret = check_block_group_item(fs_info, eb, slot);
if (repair &&
ret & REFERENCER_MISSING)
ret = delete_extent_tree_item(root, path);
ret = delete_item(root, path);
err |= ret;
break;
case BTRFS_DEV_ITEM_KEY:
@ -4504,7 +4572,7 @@ again:
key.objectid, -1);
if (repair &&
ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
ret = delete_extent_tree_item(root, path);
ret = delete_item(root, path);
err |= ret;
break;
case BTRFS_EXTENT_DATA_REF_KEY:
@ -4517,7 +4585,7 @@ again:
btrfs_extent_data_ref_count(eb, dref));
if (repair &&
ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
ret = delete_extent_tree_item(root, path);
ret = delete_item(root, path);
err |= ret;
break;
case BTRFS_SHARED_BLOCK_REF_KEY:
@ -4525,7 +4593,7 @@ again:
key.objectid, -1);
if (repair &&
ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
ret = delete_extent_tree_item(root, path);
ret = delete_item(root, path);
err |= ret;
break;
case BTRFS_SHARED_DATA_REF_KEY:
@ -4533,7 +4601,7 @@ again:
key.objectid);
if (repair &&
ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
ret = delete_extent_tree_item(root, path);
ret = delete_item(root, path);
err |= ret;
break;
default:
@ -4617,7 +4685,7 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
if (*level == 0) {
/* skip duplicate check */
if (check || !check_all) {
ret = btrfs_check_leaf(root, NULL, cur);
ret = btrfs_check_leaf(fs_info, NULL, cur);
if (ret != BTRFS_TREE_BLOCK_CLEAN) {
err |= -EIO;
break;
@ -4634,7 +4702,7 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
break;
}
if (check || !check_all) {
ret = btrfs_check_node(root, NULL, cur);
ret = btrfs_check_node(fs_info, NULL, cur);
if (ret != BTRFS_TREE_BLOCK_CLEAN) {
err |= -EIO;
break;
@ -4682,9 +4750,9 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
break;
if (btrfs_is_leaf(next))
status = btrfs_check_leaf(root, NULL, next);
status = btrfs_check_leaf(fs_info, NULL, next);
else
status = btrfs_check_node(root, NULL, next);
status = btrfs_check_node(fs_info, NULL, next);
if (status != BTRFS_TREE_BLOCK_CLEAN) {
free_extent_buffer(next);
err |= -EIO;
@ -5077,6 +5145,17 @@ int check_fs_roots_lowmem(struct btrfs_fs_info *fs_info)
btrfs_item_key_to_cpu(node, &key, slot);
if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
goto out;
if (key.type == BTRFS_INODE_ITEM_KEY &&
is_fstree(key.objectid)) {
ret = check_repair_free_space_inode(fs_info, &path);
/* Check if we still have a valid path to continue */
if (ret < 0 && path.nodes[0]) {
err |= ret;
goto next;
}
if (ret < 0 && !path.nodes[0])
goto out;
}
if (key.type == BTRFS_ROOT_ITEM_KEY &&
fs_root_objectid(key.objectid)) {
if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) {

View File

@ -46,6 +46,7 @@
#define FATAL_ERROR (1<<22) /* Fatal bit for errno */
#define INODE_FLAGS_ERROR (1<<23) /* Invalid inode flags */
#define DIR_ITEM_HASH_MISMATCH (1<<24) /* Dir item hash mismatch */
#define INODE_MODE_ERROR (1<<25) /* Bad inode mode */
/*
* Error bit for low memory mode check.
@ -66,5 +67,7 @@
int check_fs_roots_lowmem(struct btrfs_fs_info *fs_info);
int check_chunks_and_extents_lowmem(struct btrfs_fs_info *fs_info);
int check_repair_free_space_inode(struct btrfs_fs_info *fs_info,
struct btrfs_path *path);
#endif

View File

@ -57,21 +57,6 @@ static inline struct data_backref* to_data_backref(struct extent_backref *back)
return container_of(back, struct data_backref, node);
}
/*
* Much like data_backref, just removed the undetermined members
* and change it to use list_head.
* During extent scan, it is stored in root->orphan_data_extent.
* During fs tree scan, it is then moved to inode_rec->orphan_data_extents.
*/
struct orphan_data_extent {
struct list_head list;
u64 root;
u64 objectid;
u64 offset;
u64 disk_bytenr;
u64 disk_len;
};
struct tree_backref {
struct extent_backref node;
union {
@ -170,6 +155,16 @@ struct file_extent_hole {
u64 len;
};
struct unaligned_extent_rec_t {
struct list_head list;
u64 objectid;
u64 owner;
u64 offset;
u64 bytenr;
};
#define I_ERR_NO_INODE_ITEM (1 << 0)
#define I_ERR_NO_ORPHAN_ITEM (1 << 1)
#define I_ERR_DUP_INODE_ITEM (1 << 2)
@ -184,11 +179,12 @@ struct file_extent_hole {
#define I_ERR_ODD_CSUM_ITEM (1 << 11)
#define I_ERR_SOME_CSUM_MISSING (1 << 12)
#define I_ERR_LINK_COUNT_WRONG (1 << 13)
#define I_ERR_FILE_EXTENT_ORPHAN (1 << 14)
#define I_ERR_UNALIGNED_EXTENT_REC (1 << 14)
#define I_ERR_FILE_EXTENT_TOO_LARGE (1 << 15)
#define I_ERR_ODD_INODE_FLAGS (1 << 16)
#define I_ERR_INLINE_RAM_BYTES_WRONG (1 << 17)
#define I_ERR_MISMATCH_DIR_HASH (1 << 18)
#define I_ERR_INVALID_IMODE (1 << 19)
struct inode_record {
struct list_head backrefs;
@ -202,6 +198,8 @@ struct inode_record {
unsigned int nodatasum:1;
int errors;
struct list_head unaligned_extent_recs;
u64 ino;
u32 nlink;
u32 imode;
@ -213,7 +211,6 @@ struct inode_record {
u64 extent_start;
u64 extent_end;
struct rb_root holes;
struct list_head orphan_extents;
struct list_head mismatch_dir_hash;
u32 refs;

32
ctree.c
View File

@ -434,8 +434,8 @@ static inline unsigned int leaf_data_end(const struct btrfs_fs_info *fs_info,
}
enum btrfs_tree_block_status
btrfs_check_node(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
struct extent_buffer *buf)
btrfs_check_node(struct btrfs_fs_info *fs_info,
struct btrfs_disk_key *parent_key, struct extent_buffer *buf)
{
int i;
struct btrfs_key cpukey;
@ -443,7 +443,7 @@ btrfs_check_node(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
u32 nritems = btrfs_header_nritems(buf);
enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root->fs_info))
if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(fs_info))
goto fail;
ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
@ -466,7 +466,7 @@ fail:
btrfs_disk_key_to_cpu(&cpukey, parent_key);
else
btrfs_node_key_to_cpu(buf, &cpukey, 0);
btrfs_add_corrupt_extent_record(root->fs_info, &cpukey,
btrfs_add_corrupt_extent_record(fs_info, &cpukey,
buf->start, buf->len,
btrfs_header_level(buf));
}
@ -474,8 +474,8 @@ fail:
}
enum btrfs_tree_block_status
btrfs_check_leaf(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
struct extent_buffer *buf)
btrfs_check_leaf(struct btrfs_fs_info *fs_info,
struct btrfs_disk_key *parent_key, struct extent_buffer *buf)
{
int i;
struct btrfs_key cpukey;
@ -531,18 +531,18 @@ btrfs_check_leaf(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
goto fail;
}
if (i == 0 && btrfs_item_end_nr(buf, i) !=
BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
BTRFS_LEAF_DATA_SIZE(fs_info)) {
ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
fprintf(stderr, "bad item end %u wanted %u\n",
btrfs_item_end_nr(buf, i),
(unsigned)BTRFS_LEAF_DATA_SIZE(root->fs_info));
(unsigned)BTRFS_LEAF_DATA_SIZE(fs_info));
goto fail;
}
}
for (i = 0; i < nritems; i++) {
if (btrfs_item_end_nr(buf, i) >
BTRFS_LEAF_DATA_SIZE(root->fs_info)) {
BTRFS_LEAF_DATA_SIZE(fs_info)) {
btrfs_item_key(buf, &key, 0);
btrfs_print_key(&key);
fflush(stdout);
@ -550,7 +550,7 @@ btrfs_check_leaf(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
fprintf(stderr, "slot end outside of leaf %llu > %llu\n",
(unsigned long long)btrfs_item_end_nr(buf, i),
(unsigned long long)BTRFS_LEAF_DATA_SIZE(
root->fs_info));
fs_info));
goto fail;
}
}
@ -563,13 +563,13 @@ fail:
else
btrfs_item_key_to_cpu(buf, &cpukey, 0);
btrfs_add_corrupt_extent_record(root->fs_info, &cpukey,
btrfs_add_corrupt_extent_record(fs_info, &cpukey,
buf->start, buf->len, 0);
}
return ret;
}
static int noinline check_block(struct btrfs_root *root,
static int noinline check_block(struct btrfs_fs_info *fs_info,
struct btrfs_path *path, int level)
{
struct btrfs_disk_key key;
@ -585,9 +585,9 @@ static int noinline check_block(struct btrfs_root *root,
key_ptr = &key;
}
if (level == 0)
ret = btrfs_check_leaf(root, key_ptr, path->nodes[0]);
ret = btrfs_check_leaf(fs_info, key_ptr, path->nodes[0]);
else
ret = btrfs_check_node(root, key_ptr, path->nodes[level]);
ret = btrfs_check_node(fs_info, key_ptr, path->nodes[level]);
if (ret == BTRFS_TREE_BLOCK_CLEAN)
return 0;
return -EIO;
@ -871,7 +871,7 @@ static int balance_level(struct btrfs_trans_handle *trans,
}
}
/* double check we haven't messed things up */
check_block(root, path, level);
check_block(root->fs_info, path, level);
if (orig_ptr !=
btrfs_node_blockptr(path->nodes[level], path->slots[level]))
BUG();
@ -1169,7 +1169,7 @@ again:
WARN_ON(1);
level = btrfs_header_level(b);
p->nodes[level] = b;
ret = check_block(root, p, level);
ret = check_block(fs_info, p, level);
if (ret)
return -1;
ret = bin_search(b, key, level, &slot);

18
ctree.h
View File

@ -1191,15 +1191,7 @@ struct btrfs_root {
u32 type;
u64 last_inode_alloc;
/*
* Record orphan data extent ref
*
* TODO: Don't restore things in btrfs_root.
* Directly record it into inode_record, which needs a lot of
* infrastructure change to allow cooperation between extent
* and fs tree scan.
*/
struct list_head orphan_data_extents;
struct list_head unaligned_extent_recs;
/* the dirty list is only used by non-reference counted roots */
struct list_head dirty_list;
@ -2570,11 +2562,11 @@ int btrfs_comp_cpu_keys(const struct btrfs_key *k1, const struct btrfs_key *k2);
int btrfs_del_ptr(struct btrfs_root *root, struct btrfs_path *path,
int level, int slot);
enum btrfs_tree_block_status
btrfs_check_node(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
struct extent_buffer *buf);
btrfs_check_node(struct btrfs_fs_info *fs_info,
struct btrfs_disk_key *parent_key, struct extent_buffer *buf);
enum btrfs_tree_block_status
btrfs_check_leaf(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
struct extent_buffer *buf);
btrfs_check_leaf(struct btrfs_fs_info *fs_info,
struct btrfs_disk_key *parent_key, struct extent_buffer *buf);
void reada_for_search(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
int level, int slot, u64 objectid);
struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,

187
disk-io.c
View File

@ -325,8 +325,9 @@ struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
struct extent_buffer *eb;
u64 best_transid = 0;
u32 sectorsize = fs_info->sectorsize;
int mirror_num = 0;
int mirror_num = 1;
int good_mirror = 0;
int candidate_mirror = 0;
int num_copies;
int ignore = 0;
@ -349,6 +350,7 @@ struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
if (btrfs_buffer_uptodate(eb, parent_transid))
return eb;
num_copies = btrfs_num_copies(fs_info, eb->start, eb->len);
while (1) {
ret = read_whole_eb(fs_info, eb, mirror_num);
if (ret == 0 && csum_tree_block(fs_info, eb, 1) == 0 &&
@ -361,10 +363,30 @@ struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
&fs_info->recow_ebs);
eb->refs++;
}
btrfs_set_buffer_uptodate(eb);
return eb;
/*
* check_tree_block() is less strict to allow btrfs
* check to get raw eb with bad key order and fix it.
* But we still need to try to get a good copy if
* possible, or bad key order can go into tools like
* btrfs ins dump-tree.
*/
if (btrfs_header_level(eb))
ret = btrfs_check_node(fs_info, NULL, eb);
else
ret = btrfs_check_leaf(fs_info, NULL, eb);
if (!ret || candidate_mirror == mirror_num) {
btrfs_set_buffer_uptodate(eb);
return eb;
}
if (candidate_mirror <= 0)
candidate_mirror = mirror_num;
}
if (ignore) {
if (candidate_mirror > 0) {
mirror_num = candidate_mirror;
continue;
}
if (check_tree_block(fs_info, eb)) {
if (!fs_info->suppress_check_block_errors)
print_tree_block_error(fs_info, eb,
@ -376,23 +398,29 @@ struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
ret = -EIO;
break;
}
num_copies = btrfs_num_copies(fs_info, eb->start, eb->len);
if (num_copies == 1) {
ignore = 1;
continue;
}
if (btrfs_header_generation(eb) > best_transid && mirror_num) {
if (btrfs_header_generation(eb) > best_transid) {
best_transid = btrfs_header_generation(eb);
good_mirror = mirror_num;
}
mirror_num++;
if (mirror_num > num_copies) {
mirror_num = good_mirror;
if (candidate_mirror > 0)
mirror_num = candidate_mirror;
else
mirror_num = good_mirror;
ignore = 1;
continue;
}
}
free_extent_buffer(eb);
/*
* We failed to read this tree block, it be should deleted right now
* to avoid stale cache populate the cache.
*/
free_extent_buffer_nocache(eb);
return ERR_PTR(ret);
}
@ -495,7 +523,7 @@ void btrfs_setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
root->last_inode_alloc = 0;
INIT_LIST_HEAD(&root->dirty_list);
INIT_LIST_HEAD(&root->orphan_data_extents);
INIT_LIST_HEAD(&root->unaligned_extent_recs);
memset(&root->root_key, 0, sizeof(root->root_key));
memset(&root->root_item, 0, sizeof(root->root_item));
root->root_key.objectid = objectid;
@ -1585,6 +1613,17 @@ static int write_dev_supers(struct btrfs_fs_info *fs_info,
u32 crc;
int i, ret;
/*
* We need to write super block after all metadata written.
* This is the equivalent of kernel pre-flush for FUA.
*/
ret = fsync(device->fd);
if (ret < 0) {
error(
"failed to write super block for devid %llu: flush error: %m",
device->devid);
return -errno;
}
if (fs_info->super_bytenr != BTRFS_SUPER_INFO_OFFSET) {
btrfs_set_super_bytenr(sb, fs_info->super_bytenr);
crc = ~(u32)0;
@ -1599,8 +1638,20 @@ static int write_dev_supers(struct btrfs_fs_info *fs_info,
ret = pwrite64(device->fd, fs_info->super_copy,
BTRFS_SUPER_INFO_SIZE,
fs_info->super_bytenr);
if (ret != BTRFS_SUPER_INFO_SIZE)
goto write_err;
if (ret != BTRFS_SUPER_INFO_SIZE) {
errno = EIO;
error(
"failed to write super block for devid %llu: write error: %m",
device->devid);
return -EIO;
}
ret = fsync(device->fd);
if (ret < 0) {
error(
"failed to write super block for devid %llu: flush error: %m",
device->devid);
return -errno;
}
return 0;
}
@ -1622,20 +1673,108 @@ static int write_dev_supers(struct btrfs_fs_info *fs_info,
*/
ret = pwrite64(device->fd, fs_info->super_copy,
BTRFS_SUPER_INFO_SIZE, bytenr);
if (ret != BTRFS_SUPER_INFO_SIZE)
goto write_err;
if (ret != BTRFS_SUPER_INFO_SIZE) {
errno = EIO;
error(
"failed to write super block for devid %llu: write error: %m",
device->devid);
return -errno;
}
/*
* Flush after the primary sb write, this is the equivalent of
* kernel post-flush for FUA write.
*/
if (i == 0) {
ret = fsync(device->fd);
if (ret < 0) {
error(
"failed to write super block for devid %llu: flush error: %m",
device->devid);
return -errno;
}
}
}
return 0;
write_err:
if (ret > 0)
fprintf(stderr, "WARNING: failed to write all sb data\n");
else
fprintf(stderr, "WARNING: failed to write sb: %m\n");
return ret;
}
/*
* copy all the root pointers into the super backup array.
* this will bump the backup pointer by one when it is
* done
*/
static void backup_super_roots(struct btrfs_fs_info *info)
{
struct btrfs_root_backup *root_backup;
int next_backup;
int last_backup;
last_backup = find_best_backup_root(info->super_copy);
next_backup = (last_backup + 1) % BTRFS_NUM_BACKUP_ROOTS;
/* just overwrite the last backup if we're at the same generation */
root_backup = info->super_copy->super_roots + last_backup;
if (btrfs_backup_tree_root_gen(root_backup) ==
btrfs_header_generation(info->tree_root->node))
next_backup = last_backup;
root_backup = info->super_copy->super_roots + next_backup;
/*
* make sure all of our padding and empty slots get zero filled
* regardless of which ones we use today
*/
memset(root_backup, 0, sizeof(*root_backup));
btrfs_set_backup_tree_root(root_backup, info->tree_root->node->start);
btrfs_set_backup_tree_root_gen(root_backup,
btrfs_header_generation(info->tree_root->node));
btrfs_set_backup_tree_root_level(root_backup,
btrfs_header_level(info->tree_root->node));
btrfs_set_backup_chunk_root(root_backup, info->chunk_root->node->start);
btrfs_set_backup_chunk_root_gen(root_backup,
btrfs_header_generation(info->chunk_root->node));
btrfs_set_backup_chunk_root_level(root_backup,
btrfs_header_level(info->chunk_root->node));
btrfs_set_backup_extent_root(root_backup, info->extent_root->node->start);
btrfs_set_backup_extent_root_gen(root_backup,
btrfs_header_generation(info->extent_root->node));
btrfs_set_backup_extent_root_level(root_backup,
btrfs_header_level(info->extent_root->node));
/*
* we might commit during log recovery, which happens before we set
* the fs_root. Make sure it is valid before we fill it in.
*/
if (info->fs_root && info->fs_root->node) {
btrfs_set_backup_fs_root(root_backup,
info->fs_root->node->start);
btrfs_set_backup_fs_root_gen(root_backup,
btrfs_header_generation(info->fs_root->node));
btrfs_set_backup_fs_root_level(root_backup,
btrfs_header_level(info->fs_root->node));
}
btrfs_set_backup_dev_root(root_backup, info->dev_root->node->start);
btrfs_set_backup_dev_root_gen(root_backup,
btrfs_header_generation(info->dev_root->node));
btrfs_set_backup_dev_root_level(root_backup,
btrfs_header_level(info->dev_root->node));
btrfs_set_backup_csum_root(root_backup, info->csum_root->node->start);
btrfs_set_backup_csum_root_gen(root_backup,
btrfs_header_generation(info->csum_root->node));
btrfs_set_backup_csum_root_level(root_backup,
btrfs_header_level(info->csum_root->node));
btrfs_set_backup_total_bytes(root_backup,
btrfs_super_total_bytes(info->super_copy));
btrfs_set_backup_bytes_used(root_backup,
btrfs_super_bytes_used(info->super_copy));
btrfs_set_backup_num_devices(root_backup,
btrfs_super_num_devices(info->super_copy));
};
int write_all_supers(struct btrfs_fs_info *fs_info)
{
struct list_head *head = &fs_info->fs_devices->devices;
@ -1645,6 +1784,7 @@ int write_all_supers(struct btrfs_fs_info *fs_info)
int ret;
u64 flags;
backup_super_roots(fs_info);
sb = fs_info->super_copy;
dev_item = &sb->dev_item;
list_for_each_entry(dev, head, dev_list) {
@ -1667,7 +1807,8 @@ int write_all_supers(struct btrfs_fs_info *fs_info)
btrfs_set_super_flags(sb, flags | BTRFS_HEADER_FLAG_WRITTEN);
ret = write_dev_supers(fs_info, sb, dev);
BUG_ON(ret);
if (ret < 0)
return ret;
}
return 0;
}
@ -1723,8 +1864,12 @@ int close_ctree_fs_info(struct btrfs_fs_info *fs_info)
BUG_ON(ret);
ret = __commit_transaction(trans, root);
BUG_ON(ret);
write_ctree_super(trans);
ret = write_ctree_super(trans);
kfree(trans);
if (ret) {
err = ret;
goto skip_commit;
}
}
if (fs_info->finalize_on_close) {

View File

@ -2506,12 +2506,7 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
profile = BTRFS_BLOCK_GROUP_METADATA | alloc_profile;
}
/*
* Do metadata preallocation if we're not modifying the extent tree.
* Allocating chunk while modifying extent tree could lead to transid
* mismatch, as do_chunk_alloc() could commit transaction.
*/
if (root->root_key.objectid != BTRFS_EXTENT_TREE_OBJECTID) {
if (root->ref_cows) {
if (!(profile & BTRFS_BLOCK_GROUP_METADATA)) {
ret = do_chunk_alloc(trans, info,
num_bytes,

View File

@ -197,7 +197,7 @@ commit_tree:
__commit_transaction(trans, root);
if (ret < 0)
goto out;
write_ctree_super(trans);
ret = write_ctree_super(trans);
btrfs_finish_extent_commit(trans, fs_info->extent_root,
&fs_info->pinned_extents);
kfree(trans);