#define DIR_INDEX_MISSING (1<<18) /* INODE_INDEX not found */
#define DIR_INDEX_MISMATCH (1<<19) /* INODE_INDEX found but not match */
#define DIR_COUNT_AGAIN (1<<20) /* DIR isize should be recalculated */
+#define BG_ACCOUNTING_ERROR (1<<21) /* Block group accounting error */
static inline struct data_backref* to_data_backref(struct extent_backref *back)
{
}
}
+/*
+ * This function only handles BACKREF_MISSING,
+ * If corresponding extent item exists, increase the ref, else insert an extent
+ * item and backref.
+ *
+ * Returns error bits after repair.
+ */
+static int repair_tree_block_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct extent_buffer *node,
+ struct node_refs *nrefs, int level, int err)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_root *extent_root = fs_info->extent_root;
+ struct btrfs_path path;
+ struct btrfs_extent_item *ei;
+ struct btrfs_tree_block_info *bi;
+ struct btrfs_key key;
+ struct extent_buffer *eb;
+ u32 size = sizeof(*ei);
+ u32 node_size = root->fs_info->nodesize;
+ int insert_extent = 0;
+ int skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
+ int root_level = btrfs_header_level(root->node);
+ int generation;
+ int ret;
+ u64 owner;
+ u64 bytenr;
+ u64 flags = BTRFS_EXTENT_FLAG_TREE_BLOCK;
+ u64 parent = 0;
+
+ if ((err & BACKREF_MISSING) == 0)
+ return err;
+
+ WARN_ON(level > BTRFS_MAX_LEVEL);
+ WARN_ON(level < 0);
+
+ btrfs_init_path(&path);
+ bytenr = btrfs_header_bytenr(node);
+ owner = btrfs_header_owner(node);
+ generation = btrfs_header_generation(node);
+
+ key.objectid = bytenr;
+ key.type = (u8)-1;
+ key.offset = (u64)-1;
+
+ /* Search for the extent item */
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (ret <= 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
+ if (ret)
+ insert_extent = 1;
+
+ /* calculate if the extent item flag is full backref or not */
+ if (nrefs->full_backref[level] != 0)
+ flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
+
+ /* insert an extent item */
+ if (insert_extent) {
+ struct btrfs_disk_key copy_key;
+
+ generation = btrfs_header_generation(node);
+
+ if (level < root_level && nrefs->full_backref[level + 1] &&
+ owner != root->objectid) {
+ flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
+ }
+
+ key.objectid = bytenr;
+ if (!skinny_metadata) {
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = node_size;
+ size += sizeof(*bi);
+ } else {
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ key.offset = level;
+ }
+
+ btrfs_release_path(&path);
+ ret = btrfs_insert_empty_item(trans, extent_root, &path, &key,
+ size);
+ if (ret)
+ goto out;
+
+ eb = path.nodes[0];
+ ei = btrfs_item_ptr(eb, path.slots[0], struct btrfs_extent_item);
+
+ btrfs_set_extent_refs(eb, ei, 0);
+ btrfs_set_extent_generation(eb, ei, generation);
+ btrfs_set_extent_flags(eb, ei, flags);
+
+ if (!skinny_metadata) {
+ bi = (struct btrfs_tree_block_info *)(ei + 1);
+ memset_extent_buffer(eb, 0, (unsigned long)bi,
+ sizeof(*bi));
+ btrfs_set_disk_key_objectid(©_key, root->objectid);
+ btrfs_set_disk_key_type(©_key, 0);
+ btrfs_set_disk_key_offset(©_key, 0);
+
+ btrfs_set_tree_block_level(eb, bi, level);
+ btrfs_set_tree_block_key(eb, bi, ©_key);
+ }
+ btrfs_mark_buffer_dirty(eb);
+ printf("Added an extent item [%llu %u]\n", bytenr, node_size);
+ btrfs_update_block_group(trans, extent_root, bytenr, node_size,
+ 1, 0);
+
+ nrefs->refs[level] = 0;
+ nrefs->full_backref[level] =
+ flags & BTRFS_BLOCK_FLAG_FULL_BACKREF;
+ btrfs_release_path(&path);
+ }
+
+ if (level < root_level && nrefs->full_backref[level + 1] &&
+ owner != root->objectid)
+ parent = nrefs->bytenr[level + 1];
+
+ /* increase the ref */
+ ret = btrfs_inc_extent_ref(trans, extent_root, bytenr, node_size,
+ parent, root->objectid, level, 0);
+
+ nrefs->refs[level]++;
+out:
+ btrfs_release_path(&path);
+ if (ret) {
+ error(
+ "failed to repair tree block ref start %llu root %llu due to %s",
+ bytenr, root->objectid, strerror(-ret));
+ } else {
+ printf("Added one tree block ref start %llu %s %llu\n",
+ bytenr, parent ? "parent" : "root",
+ parent ? parent : root->objectid);
+ err &= ~BACKREF_MISSING;
+ }
+
+ return err;
+}
+
static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
unsigned int ext_ref);
static int check_tree_block_ref(struct btrfs_root *root,
ret = check_tree_block_ref(root, cur,
btrfs_header_bytenr(cur), btrfs_header_level(cur),
btrfs_header_owner(cur), nrefs);
+
+ if (repair && ret)
+ ret = repair_tree_block_ref(trans, root,
+ path->nodes[*level], nrefs, *level, ret);
err |= ret;
if (check_all && nrefs->need_check[*level] &&
*end, fkey->offset - *end);
if (!repair || ret) {
err |= FILE_EXTENT_ERROR;
- error("root %llu EXTENT_DATA[%llu %llu] interrupt",
- root->objectid, fkey->objectid, fkey->offset);
+ error(
+ "root %llu EXTENT_DATA[%llu %llu] interrupt, should start at %llu",
+ root->objectid, fkey->objectid, fkey->offset, *end);
}
}
*/
static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
{
+ reset_cached_block_groups(root->fs_info);
return check_btrfs_root(NULL, root, ext_ref, 0);
}
struct cache_extent *cache;
int ret = 0;
int had_dups = 0;
+ int err = 0;
if (repair) {
/*
cur_err = 1;
}
+ err = cur_err;
remove_cache_extent(extent_cache, cache);
free_all_extent_backrefs(rec);
if (!init_extent_tree && repair && (!cur_err || fix))
}
return ret;
}
- return 0;
+
+ if (err)
+ err = -EIO;
+ return err;
}
u64 calc_stripe_length(u64 type, u64 length, int num_stripes)
}
}
+/*
+ * Extra (optional) check for dev_item size to report possbile problem on a new
+ * kernel.
+ */
+static void check_dev_size_alignment(u64 devid, u64 total_bytes, u32 sectorsize)
+{
+ if (!IS_ALIGNED(total_bytes, sectorsize)) {
+ warning(
+"unaligned total_bytes detected for devid %llu, have %llu should be aligned to %u",
+ devid, total_bytes, sectorsize);
+ warning(
+"this is OK for older kernel, but may cause kernel warning for newer kernels");
+ warning("this can be fixed by 'btrfs rescue fix-device-size'");
+ }
+}
+
+/*
+ * Unlike device size alignment check above, some super total_bytes check
+ * failure can lead to mount failure for newer kernel.
+ *
+ * So this function will return the error for a fatal super total_bytes problem.
+ */
+static bool is_super_size_valid(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_device *dev;
+ struct list_head *dev_list = &fs_info->fs_devices->devices;
+ u64 total_bytes = 0;
+ u64 super_bytes = btrfs_super_total_bytes(fs_info->super_copy);
+
+ list_for_each_entry(dev, dev_list, dev_list)
+ total_bytes += dev->total_bytes;
+
+ /* Important check, which can cause unmountable fs */
+ if (super_bytes < total_bytes) {
+ error("super total bytes %llu smaller than real device(s) size %llu",
+ super_bytes, total_bytes);
+ error("mounting this fs may fail for newer kernels");
+ error("this can be fixed by 'btrfs rescue fix-device-size'");
+ return false;
+ }
+
+ /*
+ * Optional check, just to make everything aligned and match with each
+ * other.
+ *
+ * For a btrfs-image restored fs, we don't need to check it anyway.
+ */
+ if (btrfs_super_flags(fs_info->super_copy) &
+ (BTRFS_SUPER_FLAG_METADUMP | BTRFS_SUPER_FLAG_METADUMP_V2))
+ return true;
+ if (!IS_ALIGNED(super_bytes, fs_info->sectorsize) ||
+ !IS_ALIGNED(total_bytes, fs_info->sectorsize) ||
+ super_bytes != total_bytes) {
+ warning("minor unaligned/mismatch device size detected");
+ warning(
+ "recommended to use 'btrfs rescue fix-device-size' to fix it");
+ }
+ return true;
+}
+
/* check btrfs_dev_item -> btrfs_dev_extent */
static int check_devices(struct rb_root *dev_cache,
struct device_extent_tree *dev_extent_cache)
if (err)
ret = err;
+ check_dev_size_alignment(dev_rec->devid, dev_rec->total_byte,
+ global_info->sectorsize);
dev_node = rb_next(dev_node);
}
list_for_each_entry(dext_rec, &dev_extent_cache->no_device_orphans,
goto again;
}
+static int check_extent_inline_ref(struct extent_buffer *eb,
+ struct btrfs_key *key, struct btrfs_extent_inline_ref *iref)
+{
+ int ret;
+ u8 type = btrfs_extent_inline_ref_type(eb, iref);
+
+ switch (type) {
+ case BTRFS_TREE_BLOCK_REF_KEY:
+ case BTRFS_EXTENT_DATA_REF_KEY:
+ case BTRFS_SHARED_BLOCK_REF_KEY:
+ case BTRFS_SHARED_DATA_REF_KEY:
+ ret = 0;
+ break;
+ default:
+ error("extent[%llu %u %llu] has unknown ref type: %d",
+ key->objectid, key->type, key->offset, type);
+ ret = UNKNOWN_TYPE;
+ break;
+ }
+
+ return ret;
+}
+
/*
* Check backrefs of a tree block given by @bytenr or @eb.
*
type = btrfs_extent_inline_ref_type(leaf, iref);
offset = btrfs_extent_inline_ref_offset(leaf, iref);
+ ret = check_extent_inline_ref(leaf, &key, iref);
+ if (ret) {
+ err |= ret;
+ break;
+ }
if (type == BTRFS_TREE_BLOCK_REF_KEY) {
if (offset == root->objectid)
found_ref = 1;
}
/*
+ * If @err contains BACKREF_MISSING then add extent of the
+ * file_extent_data_item.
+ *
+ * Returns error bits after reapir.
+ */
+static int repair_extent_data_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *pathp,
+ struct node_refs *nrefs,
+ int err)
+{
+ struct btrfs_file_extent_item *fi;
+ struct btrfs_key fi_key;
+ struct btrfs_key key;
+ struct btrfs_extent_item *ei;
+ struct btrfs_path path;
+ struct btrfs_root *extent_root = root->fs_info->extent_root;
+ struct extent_buffer *eb;
+ u64 size;
+ u64 disk_bytenr;
+ u64 num_bytes;
+ u64 parent;
+ u64 offset;
+ u64 extent_offset;
+ u64 file_offset;
+ int generation;
+ int slot;
+ int ret = 0;
+
+ eb = pathp->nodes[0];
+ slot = pathp->slots[0];
+ btrfs_item_key_to_cpu(eb, &fi_key, slot);
+ fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
+
+ if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE ||
+ btrfs_file_extent_disk_bytenr(eb, fi) == 0)
+ return err;
+
+ file_offset = fi_key.offset;
+ generation = btrfs_file_extent_generation(eb, fi);
+ disk_bytenr = btrfs_file_extent_disk_bytenr(eb, fi);
+ num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi);
+ extent_offset = btrfs_file_extent_offset(eb, fi);
+ offset = file_offset - extent_offset;
+
+ /* now repair only adds backref */
+ if ((err & BACKREF_MISSING) == 0)
+ return err;
+
+ /* search extent item */
+ key.objectid = disk_bytenr;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = num_bytes;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (ret < 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ /* insert an extent item */
+ if (ret > 0) {
+ key.objectid = disk_bytenr;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = num_bytes;
+ size = sizeof(*ei);
+
+ btrfs_release_path(&path);
+ ret = btrfs_insert_empty_item(trans, extent_root, &path, &key,
+ size);
+ if (ret)
+ goto out;
+ eb = path.nodes[0];
+ ei = btrfs_item_ptr(eb, path.slots[0], struct btrfs_extent_item);
+
+ btrfs_set_extent_refs(eb, ei, 0);
+ btrfs_set_extent_generation(eb, ei, generation);
+ btrfs_set_extent_flags(eb, ei, BTRFS_EXTENT_FLAG_DATA);
+
+ btrfs_mark_buffer_dirty(eb);
+ ret = btrfs_update_block_group(trans, extent_root, disk_bytenr,
+ num_bytes, 1, 0);
+ btrfs_release_path(&path);
+ }
+
+ if (nrefs->full_backref[0])
+ parent = btrfs_header_bytenr(eb);
+ else
+ parent = 0;
+
+ ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, parent,
+ root->objectid,
+ parent ? BTRFS_FIRST_FREE_OBJECTID : fi_key.objectid,
+ offset);
+ if (ret) {
+ error(
+ "failed to increase extent data backref[%llu %llu] root %llu",
+ disk_bytenr, num_bytes, root->objectid);
+ goto out;
+ } else {
+ printf("Add one extent data backref [%llu %llu]\n",
+ disk_bytenr, num_bytes);
+ }
+
+ err &= ~BACKREF_MISSING;
+out:
+ if (ret)
+ error("can't repair root %llu extent data item[%llu %llu]",
+ root->objectid, disk_bytenr, num_bytes);
+ return err;
+}
+
+/*
* Check EXTENT_DATA item, mainly for its dbackref in extent tree
*
* Return >0 any error found and output error message
type = btrfs_extent_inline_ref_type(leaf, iref);
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
+ ret = check_extent_inline_ref(leaf, &dbref_key, iref);
+ if (ret) {
+ err |= ret;
+ break;
+ }
if (type == BTRFS_EXTENT_DATA_REF_KEY) {
ref_root = btrfs_extent_data_ref_root(leaf, dref);
if (ref_root == root->objectid)
leaf = path.nodes[0];
slot = path.slots[0];
- if (slot >= btrfs_header_nritems(leaf))
+ if (slot >= btrfs_header_nritems(leaf) ||
+ btrfs_header_owner(leaf) != root_id)
goto next;
btrfs_item_key_to_cpu(leaf, &key, slot);
if (key.objectid != objectid || key.type != BTRFS_EXTENT_DATA_KEY)
}
/*
+ * Only delete backref if REFERENCER_MISSING now
+ *
+ * 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
+ */
+static int repair_extent_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, struct btrfs_path *path,
+ u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid,
+ u64 owner, u64 offset, int err)
+{
+ 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)) {
+ /* 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;
+ printf("Delete backref in extent [%llu %llu]\n",
+ bytenr, num_bytes);
+ } else {
+ error("fail to delete backref in extent [%llu %llu]",
+ bytenr, num_bytes);
+ }
+ }
+
+ /* 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;
+ return ret;
+}
+
+/*
* This function will check a given extent item, including its backref and
* itself (like crossing stripe boundary and type)
*
* Since we don't use extent_record anymore, introduce new error bit
*/
-static int check_extent_item(struct btrfs_fs_info *fs_info,
- struct extent_buffer *eb, int slot)
+static int check_extent_item(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path)
{
struct btrfs_extent_item *ei;
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;
u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
u32 item_size = btrfs_item_size_nr(eb, slot);
u64 flags;
u64 offset;
+ u64 parent;
+ u64 num_bytes;
+ u64 root_objectid;
+ u64 owner;
+ u64 owner_offset;
int metadata = 0;
int level;
struct btrfs_key key;
int err = 0;
btrfs_item_key_to_cpu(eb, &key, slot);
- if (key.type == BTRFS_EXTENT_ITEM_KEY)
+ if (key.type == BTRFS_EXTENT_ITEM_KEY) {
bytes_used += key.offset;
- else
+ num_bytes = key.offset;
+ } else {
bytes_used += nodesize;
+ num_bytes = nodesize;
+ }
if (item_size < sizeof(*ei)) {
/*
goto out;
}
+ parent = 0;
+ root_objectid = 0;
+ owner = 0;
+ owner_offset = 0;
/* Now check every backref in this extent item */
iref = (struct btrfs_extent_inline_ref *)ptr;
type = btrfs_extent_inline_ref_type(eb, iref);
offset = btrfs_extent_inline_ref_offset(eb, iref);
switch (type) {
case BTRFS_TREE_BLOCK_REF_KEY:
+ root_objectid = offset;
+ owner = level;
ret = check_tree_block_backref(fs_info, offset, key.objectid,
level);
err |= ret;
break;
case BTRFS_SHARED_BLOCK_REF_KEY:
+ parent = offset;
ret = check_shared_block_backref(fs_info, offset, key.objectid,
level);
err |= ret;
break;
case BTRFS_EXTENT_DATA_REF_KEY:
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
- ret = check_extent_data_backref(fs_info,
- btrfs_extent_data_ref_root(eb, dref),
- btrfs_extent_data_ref_objectid(eb, dref),
- btrfs_extent_data_ref_offset(eb, dref),
- key.objectid, key.offset,
- btrfs_extent_data_ref_count(eb, dref));
+ 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;
break;
case BTRFS_SHARED_DATA_REF_KEY:
+ parent = offset;
ret = check_shared_data_backref(fs_info, offset, key.objectid);
err |= ret;
break;
default:
error("extent[%llu %d %llu] has unknown ref type: %d",
key.objectid, key.type, key.offset, type);
- err |= UNKNOWN_TYPE;
+ ret = UNKNOWN_TYPE;
+ err |= ret;
goto out;
}
+ if (err && repair) {
+ ret = repair_extent_item(trans, fs_info->extent_root, path,
+ key.objectid, num_bytes, parent, root_objectid,
+ owner, owner_offset, ret);
+ if (ret < 0)
+ goto out;
+ if (ret) {
+ goto next;
+ err = ret;
+ }
+ }
+
ptr += btrfs_extent_inline_ref_size(type);
goto next;
struct btrfs_path path;
struct btrfs_key key;
struct btrfs_dev_extent *ptr;
+ u64 total_bytes;
u64 dev_id;
u64 used;
u64 total = 0;
dev_item = btrfs_item_ptr(eb, slot, struct btrfs_dev_item);
dev_id = btrfs_device_id(eb, dev_item);
used = btrfs_device_bytes_used(eb, dev_item);
+ total_bytes = btrfs_device_total_bytes(eb, dev_item);
key.objectid = dev_id;
key.type = BTRFS_DEV_EXTENT_KEY;
BTRFS_DEV_EXTENT_KEY, dev_id);
return ACCOUNTING_MISMATCH;
}
+ check_dev_size_alignment(dev_id, total_bytes, fs_info->sectorsize);
+
return 0;
}
error(
"block group[%llu %llu] used %llu but extent items used %llu",
bg_key.objectid, bg_key.offset, used, total);
- err |= ACCOUNTING_MISMATCH;
+ err |= BG_ACCOUNTING_ERROR;
+ }
+ return err;
+}
+
+/*
+ * Add block group item to the extent tree if @err contains REFERENCER_MISSING.
+ * FIXME: We still need to repair error of dev_item.
+ *
+ * Returns error after repair.
+ */
+static int repair_chunk_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *chunk_root,
+ struct btrfs_path *path, int err)
+{
+ struct btrfs_chunk *chunk;
+ struct btrfs_key chunk_key;
+ struct extent_buffer *eb = path->nodes[0];
+ u64 length;
+ int slot = path->slots[0];
+ u64 type;
+ int ret = 0;
+
+ btrfs_item_key_to_cpu(eb, &chunk_key, slot);
+ if (chunk_key.type != BTRFS_CHUNK_ITEM_KEY)
+ return err;
+ chunk = btrfs_item_ptr(eb, slot, struct btrfs_chunk);
+ type = btrfs_chunk_type(path->nodes[0], chunk);
+ length = btrfs_chunk_length(eb, chunk);
+
+ if (err & REFERENCER_MISSING) {
+ ret = btrfs_make_block_group(trans, chunk_root->fs_info, 0,
+ type, chunk_key.objectid, chunk_key.offset, length);
+ if (ret) {
+ error("fail to add block group item[%llu %llu]",
+ chunk_key.offset, length);
+ goto out;
+ } else {
+ err &= ~REFERENCER_MISSING;
+ printf("Added block group item[%llu %llu]\n",
+ chunk_key.offset, length);
+ }
}
+
+out:
return err;
}
return err;
}
+static int delete_extent_tree_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path)
+{
+ struct btrfs_key key;
+ int ret = 0;
+
+ 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:
+ 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
*/
switch (type) {
case BTRFS_EXTENT_DATA_KEY:
ret = check_extent_data_item(root, path, nrefs, account_bytes);
+ if (repair && ret)
+ ret = repair_extent_data_item(trans, root, path, nrefs,
+ ret);
err |= ret;
break;
case BTRFS_BLOCK_GROUP_ITEM_KEY:
ret = check_block_group_item(fs_info, eb, slot);
+ if (repair &&
+ ret & REFERENCER_MISSING)
+ ret = delete_extent_tree_item(trans, root, path);
err |= ret;
break;
case BTRFS_DEV_ITEM_KEY:
break;
case BTRFS_CHUNK_ITEM_KEY:
ret = check_chunk_item(fs_info, eb, slot);
+ if (repair && ret)
+ ret = repair_chunk_item(trans, root, path, ret);
err |= ret;
break;
case BTRFS_DEV_EXTENT_KEY:
break;
case BTRFS_EXTENT_ITEM_KEY:
case BTRFS_METADATA_ITEM_KEY:
- ret = check_extent_item(fs_info, eb, slot);
+ ret = check_extent_item(trans, fs_info, path);
err |= ret;
break;
case BTRFS_EXTENT_CSUM_KEY:
case BTRFS_TREE_BLOCK_REF_KEY:
ret = check_tree_block_backref(fs_info, key.offset,
key.objectid, -1);
+ if (repair &&
+ ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
+ ret = delete_extent_tree_item(trans, root, path);
err |= ret;
break;
case BTRFS_EXTENT_DATA_REF_KEY:
btrfs_extent_data_ref_offset(eb, dref),
key.objectid, 0,
btrfs_extent_data_ref_count(eb, dref));
+ if (repair &&
+ ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
+ ret = delete_extent_tree_item(trans, root, path);
err |= ret;
break;
case BTRFS_SHARED_BLOCK_REF_KEY:
ret = check_shared_block_backref(fs_info, key.offset,
key.objectid, -1);
+ if (repair &&
+ ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
+ ret = delete_extent_tree_item(trans, root, path);
err |= ret;
break;
case BTRFS_SHARED_DATA_REF_KEY:
ret = check_shared_data_backref(fs_info, key.offset,
key.objectid);
+ if (repair &&
+ ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
+ ret = delete_extent_tree_item(trans, root, path);
err |= ret;
break;
default:
ret = btrfs_fix_block_accounting(trans, root);
if (ret)
err |= ret;
+ else
+ err &= ~BG_ACCOUNTING_ERROR;
}
if (trans)
else
ret = check_chunks_and_extents(fs_info);
+ /* Also repair device size related problems */
+ if (repair && !ret) {
+ ret = btrfs_fix_device_and_super_size(fs_info);
+ if (ret > 0)
+ ret = 0;
+ }
return ret;
}
goto close_out;
}
+ if (!init_extent_tree) {
+ ret = repair_root_items(info);
+ if (ret < 0) {
+ err = !!ret;
+ error("failed to repair root items: %s", strerror(-ret));
+ goto close_out;
+ }
+ if (repair) {
+ fprintf(stderr, "Fixed %d roots.\n", ret);
+ ret = 0;
+ } else if (ret > 0) {
+ fprintf(stderr,
+ "Found %d roots with an outdated root item.\n",
+ ret);
+ fprintf(stderr,
+ "Please run a filesystem check with the option --repair to fix them.\n");
+ ret = 1;
+ err |= ret;
+ goto close_out;
+ }
+ }
+
ret = do_check_chunks_and_extents(info);
err |= !!ret;
if (ret)
error(
"errors found in extent allocation tree or chunk allocation");
- ret = repair_root_items(info);
- err |= !!ret;
- if (ret < 0) {
- error("failed to repair root items: %s", strerror(-ret));
- goto close_out;
- }
- if (repair) {
- fprintf(stderr, "Fixed %d roots.\n", ret);
- ret = 0;
- } else if (ret > 0) {
- fprintf(stderr,
- "Found %d roots with an outdated root item.\n",
- ret);
- fprintf(stderr,
- "Please run a filesystem check with the option --repair to fix them.\n");
- ret = 1;
- err |= !!ret;
- goto close_out;
- }
+ /* Only re-check super size after we checked and repaired the fs */
+ err |= !is_super_size_valid(info);
if (!ctx.progress_enabled) {
if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))