forked from Mirrors/btrfs-progs
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
commit
5ca28fc25f
638
check/main.c
638
check/main.c
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
32
ctree.c
|
@ -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
18
ctree.h
|
@ -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
187
disk-io.c
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue