static u64 btree_space_waste = 0;
static u64 data_bytes_allocated = 0;
static u64 data_bytes_referenced = 0;
-static int found_old_backref = 0;
static LIST_HEAD(duplicate_extents);
static LIST_HEAD(delete_items);
static int no_holes = 0;
u64 last_snapshot;
u8 level;
u8 drop_level;
- int level_size;
struct btrfs_key drop_key;
};
for (i = slot; i < nritems; i++) {
bytenr = btrfs_node_blockptr(node, i);
ptr_gen = btrfs_node_ptr_generation(node, i);
- readahead_tree_block(fs_info, bytenr, fs_info->nodesize,
- ptr_gen);
+ readahead_tree_block(fs_info, bytenr, ptr_gen);
}
}
if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) {
free_extent_buffer(next);
reada_walk_down(root, cur, path->slots[*level]);
- next = read_tree_block(root->fs_info, bytenr,
- fs_info->nodesize, ptr_gen);
+ next = read_tree_block(root->fs_info, bytenr, ptr_gen);
if (!extent_buffer_uptodate(next)) {
struct btrfs_key node_key;
if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) {
free_extent_buffer(next);
reada_walk_down(root, cur, path->slots[*level]);
- next = read_tree_block(fs_info, bytenr,
- fs_info->nodesize, ptr_gen);
+ next = read_tree_block(fs_info, bytenr, ptr_gen);
if (!extent_buffer_uptodate(next)) {
struct btrfs_key node_key;
return is_fstree(objectid);
}
-static int check_fs_roots(struct btrfs_root *root,
+static int check_fs_roots(struct btrfs_fs_info *fs_info,
struct cache_tree *root_cache)
{
struct btrfs_path path;
struct walk_control wc;
struct extent_buffer *leaf, *tree_node;
struct btrfs_root *tmp_root;
- struct btrfs_root *tree_root = root->fs_info->tree_root;
+ struct btrfs_root *tree_root = fs_info->tree_root;
int ret;
int err = 0;
* reflected into the free space cache yet.
*/
if (repair)
- reset_cached_block_groups(root->fs_info);
+ reset_cached_block_groups(fs_info);
memset(&wc, 0, sizeof(wc));
cache_tree_init(&wc.shared);
btrfs_init_path(&path);
fs_root_objectid(key.objectid)) {
if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
tmp_root = btrfs_read_fs_root_no_cache(
- root->fs_info, &key);
+ fs_info, &key);
} else {
key.offset = (u64)-1;
tmp_root = btrfs_read_fs_root(
- root->fs_info, &key);
+ fs_info, &key);
}
if (IS_ERR(tmp_root)) {
err = 1;
return err;
}
+static int do_check_fs_roots(struct btrfs_fs_info *fs_info,
+ struct cache_tree *root_cache)
+{
+ int ret;
+
+ if (!ctx.progress_enabled)
+ fprintf(stderr, "checking fs roots\n");
+ if (check_mode == CHECK_MODE_LOWMEM)
+ ret = check_fs_roots_v2(fs_info);
+ else
+ ret = check_fs_roots(fs_info, root_cache);
+
+ return ret;
+}
+
static int all_backpointers_checked(struct extent_record *rec, int print_errs)
{
struct list_head *cur = rec->backrefs.next;
continue;
/* fixme, get the parent transid */
- readahead_tree_block(fs_info, bits[i].start,
- bits[i].size, 0);
+ readahead_tree_block(fs_info, bits[i].start, 0);
}
}
*last = bits[0].start;
}
/* fixme, get the real parent transid */
- buf = read_tree_block(root->fs_info, bytenr, size, gen);
+ buf = read_tree_block(root->fs_info, bytenr, gen);
if (!extent_buffer_uptodate(buf)) {
record_bad_block_io(root->fs_info,
extent_cache, bytenr, size);
total_fs_tree_bytes += buf->len;
if (btrfs_header_owner(buf) == BTRFS_EXTENT_TREE_OBJECTID)
total_extent_tree_bytes += buf->len;
- if (!found_old_backref &&
- btrfs_header_owner(buf) == BTRFS_TREE_RELOC_OBJECTID &&
- btrfs_header_backref_rev(buf) == BTRFS_MIXED_BACKREF_REV &&
- !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC))
- found_old_backref = 1;
out:
free_extent_buffer(buf);
return ret;
goto repair_abort;
}
- btrfs_fix_block_accounting(trans, root);
+ ret = btrfs_fix_block_accounting(trans, root);
+ if (ret)
+ goto repair_abort;
ret = btrfs_commit_transaction(trans, root);
if (ret)
goto repair_abort;
static int add_root_item_to_list(struct list_head *head,
u64 objectid, u64 bytenr, u64 last_snapshot,
u8 level, u8 drop_level,
- int level_size, struct btrfs_key *drop_key)
+ struct btrfs_key *drop_key)
{
struct root_item_record *ri_rec;
ri_rec->bytenr = bytenr;
ri_rec->objectid = objectid;
ri_rec->level = level;
- ri_rec->level_size = level_size;
ri_rec->drop_level = drop_level;
ri_rec->last_snapshot = last_snapshot;
if (drop_key)
rec = list_entry(list->next,
struct root_item_record, list);
last = 0;
- buf = read_tree_block(root->fs_info,
- rec->bytenr, rec->level_size, 0);
+ buf = read_tree_block(root->fs_info, rec->bytenr, 0);
if (!extent_buffer_uptodate(buf)) {
free_extent_buffer(buf);
ret = -EIO;
return ret;
}
-static int check_chunks_and_extents(struct btrfs_root *root)
+static int check_chunks_and_extents(struct btrfs_fs_info *fs_info)
{
struct rb_root dev_cache;
struct cache_tree chunk_cache;
struct list_head dropping_trees;
struct list_head normal_trees;
struct btrfs_root *root1;
+ struct btrfs_root *root;
u64 objectid;
- u32 level_size;
u8 level;
+ root = fs_info->fs_root;
dev_cache = RB_ROOT;
cache_tree_init(&chunk_cache);
block_group_tree_init(&block_group_cache);
INIT_LIST_HEAD(&normal_trees);
if (repair) {
- root->fs_info->excluded_extents = &excluded_extents;
- root->fs_info->fsck_extent_cache = &extent_cache;
- root->fs_info->free_extent_hook = free_extent_hook;
- root->fs_info->corrupt_blocks = &corrupt_blocks;
+ fs_info->excluded_extents = &excluded_extents;
+ fs_info->fsck_extent_cache = &extent_cache;
+ fs_info->free_extent_hook = free_extent_hook;
+ fs_info->corrupt_blocks = &corrupt_blocks;
}
bits_nr = 1024;
}
again:
- root1 = root->fs_info->tree_root;
+ root1 = fs_info->tree_root;
level = btrfs_header_level(root1->node);
ret = add_root_item_to_list(&normal_trees, root1->root_key.objectid,
- root1->node->start, 0, level, 0,
- root1->fs_info->nodesize, NULL);
+ root1->node->start, 0, level, 0, NULL);
if (ret < 0)
goto out;
- root1 = root->fs_info->chunk_root;
+ root1 = fs_info->chunk_root;
level = btrfs_header_level(root1->node);
ret = add_root_item_to_list(&normal_trees, root1->root_key.objectid,
- root1->node->start, 0, level, 0,
- root1->fs_info->nodesize, NULL);
+ 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, root->fs_info->tree_root,
- &key, &path, 0, 0);
+ ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0);
if (ret < 0)
goto out;
while(1) {
last_snapshot = btrfs_root_last_snapshot(&ri);
if (btrfs_disk_key_objectid(&ri.drop_progress) == 0) {
level = btrfs_root_level(&ri);
- level_size = root->fs_info->nodesize;
ret = add_root_item_to_list(&normal_trees,
found_key.objectid,
btrfs_root_bytenr(&ri),
last_snapshot, level,
- 0, level_size, NULL);
+ 0, NULL);
if (ret < 0)
goto out;
} else {
level = btrfs_root_level(&ri);
- level_size = root->fs_info->nodesize;
objectid = found_key.objectid;
btrfs_disk_key_to_cpu(&found_key,
&ri.drop_progress);
objectid,
btrfs_root_bytenr(&ri),
last_snapshot, level,
- ri.drop_level,
- level_size, &found_key);
+ ri.drop_level, &found_key);
if (ret < 0)
goto out;
}
out:
task_stop(ctx.info);
if (repair) {
- free_corrupt_blocks_tree(root->fs_info->corrupt_blocks);
+ free_corrupt_blocks_tree(fs_info->corrupt_blocks);
extent_io_tree_cleanup(&excluded_extents);
- root->fs_info->fsck_extent_cache = NULL;
- root->fs_info->free_extent_hook = NULL;
- root->fs_info->corrupt_blocks = NULL;
- root->fs_info->excluded_extents = NULL;
+ fs_info->fsck_extent_cache = NULL;
+ fs_info->free_extent_hook = NULL;
+ fs_info->corrupt_blocks = NULL;
+ fs_info->excluded_extents = NULL;
}
free(bits);
free_chunk_cache_tree(&chunk_cache);
free_root_item_list(&dropping_trees);
return ret;
loop:
- free_corrupt_blocks_tree(root->fs_info->corrupt_blocks);
+ free_corrupt_blocks_tree(fs_info->corrupt_blocks);
free_extent_cache_tree(&seen);
free_extent_cache_tree(&pending);
free_extent_cache_tree(&reada);
btrfs_release_path(&path);
/* Get level from tree block as an alternative source */
- eb = read_tree_block(fs_info, bytenr, fs_info->nodesize, transid);
+ eb = read_tree_block(fs_info, bytenr, transid);
if (!extent_buffer_uptodate(eb)) {
free_extent_buffer(eb);
return -EIO;
}
/* Read out the tree block to get item/node key */
- eb = read_tree_block(fs_info, bytenr, root->fs_info->nodesize, 0);
+ eb = read_tree_block(fs_info, bytenr, 0);
if (!extent_buffer_uptodate(eb)) {
err |= REFERENCER_MISSING;
free_extent_buffer(eb);
int found_parent = 0;
int i;
- eb = read_tree_block(fs_info, parent, fs_info->nodesize, 0);
+ eb = read_tree_block(fs_info, parent, 0);
if (!extent_buffer_uptodate(eb))
goto out;
int found_parent = 0;
int i;
- eb = read_tree_block(fs_info, parent, fs_info->nodesize, 0);
+ eb = read_tree_block(fs_info, parent, 0);
if (!extent_buffer_uptodate(eb))
goto out;
total_fs_tree_bytes += node->len;
if (btrfs_header_owner(node) == BTRFS_EXTENT_TREE_OBJECTID)
total_extent_tree_bytes += node->len;
- if (!found_old_backref &&
- btrfs_header_owner(node) == BTRFS_TREE_RELOC_OBJECTID &&
- btrfs_header_backref_rev(node) == BTRFS_MIXED_BACKREF_REV &&
- !btrfs_header_flag(node, BTRFS_HEADER_FLAG_RELOC))
- found_old_backref = 1;
/* pre-order tranversal, check itself first */
level = btrfs_header_level(node);
* As a btrfs tree has most 8 levels (0..7), so it's quite safe
* to call the function itself.
*/
- eb = read_tree_block(root->fs_info, blocknr,
- root->fs_info->nodesize, 0);
+ eb = read_tree_block(root->fs_info, blocknr, 0);
if (extent_buffer_uptodate(eb)) {
ret = traverse_tree_block(root, eb);
err |= ret;
/*
* Low memory usage version check_chunks_and_extents.
*/
-static int check_chunks_and_extents_v2(struct btrfs_root *root)
+static int check_chunks_and_extents_v2(struct btrfs_fs_info *fs_info)
{
struct btrfs_path path;
struct btrfs_key key;
struct btrfs_root *root1;
+ struct btrfs_root *root;
struct btrfs_root *cur_root;
int err = 0;
int ret;
+ root = fs_info->fs_root;
+
root1 = root->fs_info->chunk_root;
ret = traverse_tree_block(root1, root1->node);
err |= ret;
return err;
}
+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_v2(fs_info);
+ else
+ ret = check_chunks_and_extents(fs_info);
+
+ return ret;
+}
+
static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int overwrite)
{
* in, but for now this doesn't actually use the root so
* just pass in extent_root.
*/
- tmp = read_tree_block(fs_info, bytenr, fs_info->nodesize, 0);
+ tmp = read_tree_block(fs_info, bytenr, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr, "Error reading root block\n");
return -EIO;
continue;
}
- tmp = read_tree_block(fs_info, bytenr,
- fs_info->nodesize, 0);
+ tmp = read_tree_block(fs_info, bytenr, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr, "Error reading tree block\n");
return -EIO;
return ret;
}
+static int do_clear_free_space_cache(struct btrfs_fs_info *fs_info,
+ int clear_version)
+{
+ int ret = 0;
+
+ if (clear_version == 1) {
+ if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
+ error(
+ "free space cache v2 detected, use --clear-space-cache v2");
+ 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");
+ ret = 1;
+ } else {
+ printf("Free space cache cleared\n");
+ }
+ } else if (clear_version == 2) {
+ if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
+ printf("no free space cache v2 to clear\n");
+ ret = 0;
+ goto close_out;
+ }
+ printf("Clear free space cache v2\n");
+ ret = btrfs_clear_free_space_tree(fs_info);
+ if (ret) {
+ error("failed to clear free space cache v2: %d", ret);
+ ret = 1;
+ } else {
+ printf("free space cache v2 cleared\n");
+ }
+ }
+close_out:
+ return ret;
+}
+
const char * const cmd_check_usage[] = {
"btrfs check [options] <device>",
"Check structural integrity of a filesystem (unmounted).",
"",
"-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",
u64 tree_root_bytenr = 0;
u64 chunk_root_bytenr = 0;
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
- int ret;
+ int ret = 0;
int err = 0;
u64 num;
int init_csum_tree = 0;
int qgroup_report = 0;
int qgroups_repaired = 0;
unsigned ctree_flags = OPEN_CTREE_EXCLUSIVE;
+ int force = 0;
while(1) {
int c;
enum { GETOPT_VAL_REPAIR = 257, GETOPT_VAL_INIT_CSUM,
GETOPT_VAL_INIT_EXTENT, GETOPT_VAL_CHECK_CSUM,
GETOPT_VAL_READONLY, GETOPT_VAL_CHUNK_TREE,
- GETOPT_VAL_MODE, GETOPT_VAL_CLEAR_SPACE_CACHE };
+ GETOPT_VAL_MODE, GETOPT_VAL_CLEAR_SPACE_CACHE,
+ GETOPT_VAL_FORCE };
static const struct option long_options[] = {
{ "super", required_argument, NULL, 's' },
{ "repair", no_argument, NULL, GETOPT_VAL_REPAIR },
GETOPT_VAL_MODE },
{ "clear-space-cache", required_argument, NULL,
GETOPT_VAL_CLEAR_SPACE_CACHE},
+ { "force", no_argument, NULL, GETOPT_VAL_FORCE },
{ NULL, 0, NULL, 0}
};
}
ctree_flags |= OPEN_CTREE_WRITES;
break;
+ case GETOPT_VAL_FORCE:
+ force = 1;
+ break;
}
}
radix_tree_init();
cache_tree_init(&root_cache);
- if((ret = check_mounted(argv[optind])) < 0) {
- error("could not check mount status: %s", strerror(-ret));
- err |= !!ret;
- goto err_out;
- } else if(ret) {
- error("%s is currently mounted, aborting", argv[optind]);
- ret = -EBUSY;
- err |= !!ret;
- goto err_out;
+ ret = check_mounted(argv[optind]);
+ if (!force) {
+ if (ret < 0) {
+ error("could not check mount status: %s",
+ strerror(-ret));
+ err |= !!ret;
+ goto err_out;
+ } else if (ret) {
+ error(
+"%s is currently mounted, use --force if you really intend to check the filesystem",
+ argv[optind]);
+ ret = -EBUSY;
+ err |= !!ret;
+ goto err_out;
+ }
+ } else {
+ if (repair) {
+ error("repair and --force is not yet supported");
+ ret = 1;
+ err |= !!ret;
+ goto err_out;
+ }
+ if (ret < 0) {
+ warning(
+"cannot check mount status of %s, the filesystem could be mounted, continuing because of --force",
+ argv[optind]);
+ } else if (ret) {
+ warning(
+ "filesystem mounted, continuing because of --force");
+ }
}
/* only allow partial opening under repair mode */
global_info = info;
root = info->fs_root;
- if (clear_space_cache == 1) {
- if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
- error(
- "free space cache v2 detected, use --clear-space-cache v2");
- ret = 1;
- goto close_out;
- }
- printf("Clearing free space cache\n");
- ret = clear_free_space_cache(info);
- if (ret) {
- error("failed to clear free space cache");
- ret = 1;
- } else {
- printf("Free space cache cleared\n");
- }
+ uuid_unparse(info->super_copy->fsid, uuidbuf);
+
+ printf("Checking filesystem on %s\nUUID: %s\n", argv[optind], uuidbuf);
+
+ /*
+ * Check the bare minimum before starting anything else that could rely
+ * on it, namely the tree roots, any local consistency checks
+ */
+ if (!extent_buffer_uptodate(info->tree_root->node) ||
+ !extent_buffer_uptodate(info->dev_root->node) ||
+ !extent_buffer_uptodate(info->chunk_root->node)) {
+ error("critical roots corrupted, unable to check the filesystem");
+ err |= !!ret;
+ ret = -EIO;
goto close_out;
- } else if (clear_space_cache == 2) {
- if (!btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
- printf("no free space cache v2 to clear\n");
- ret = 0;
- goto close_out;
- }
- printf("Clear free space cache v2\n");
- ret = btrfs_clear_free_space_tree(info);
- if (ret) {
- error("failed to clear free space cache v2: %d", ret);
- ret = 1;
- } else {
- printf("free space cache v2 cleared\n");
- }
+ }
+
+ if (clear_space_cache) {
+ ret = do_clear_free_space_cache(info, clear_space_cache);
+ err |= !!ret;
goto close_out;
}
}
}
- uuid_unparse(info->super_copy->fsid, uuidbuf);
if (qgroup_report) {
printf("Print quota groups for %s\nUUID: %s\n", argv[optind],
uuidbuf);
err |= !!ret;
goto close_out;
}
- printf("Checking filesystem on %s\nUUID: %s\n", argv[optind], uuidbuf);
-
- if (!extent_buffer_uptodate(info->tree_root->node) ||
- !extent_buffer_uptodate(info->dev_root->node) ||
- !extent_buffer_uptodate(info->chunk_root->node)) {
- error("critical roots corrupted, unable to check the filesystem");
- err |= !!ret;
- ret = -EIO;
- goto close_out;
- }
if (init_extent_tree || init_csum_tree) {
struct btrfs_trans_handle *trans;
goto close_out;
}
- if (!ctx.progress_enabled)
- fprintf(stderr, "checking extents\n");
- if (check_mode == CHECK_MODE_LOWMEM)
- ret = check_chunks_and_extents_v2(root);
- else
- ret = check_chunks_and_extents(root);
+ ret = do_check_chunks_and_extents(info);
err |= !!ret;
if (ret)
error(
* ignore it when this happens.
*/
no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
- if (!ctx.progress_enabled)
- fprintf(stderr, "checking fs roots\n");
- if (check_mode == CHECK_MODE_LOWMEM)
- ret = check_fs_roots_v2(root->fs_info);
- else
- ret = check_fs_roots(root, &root_cache);
+ ret = do_check_fs_roots(info, &root_cache);
err |= !!ret;
if (ret) {
error("errors found in fs roots");
err |= !!ret;
}
out:
- if (found_old_backref) { /*
- * there was a disk format change when mixed
- * backref was in testing tree. The old format
- * existed about one week.
- */
- printf("\n * Found old mixed backref format. "
- "The old format is not supported! *"
- "\n * Please mount the FS in readonly mode, "
- "backup data and re-format the FS. *\n\n");
- err |= 1;
- }
printf("found %llu bytes used, ",
(unsigned long long)bytes_used);
if (err)