summaryrefslogtreecommitdiff
path: root/check/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'check/main.c')
-rw-r--r--check/main.c608
1 files changed, 390 insertions, 218 deletions
diff --git a/check/main.c b/check/main.c
index 3190b5d4..db18827b 100644
--- a/check/main.c
+++ b/check/main.c
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <getopt.h>
#include <uuid/uuid.h>
+#include <time.h>
#include "ctree.h"
#include "volumes.h"
#include "repair.h"
@@ -47,20 +48,6 @@
#include "check/mode-original.h"
#include "check/mode-lowmem.h"
-enum task_position {
- TASK_EXTENTS,
- TASK_FREE_SPACE,
- TASK_FS_ROOTS,
- TASK_NOTHING, /* have to be the last element */
-};
-
-struct task_ctx {
- int progress_enabled;
- enum task_position tp;
-
- struct task_info *info;
-};
-
u64 bytes_used = 0;
u64 total_csum_bytes = 0;
u64 total_btree_bytes = 0;
@@ -72,6 +59,7 @@ u64 data_bytes_referenced = 0;
LIST_HEAD(duplicate_extents);
LIST_HEAD(delete_items);
int no_holes = 0;
+static int is_free_space_tree = 0;
int init_extent_tree = 0;
int check_data_csum = 0;
struct btrfs_fs_info *global_info;
@@ -173,28 +161,55 @@ static int compare_extent_backref(struct rb_node *node1, struct rb_node *node2)
return compare_tree_backref(node1, node2);
}
+static void print_status_check_line(void *p)
+{
+ struct task_ctx *priv = p;
+ const char *task_position_string[] = {
+ "[1/7] checking root items ",
+ "[2/7] checking extents ",
+ is_free_space_tree ?
+ "[3/7] checking free space tree " :
+ "[3/7] checking free space cache ",
+ "[4/7] checking fs roots ",
+ check_data_csum ?
+ "[5/7] checking csums against data " :
+ "[5/7] checking csums (without verifying data) ",
+ "[6/7] checking root refs ",
+ "[7/7] checking quota groups ",
+ };
+ time_t elapsed;
+ int hours;
+ int minutes;
+ int seconds;
+
+ elapsed = time(NULL) - priv->start_time;
+ hours = elapsed / 3600;
+ elapsed -= hours * 3600;
+ minutes = elapsed / 60;
+ elapsed -= minutes * 60;
+ seconds = elapsed;
+
+ printf("%s (%d:%02d:%02d elapsed", task_position_string[priv->tp],
+ hours, minutes, seconds);
+ if (priv->item_count > 0)
+ printf(", %llu items checked)\r", priv->item_count);
+ else
+ printf(")\r");
+ fflush(stdout);
+}
static void *print_status_check(void *p)
{
struct task_ctx *priv = p;
- const char work_indicator[] = { '.', 'o', 'O', 'o' };
- uint32_t count = 0;
- static char *task_position_string[] = {
- "checking extents",
- "checking free space cache",
- "checking fs roots",
- };
- task_period_start(priv->info, 1000 /* 1s */);
+ /* 1 second */
+ task_period_start(priv->info, 1000);
if (priv->tp == TASK_NOTHING)
return NULL;
while (1) {
- printf("%s [%c]\r", task_position_string[priv->tp],
- work_indicator[count % 4]);
- count++;
- fflush(stdout);
+ print_status_check_line(p);
task_period_wait(priv->info);
}
return NULL;
@@ -202,6 +217,7 @@ static void *print_status_check(void *p)
static int print_status_return(void *p)
{
+ print_status_check_line(p);
printf("\n");
fflush(stdout);
@@ -578,6 +594,8 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
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");
fprintf(stderr, "\n");
/* Print the orphan extents if needed */
if (errors & I_ERR_FILE_EXTENT_ORPHAN)
@@ -1472,7 +1490,7 @@ static int process_file_extent(struct btrfs_root *root,
u8 compression = btrfs_file_extent_compression(eb, fi);
struct btrfs_item *item = btrfs_item_nr(slot);
- num_bytes = btrfs_file_extent_inline_len(eb, slot, fi);
+ num_bytes = btrfs_file_extent_ram_bytes(eb, fi);
if (num_bytes == 0)
rec->errors |= I_ERR_BAD_FILE_EXTENT;
if (compression) {
@@ -1483,6 +1501,9 @@ static int process_file_extent(struct btrfs_root *root,
} else {
if (num_bytes > max_inline_size)
rec->errors |= I_ERR_FILE_EXTENT_TOO_LARGE;
+ if (btrfs_file_extent_inline_item_len(eb, item) !=
+ num_bytes)
+ rec->errors |= I_ERR_INLINE_RAM_BYTES_WRONG;
}
rec->found_size += num_bytes;
num_bytes = (num_bytes + mask) & ~mask;
@@ -2270,9 +2291,9 @@ static int repair_inode_nlinks(struct btrfs_trans_handle *trans,
ret = reset_nlink(trans, root, path, rec);
if (ret < 0) {
+ errno = -ret;
fprintf(stderr,
- "Failed to reset nlink for inode %llu: %s\n",
- rec->ino, strerror(-ret));
+ "Failed to reset nlink for inode %llu: %m\n", rec->ino);
goto out;
}
@@ -2534,6 +2555,41 @@ out:
return ret;
}
+static int repair_inline_ram_bytes(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+ struct inode_record *rec)
+{
+ struct btrfs_key key;
+ struct btrfs_file_extent_item *fi;
+ struct btrfs_item *i;
+ u64 on_disk_item_len;
+ int ret;
+
+ key.objectid = rec->ino;
+ key.offset = 0;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+
+ ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ if (ret > 0)
+ ret = -ENOENT;
+ if (ret < 0)
+ goto out;
+
+ i = btrfs_item_nr(path->slots[0]);
+ on_disk_item_len = btrfs_file_extent_inline_item_len(path->nodes[0], i);
+ fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_file_extent_item);
+ btrfs_set_file_extent_ram_bytes(path->nodes[0], fi, on_disk_item_len);
+ btrfs_mark_buffer_dirty(path->nodes[0]);
+ printf("Repaired inline ram_bytes for root %llu ino %llu\n",
+ root->objectid, rec->ino);
+ rec->errors &= ~I_ERR_INLINE_RAM_BYTES_WRONG;
+out:
+ btrfs_release_path(path);
+ return ret;
+}
+
static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
{
struct btrfs_trans_handle *trans;
@@ -2545,8 +2601,9 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
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_FILE_EXTENT_DISCOUNT |
+ I_ERR_FILE_NBYTES_WRONG |
+ I_ERR_INLINE_RAM_BYTES_WRONG)))
return rec->errors;
/*
@@ -2575,6 +2632,8 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
ret = repair_inode_nlinks(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_FILE_NBYTES_WRONG)
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);
btrfs_commit_transaction(trans, root);
btrfs_release_path(&path);
return ret;
@@ -2676,7 +2735,10 @@ static int check_inode_recs(struct btrfs_root *root,
(unsigned long long)root->objectid);
ret = btrfs_make_root_dir(trans, root, root_dirid);
- BUG_ON(ret);
+ if (ret < 0) {
+ btrfs_abort_transaction(trans, ret);
+ return ret;
+ }
btrfs_commit_transaction(trans, root);
return -EAGAIN;
@@ -2721,7 +2783,6 @@ static int check_inode_recs(struct btrfs_root *root,
free_inode_rec(rec);
continue;
}
- ret = 0;
}
if (!(repair && ret == 0))
@@ -2942,6 +3003,7 @@ static int check_root_refs(struct btrfs_root *root,
loop = 0;
cache = search_cache_extent(root_cache, 0);
while (1) {
+ ctx.item_count++;
if (!cache)
break;
rec = container_of(cache, struct root_record, cache);
@@ -3108,8 +3170,8 @@ static int repair_btree(struct btrfs_root *root,
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
- fprintf(stderr, "Error starting transaction: %s\n",
- strerror(-ret));
+ errno = -ret;
+ fprintf(stderr, "Error starting transaction: %m\n");
return ret;
}
btrfs_init_path(&path);
@@ -3263,6 +3325,7 @@ static int check_fs_root(struct btrfs_root *root,
}
while (1) {
+ ctx.item_count++;
wret = walk_down_tree(root, &path, wc, &level, &nrefs);
if (wret < 0)
ret = wret;
@@ -3300,8 +3363,8 @@ skip_walking:
root->root_key.objectid);
ret = repair_btree(root, &corrupt_blocks);
if (ret < 0)
- fprintf(stderr, "Failed to repair btree: %s\n",
- strerror(-ret));
+ errno = -ret;
+ fprintf(stderr, "Failed to repair btree: %m\n");
if (!ret)
printf("Btree for root %llu is fixed\n",
root->root_key.objectid);
@@ -3337,14 +3400,10 @@ static int check_fs_roots(struct btrfs_fs_info *fs_info,
struct extent_buffer *leaf, *tree_node;
struct btrfs_root *tmp_root;
struct btrfs_root *tree_root = fs_info->tree_root;
+ u64 skip_root = 0;
int ret;
int err = 0;
- if (ctx.progress_enabled) {
- ctx.tp = TASK_FS_ROOTS;
- task_start(ctx.info);
- }
-
/*
* Just in case we made any changes to the extent tree that weren't
* reflected into the free space cache yet.
@@ -3357,7 +3416,10 @@ static int check_fs_roots(struct btrfs_fs_info *fs_info,
again:
key.offset = 0;
- key.objectid = 0;
+ if (skip_root)
+ key.objectid = skip_root + 1;
+ else
+ key.objectid = 0;
key.type = BTRFS_ROOT_ITEM_KEY;
ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
if (ret < 0) {
@@ -3366,6 +3428,7 @@ again:
}
tree_node = tree_root->node;
while (1) {
+
if (tree_node != tree_root->node) {
free_root_recs_tree(root_cache);
btrfs_release_path(&path);
@@ -3402,8 +3465,18 @@ again:
btrfs_release_path(&path);
goto again;
}
- if (ret)
+ if (ret) {
err = 1;
+
+ /*
+ * We failed to repair this root but modified
+ * tree root, after again: label we will still
+ * hit this root and fail to repair, so we must
+ * skip it to avoid infinite loop.
+ */
+ if (repair)
+ skip_root = key.objectid;
+ }
if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
btrfs_free_fs_root(tmp_root);
} else if (key.type == BTRFS_ROOT_REF_KEY ||
@@ -3421,8 +3494,6 @@ out:
if (!cache_tree_empty(&wc.shared))
fprintf(stderr, "warning line %d\n", __LINE__);
- task_stop(ctx.info);
-
return err;
}
@@ -3491,8 +3562,6 @@ static int do_check_fs_roots(struct btrfs_fs_info *fs_info,
{
int ret;
- if (!ctx.progress_enabled)
- fprintf(stderr, "checking fs roots\n");
if (check_mode == CHECK_MODE_LOWMEM)
ret = check_fs_roots_lowmem(fs_info);
else
@@ -3681,7 +3750,12 @@ static int check_owner_ref(struct btrfs_root *root,
if (btrfs_header_owner(buf) == back->root)
return 0;
}
- BUG_ON(rec->is_root);
+ /*
+ * Some unexpected root item referring to this one, return 1 to
+ * indicate owner not found
+ */
+ if (rec->is_root)
+ return 1;
/* try to find the block by search corresponding fs tree */
key.objectid = btrfs_header_owner(buf);
@@ -3977,6 +4051,11 @@ static int try_to_fix_bad_block(struct btrfs_root *root,
btrfs_init_path(&path);
ULIST_ITER_INIT(&iter);
+ /*
+ * If we found no roots referencing to this tree block, there is no
+ * chance to fix. So our default ret is -EIO.
+ */
+ ret = -EIO;
while ((node = ulist_next(roots, &iter))) {
root_key.objectid = node->val;
root_key.type = BTRFS_ROOT_ITEM_KEY;
@@ -4964,7 +5043,7 @@ btrfs_new_device_extent_record(struct extent_buffer *leaf,
rec->offset = key->offset;
ptr = btrfs_item_ptr(leaf, slot, struct btrfs_dev_extent);
- rec->chunk_objecteid =
+ rec->chunk_objectid =
btrfs_dev_extent_chunk_objectid(leaf, ptr);
rec->chunk_offset =
btrfs_dev_extent_chunk_offset(leaf, ptr);
@@ -5098,18 +5177,20 @@ static int process_extent_item(struct btrfs_root *root,
case BTRFS_TREE_BLOCK_REF_KEY:
ret = add_tree_backref(extent_cache, key.objectid,
0, offset, 0);
- if (ret < 0)
+ if (ret < 0) {
+ errno = -ret;
error(
- "add_tree_backref failed (extent items tree block): %s",
- strerror(-ret));
+ "add_tree_backref failed (extent items tree block): %m");
+ }
break;
case BTRFS_SHARED_BLOCK_REF_KEY:
ret = add_tree_backref(extent_cache, key.objectid,
offset, 0, 0);
- if (ret < 0)
+ if (ret < 0) {
+ errno = -ret;
error(
- "add_tree_backref failed (extent items shared block): %s",
- strerror(-ret));
+ "add_tree_backref failed (extent items shared block): %m");
+ }
break;
case BTRFS_EXTENT_DATA_REF_KEY:
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
@@ -5321,20 +5402,8 @@ static int check_space_cache(struct btrfs_root *root)
int ret;
int error = 0;
- if (btrfs_super_cache_generation(root->fs_info->super_copy) != -1ULL &&
- btrfs_super_generation(root->fs_info->super_copy) !=
- btrfs_super_cache_generation(root->fs_info->super_copy)) {
- printf("cache and super generation don't match, space cache "
- "will be invalidated\n");
- return 0;
- }
-
- if (ctx.progress_enabled) {
- ctx.tp = TASK_FREE_SPACE;
- task_start(ctx.info);
- }
-
while (1) {
+ ctx.item_count++;
cache = btrfs_lookup_first_block_group(root->fs_info, start);
if (!cache)
break;
@@ -5353,16 +5422,18 @@ static int check_space_cache(struct btrfs_root *root)
if (btrfs_fs_compat_ro(root->fs_info, FREE_SPACE_TREE)) {
ret = exclude_super_stripes(root, cache);
if (ret) {
- fprintf(stderr, "could not exclude super stripes: %s\n",
- strerror(-ret));
+ errno = -ret;
+ fprintf(stderr,
+ "could not exclude super stripes: %m\n");
error++;
continue;
}
ret = load_free_space_tree(root->fs_info, cache);
free_excluded_extents(root, cache);
if (ret < 0) {
- fprintf(stderr, "could not load free space tree: %s\n",
- strerror(-ret));
+ errno = -ret;
+ fprintf(stderr,
+ "could not load free space tree: %m\n");
error++;
continue;
}
@@ -5383,8 +5454,6 @@ static int check_space_cache(struct btrfs_root *root)
}
}
- task_stop(ctx.info);
-
return error ? -EINVAL : 0;
}
@@ -5654,6 +5723,7 @@ static int check_csums(struct btrfs_root *root)
}
while (1) {
+ ctx.item_count++;
if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
ret = btrfs_next_leaf(root, &path);
if (ret < 0) {
@@ -6056,19 +6126,21 @@ static int run_next_block(struct btrfs_root *root,
if (key.type == BTRFS_TREE_BLOCK_REF_KEY) {
ret = add_tree_backref(extent_cache,
key.objectid, 0, key.offset, 0);
- if (ret < 0)
+ if (ret < 0) {
+ errno = -ret;
error(
- "add_tree_backref failed (leaf tree block): %s",
- strerror(-ret));
+ "add_tree_backref failed (leaf tree block): %m");
+ }
continue;
}
if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) {
ret = add_tree_backref(extent_cache,
key.objectid, key.offset, 0, 0);
- if (ret < 0)
+ if (ret < 0) {
+ errno = -ret;
error(
- "add_tree_backref failed (leaf shared block): %s",
- strerror(-ret));
+ "add_tree_backref failed (leaf shared block): %m");
+ }
continue;
}
if (key.type == BTRFS_EXTENT_DATA_REF_KEY) {
@@ -6170,9 +6242,9 @@ static int run_next_block(struct btrfs_root *root,
ret = add_tree_backref(extent_cache, ptr, parent,
owner, 1);
if (ret < 0) {
+ errno = -ret;
error(
- "add_tree_backref failed (non-leaf block): %s",
- strerror(-ret));
+ "add_tree_backref failed (non-leaf block): %m");
continue;
}
@@ -6232,8 +6304,7 @@ static int add_root_to_pending(struct extent_buffer *buf,
* we're tracking for repair. This hook makes sure we
* remove any backrefs for blocks as we are fixing them.
*/
-static int free_extent_hook(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int free_extent_hook(struct btrfs_fs_info *fs_info,
u64 bytenr, u64 num_bytes, u64 parent,
u64 root_objectid, u64 owner, u64 offset,
int refs_to_drop)
@@ -6241,7 +6312,7 @@ static int free_extent_hook(struct btrfs_trans_handle *trans,
struct extent_record *rec;
struct cache_extent *cache;
int is_data;
- struct cache_tree *extent_cache = root->fs_info->fsck_extent_cache;
+ struct cache_tree *extent_cache = fs_info->fsck_extent_cache;
is_data = owner >= BTRFS_FIRST_FREE_OBJECTID;
cache = lookup_cache_extent(extent_cache, bytenr, num_bytes);
@@ -6800,7 +6871,7 @@ static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
goto out;
fprintf(stderr,
- "attempting to repair backref discrepency for bytenr %llu\n",
+ "attempting to repair backref discrepancy for bytenr %llu\n",
rec->start);
/*
@@ -7780,7 +7851,7 @@ static int check_chunk_refs(struct chunk_record *chunk_rec,
dev_extent_rec->length != length) {
if (!silent)
fprintf(stderr,
- "Chunk[%llu, %u, %llu] stripe[%llu, %llu] dismatch dev extent[%llu, %llu, %llu]\n",
+"Chunk[%llu, %u, %llu] stripe[%llu, %llu] mismatch dev extent[%llu, %llu, %llu]\n",
chunk_rec->objectid,
chunk_rec->type,
chunk_rec->offset,
@@ -8047,6 +8118,7 @@ static int deal_root_from_list(struct list_head *list,
* can maximize readahead.
*/
while (1) {
+ ctx.item_count++;
ret = run_next_block(root, bits, bits_nr, &last,
pending, seen, reada, nodes,
extent_cache, chunk_cache,
@@ -8075,6 +8147,89 @@ static int deal_root_from_list(struct list_head *list,
return ret;
}
+/**
+ * parse_tree_roots - Go over all roots in the tree root and add each one to
+ * a list.
+ *
+ * @fs_info - pointer to fs_info struct of the file system.
+ *
+ * @normal_trees - list to contains all roots which don't have a drop
+ * operation in progress
+ *
+ * @dropping_trees - list containing all roots which have a drop operation
+ * pending
+ *
+ * Returns 0 on success or a negative value indicating an error.
+ */
+static int parse_tree_roots(struct btrfs_fs_info *fs_info,
+ struct list_head *normal_trees,
+ struct list_head *dropping_trees)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_key found_key;
+ struct btrfs_root_item ri;
+ struct extent_buffer *leaf;
+ int slot;
+ int ret = 0;
+
+ btrfs_init_path(&path);
+ key.offset = 0;
+ key.objectid = 0;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+ while (1) {
+ leaf = path.nodes[0];
+ slot = path.slots[0];
+ if (slot >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(fs_info->tree_root, &path);
+ if (ret != 0)
+ break;
+ leaf = path.nodes[0];
+ slot = path.slots[0];
+ }
+ btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]);
+ if (found_key.type == BTRFS_ROOT_ITEM_KEY) {
+ unsigned long offset;
+ u64 last_snapshot;
+ u8 level;
+
+ offset = btrfs_item_ptr_offset(leaf, path.slots[0]);
+ read_extent_buffer(leaf, &ri, offset, sizeof(ri));
+ last_snapshot = btrfs_root_last_snapshot(&ri);
+ level = btrfs_root_level(&ri);
+ if (btrfs_disk_key_objectid(&ri.drop_progress) == 0) {
+ ret = add_root_item_to_list(normal_trees,
+ found_key.objectid,
+ btrfs_root_bytenr(&ri),
+ last_snapshot, level,
+ 0, NULL);
+ if (ret < 0)
+ break;
+ } else {
+ u64 objectid = found_key.objectid;
+
+ btrfs_disk_key_to_cpu(&found_key,
+ &ri.drop_progress);
+ ret = add_root_item_to_list(dropping_trees,
+ objectid,
+ btrfs_root_bytenr(&ri),
+ last_snapshot, level,
+ ri.drop_level, &found_key);
+ if (ret < 0)
+ break;
+ }
+ }
+ path.slots[0]++;
+ }
+
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
static int check_chunks_and_extents(struct btrfs_fs_info *fs_info)
{
struct rb_root dev_cache;
@@ -8088,20 +8243,13 @@ static int check_chunks_and_extents(struct btrfs_fs_info *fs_info)
struct cache_tree nodes;
struct extent_io_tree excluded_extents;
struct cache_tree corrupt_blocks;
- struct btrfs_path path;
- struct btrfs_key key;
- struct btrfs_key found_key;
int ret, err = 0;
struct block_info *bits;
int bits_nr;
- struct extent_buffer *leaf;
- int slot;
- struct btrfs_root_item ri;
struct list_head dropping_trees;
struct list_head normal_trees;
struct btrfs_root *root1;
struct btrfs_root *root;
- u64 objectid;
u8 level;
root = fs_info->fs_root;
@@ -8134,11 +8282,6 @@ static int check_chunks_and_extents(struct btrfs_fs_info *fs_info)
exit(1);
}
- if (ctx.progress_enabled) {
- ctx.tp = TASK_EXTENTS;
- task_start(ctx.info);
- }
-
again:
root1 = fs_info->tree_root;
level = btrfs_header_level(root1->node);
@@ -8152,57 +8295,10 @@ again:
root1->node->start, 0, level, 0, NULL);
if (ret < 0)
goto out;
- btrfs_init_path(&path);
- key.offset = 0;
- key.objectid = 0;
- key.type = BTRFS_ROOT_ITEM_KEY;
- ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0);
+
+ ret = parse_tree_roots(fs_info, &normal_trees, &dropping_trees);
if (ret < 0)
goto out;
- while (1) {
- leaf = path.nodes[0];
- slot = path.slots[0];
- if (slot >= btrfs_header_nritems(path.nodes[0])) {
- ret = btrfs_next_leaf(root, &path);
- if (ret != 0)
- break;
- leaf = path.nodes[0];
- slot = path.slots[0];
- }
- btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]);
- if (found_key.type == BTRFS_ROOT_ITEM_KEY) {
- unsigned long offset;
- u64 last_snapshot;
-
- offset = btrfs_item_ptr_offset(leaf, path.slots[0]);
- read_extent_buffer(leaf, &ri, offset, sizeof(ri));
- last_snapshot = btrfs_root_last_snapshot(&ri);
- if (btrfs_disk_key_objectid(&ri.drop_progress) == 0) {
- level = btrfs_root_level(&ri);
- ret = add_root_item_to_list(&normal_trees,
- found_key.objectid,
- btrfs_root_bytenr(&ri),
- last_snapshot, level,
- 0, NULL);
- if (ret < 0)
- goto out;
- } else {
- level = btrfs_root_level(&ri);
- objectid = found_key.objectid;
- btrfs_disk_key_to_cpu(&found_key,
- &ri.drop_progress);
- ret = add_root_item_to_list(&dropping_trees,
- objectid,
- btrfs_root_bytenr(&ri),
- last_snapshot, level,
- ri.drop_level, &found_key);
- if (ret < 0)
- goto out;
- }
- }
- path.slots[0]++;
- }
- btrfs_release_path(&path);
/*
* check_block can return -EAGAIN if it fixes something, please keep
@@ -8248,7 +8344,6 @@ again:
ret = err;
out:
- task_stop(ctx.info);
if (repair) {
free_corrupt_blocks_tree(fs_info->corrupt_blocks);
extent_io_tree_cleanup(&excluded_extents);
@@ -8290,8 +8385,6 @@ static int do_check_chunks_and_extents(struct btrfs_fs_info *fs_info)
{
int ret;
- if (!ctx.progress_enabled)
- fprintf(stderr, "checking extents\n");
if (check_mode == CHECK_MODE_LOWMEM)
ret = check_chunks_and_extents_lowmem(fs_info);
else
@@ -8307,7 +8400,7 @@ static int do_check_chunks_and_extents(struct btrfs_fs_info *fs_info)
}
static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, int overwrite)
+ struct btrfs_root *root)
{
struct extent_buffer *c;
struct extent_buffer *old = root->node;
@@ -8317,21 +8410,13 @@ static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
level = 0;
- if (overwrite) {
- c = old;
- extent_buffer_get(c);
- goto init;
- }
c = btrfs_alloc_free_block(trans, root,
root->fs_info->nodesize,
root->root_key.objectid,
&disk_key, level, 0, 0);
- if (IS_ERR(c)) {
- c = old;
- extent_buffer_get(c);
- overwrite = 1;
- }
-init:
+ if (IS_ERR(c))
+ return PTR_ERR(c);
+
memset_extent_buffer(c, 0, 0, sizeof(struct btrfs_header));
btrfs_set_header_level(c, level);
btrfs_set_header_bytenr(c, c->start);
@@ -8350,9 +8435,7 @@ init:
/*
* this case can happen in the following case:
*
- * 1.overwrite previous root.
- *
- * 2.reinit reloc data root, this is because we skip pin
+ * reinit reloc data root, this is because we skip pin
* down reloc data tree before which means we can allocate
* same block bytenr here.
*/
@@ -8537,7 +8620,7 @@ reinit_data_reloc:
goto out;
}
record_root_in_trans(trans, root);
- ret = btrfs_fsck_reinit_root(trans, root, 0);
+ ret = btrfs_fsck_reinit_root(trans, root);
if (ret)
goto out;
ret = btrfs_make_root_dir(trans, root, BTRFS_FIRST_FREE_OBJECTID);
@@ -8571,7 +8654,7 @@ static int reinit_extent_tree(struct btrfs_trans_handle *trans,
* first we need to walk all of the trees except the extent tree and pin
* down/exclude the bytes that are in use so we don't overwrite any
* existing metadata.
- * If pinnned, unpin will be done in the end of transaction.
+ * If pinned, unpin will be done in the end of transaction.
* If excluded, cleanup will be done in check_chunks_and_extents_lowmem.
*/
again:
@@ -8603,7 +8686,7 @@ again:
}
/* Ok we can allocate now, reinit the extent root */
- ret = btrfs_fsck_reinit_root(trans, fs_info->extent_root, 0);
+ ret = btrfs_fsck_reinit_root(trans, fs_info->extent_root);
if (ret) {
fprintf(stderr, "extent root initialization failed\n");
/*
@@ -8633,7 +8716,7 @@ again:
fprintf(stderr, "Error adding block group\n");
return ret;
}
- btrfs_extent_post_op(trans);
+ btrfs_run_delayed_refs(trans, -1);
}
ret = reset_balance(trans, fs_info);
@@ -9021,6 +9104,7 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
struct cache_extent *entry;
struct root_item_info *rii;
+ ctx.item_count++;
if (slot >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(info->extent_root, &path);
if (ret < 0) {
@@ -9062,7 +9146,7 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
* It's a valid extent/metadata item that has no inline ref,
* but SHARED_BLOCK_REF or other shared references.
* So we need to do extra check to avoid reading beyond leaf
- * boudnary.
+ * boundary.
*/
if ((unsigned long)iref >= item_end)
goto next;
@@ -9339,7 +9423,6 @@ static int do_clear_free_space_cache(struct btrfs_fs_info *fs_info,
ret = 1;
goto close_out;
}
- printf("Clearing free space cache\n");
ret = clear_free_space_cache(fs_info);
if (ret) {
error("failed to clear free space cache");
@@ -9366,35 +9449,69 @@ close_out:
return ret;
}
+static int validate_free_space_cache(struct btrfs_root *root)
+{
+ int ret;
+
+ if (btrfs_super_cache_generation(root->fs_info->super_copy) != -1ULL &&
+ btrfs_super_generation(root->fs_info->super_copy) !=
+ btrfs_super_cache_generation(root->fs_info->super_copy)) {
+ printf(
+"cache and super generation don't match, space cache will be invalidated\n");
+ return 0;
+ }
+
+ ret = check_space_cache(root);
+ if (ret && btrfs_fs_compat_ro(global_info, FREE_SPACE_TREE) &&
+ repair) {
+ ret = do_clear_free_space_cache(global_info, 2);
+ if (ret)
+ goto out;
+
+ ret = btrfs_create_free_space_tree(global_info);
+ if (ret)
+ error("couldn't repair freespace tree");
+ }
+
+out:
+ return ret ? -EINVAL : 0;
+}
+
const char * const cmd_check_usage[] = {
"btrfs check [options] <device>",
"Check structural integrity of a filesystem (unmounted).",
"Check structural integrity of an unmounted filesystem. Verify internal",
"trees' consistency and item connectivity. In the repair mode try to",
"fix the problems found. ",
- "WARNING: the repair mode is considered dangerous",
+ "WARNING: the repair mode is considered dangerous and should not be used",
+ " without prior analysis of problems found on the filesystem."
"",
- "-s|--super <superblock> use this superblock copy",
- "-b|--backup use the first valid backup root copy",
- "--force skip mount checks, repair is not possible",
- "--repair try to repair the filesystem",
- "--readonly run in read-only mode (default)",
- "--init-csum-tree create a new CRC tree",
- "--init-extent-tree create a new extent tree",
- "--mode <MODE> allows choice of memory/IO trade-offs",
- " where MODE is one of:",
- " original - read inodes and extents to memory (requires",
- " more memory, does less IO)",
- " lowmem - try to use less memory but read blocks again",
- " when needed",
- "--check-data-csum verify checksums of data blocks",
- "-Q|--qgroup-report print a report on qgroup consistency",
- "-E|--subvol-extents <subvolid>",
- " print subvolume extents and sharing state",
- "-r|--tree-root <bytenr> use the given bytenr for the tree root",
- "--chunk-root <bytenr> use the given bytenr for the chunk tree root",
- "-p|--progress indicate progress",
- "--clear-space-cache v1|v2 clear space cache for v1 or v2",
+ "Options:",
+ " starting point selection:",
+ " -s|--super <superblock> use this superblock copy",
+ " -b|--backup use the first valid backup root copy",
+ " -r|--tree-root <bytenr> use the given bytenr for the tree root",
+ " --chunk-root <bytenr> use the given bytenr for the chunk tree root",
+ " operation modes:",
+ " --readonly run in read-only mode (default)",
+ " --repair try to repair the filesystem",
+ " --force skip mount checks, repair is not possible",
+ " --mode <MODE> allows choice of memory/IO trade-offs",
+ " where MODE is one of:",
+ " original - read inodes and extents to memory (requires",
+ " more memory, does less IO)",
+ " lowmem - try to use less memory but read blocks again",
+ " when needed (experimental)",
+ " repair options:",
+ " --init-csum-tree create a new CRC tree",
+ " --init-extent-tree create a new extent tree",
+ " --clear-space-cache v1|v2 clear space cache for v1 or v2",
+ " check and reporting options:",
+ " --check-data-csum verify checksums of data blocks",
+ " -Q|--qgroup-report print a report on qgroup consistency",
+ " -E|--subvol-extents <subvolid>",
+ " print subvolume extents and sharing state",
+ " -p|--progress indicate progress",
NULL
};
@@ -9559,14 +9676,16 @@ int cmd_check(int argc, char **argv)
if (repair && check_mode == CHECK_MODE_LOWMEM)
warning("low-memory mode repair support is only partial");
+ printf("Opening filesystem to check...\n");
+
radix_tree_init();
cache_tree_init(&root_cache);
ret = check_mounted(argv[optind]);
if (!force) {
if (ret < 0) {
- error("could not check mount status: %s",
- strerror(-ret));
+ errno = -ret;
+ error("could not check mount status: %m");
err |= !!ret;
goto err_out;
} else if (ret) {
@@ -9681,6 +9800,7 @@ int cmd_check(int argc, char **argv)
goto close_out;
}
+ trans->reinit_extent_tree = true;
if (init_extent_tree) {
printf("Creating a new extent tree\n");
ret = reinit_extent_tree(trans, info,
@@ -9692,7 +9812,7 @@ int cmd_check(int argc, char **argv)
if (init_csum_tree) {
printf("Reinitialize checksum tree\n");
- ret = btrfs_fsck_reinit_root(trans, info->csum_root, 0);
+ ret = btrfs_fsck_reinit_root(trans, info->csum_root);
if (ret) {
error("checksum tree initialization failed: %d",
ret);
@@ -9732,10 +9852,18 @@ int cmd_check(int argc, char **argv)
}
if (!init_extent_tree) {
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[1/7] checking root items\n");
+ } else {
+ ctx.tp = TASK_ROOT_ITEMS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
ret = repair_root_items(info);
+ task_stop(ctx.info);
if (ret < 0) {
err = !!ret;
- error("failed to repair root items: %s", strerror(-ret));
+ errno = -ret;
+ error("failed to repair root items: %m");
goto close_out;
}
if (repair) {
@@ -9751,9 +9879,18 @@ int cmd_check(int argc, char **argv)
err |= ret;
goto close_out;
}
+ } else {
+ fprintf(stderr, "[1/7] checking root items... skipped\n");
}
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[2/7] checking extents\n");
+ } else {
+ ctx.tp = TASK_EXTENTS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
ret = do_check_chunks_and_extents(info);
+ task_stop(ctx.info);
err |= !!ret;
if (ret)
error(
@@ -9762,21 +9899,21 @@ int cmd_check(int argc, char **argv)
/* Only re-check super size after we checked and repaired the fs */
err |= !is_super_size_valid(info);
+ is_free_space_tree = btrfs_fs_compat_ro(info, FREE_SPACE_TREE);
+
if (!ctx.progress_enabled) {
- if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
- fprintf(stderr, "checking free space tree\n");
+ if (is_free_space_tree)
+ fprintf(stderr, "[3/7] checking free space tree\n");
else
- fprintf(stderr, "checking free space cache\n");
+ fprintf(stderr, "[3/7] checking free space cache\n");
+ } else {
+ ctx.tp = TASK_FREE_SPACE;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
}
- ret = check_space_cache(root);
+
+ ret = validate_free_space_cache(root);
+ task_stop(ctx.info);
err |= !!ret;
- if (ret) {
- if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
- error("errors found in free space tree");
- else
- error("errors found in free space cache");
- goto out;
- }
/*
* We used to have to have these hole extents in between our real
@@ -9785,19 +9922,34 @@ int cmd_check(int argc, char **argv)
* ignore it when this happens.
*/
no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[4/7] checking fs roots\n");
+ } else {
+ ctx.tp = TASK_FS_ROOTS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
+
ret = do_check_fs_roots(info, &root_cache);
+ task_stop(ctx.info);
err |= !!ret;
if (ret) {
error("errors found in fs roots");
goto out;
}
- if (check_data_csum)
- fprintf(stderr, "checking csums against data\n");
- else
- fprintf(stderr,
- "checking only csum items (without verifying data)\n");
+ if (!ctx.progress_enabled) {
+ if (check_data_csum)
+ fprintf(stderr, "[5/7] checking csums against data\n");
+ else
+ fprintf(stderr,
+ "[5/7] checking only csums items (without verifying data)\n");
+ } else {
+ ctx.tp = TASK_CSUMS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
+
ret = check_csums(root);
+ task_stop(ctx.info);
/*
* Data csum error is not fatal, and it may indicate more serious
* corruption, continue checking.
@@ -9806,15 +9958,25 @@ int cmd_check(int argc, char **argv)
error("errors found in csum tree");
err |= !!ret;
- fprintf(stderr, "checking root refs\n");
/* For low memory mode, check_fs_roots_v2 handles root refs */
- if (check_mode != CHECK_MODE_LOWMEM) {
+ if (check_mode != CHECK_MODE_LOWMEM) {
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[6/7] checking root refs\n");
+ } else {
+ ctx.tp = TASK_ROOT_REFS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
+
ret = check_root_refs(root, &root_cache);
+ task_stop(ctx.info);
err |= !!ret;
if (ret) {
error("errors found in root refs");
goto out;
}
+ } else {
+ fprintf(stderr,
+ "[6/7] checking root refs done with fs roots in lowmem mode, skipping\n");
}
while (repair && !list_empty(&root->fs_info->recow_ebs)) {
@@ -9844,8 +10006,15 @@ int cmd_check(int argc, char **argv)
}
if (info->quota_enabled) {
- fprintf(stderr, "checking quota groups\n");
+ qgroup_set_item_count_ptr(&ctx.item_count);
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[7/7] checking quota groups\n");
+ } else {
+ ctx.tp = TASK_QGROUPS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
ret = qgroup_verify_all(info);
+ task_stop(ctx.info);
err |= !!ret;
if (ret) {
error("failed to check quota groups");
@@ -9860,6 +10029,9 @@ int cmd_check(int argc, char **argv)
if (qgroup_report_ret && (!qgroups_repaired || ret))
err |= qgroup_report_ret;
ret = 0;
+ } else {
+ fprintf(stderr,
+ "[7/7] checking quota groups skipped (not enabled on this FS)\n");
}
if (!list_empty(&root->fs_info->recow_ebs)) {