#include "utils.h"
#include "commands.h"
#include "free-space-cache.h"
+#include "free-space-tree.h"
#include "btrfsck.h"
#include "qgroup-verify.h"
#include "rbtree-utils.h"
static int check_data_csum = 0;
static struct btrfs_fs_info *global_info;
static struct task_ctx ctx = { 0 };
-
-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 */);
-
- 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);
- task_period_wait(priv->info);
- }
- return NULL;
-}
-
-static int print_status_return(void *p)
-{
- printf("\n");
- fflush(stdout);
-
- return 0;
-}
+static struct cache_tree *roots_info_cache = NULL;
struct extent_backref {
struct list_head list;
};
};
+/* Explicit initialization for extent_record::flag_block_full_backref */
+enum { FLAG_UNSET = 2 };
+
struct extent_record {
struct list_head backrefs;
struct list_head dups;
u64 info_objectid;
u32 num_duplicates;
u8 info_level;
- int flag_block_full_backref;
+ unsigned int flag_block_full_backref:2;
unsigned int found_rec:1;
unsigned int content_checked:1;
unsigned int owner_ref_checked:1;
u64 len;
};
+struct inode_record {
+ struct list_head backrefs;
+ unsigned int checked:1;
+ unsigned int merging:1;
+ unsigned int found_inode_item:1;
+ unsigned int found_dir_item:1;
+ unsigned int found_file_extent:1;
+ unsigned int found_csum_item:1;
+ unsigned int some_csum_missing:1;
+ unsigned int nodatasum:1;
+ int errors;
+
+ u64 ino;
+ u32 nlink;
+ u32 imode;
+ u64 isize;
+ u64 nbytes;
+
+ u32 found_link;
+ u64 found_size;
+ u64 extent_start;
+ u64 extent_end;
+ struct rb_root holes;
+ struct list_head orphan_extents;
+
+ u32 refs;
+};
+
+#define I_ERR_NO_INODE_ITEM (1 << 0)
+#define I_ERR_NO_ORPHAN_ITEM (1 << 1)
+#define I_ERR_DUP_INODE_ITEM (1 << 2)
+#define I_ERR_DUP_DIR_INDEX (1 << 3)
+#define I_ERR_ODD_DIR_ITEM (1 << 4)
+#define I_ERR_ODD_FILE_EXTENT (1 << 5)
+#define I_ERR_BAD_FILE_EXTENT (1 << 6)
+#define I_ERR_FILE_EXTENT_OVERLAP (1 << 7)
+#define I_ERR_FILE_EXTENT_DISCOUNT (1 << 8) // 100
+#define I_ERR_DIR_ISIZE_WRONG (1 << 9)
+#define I_ERR_FILE_NBYTES_WRONG (1 << 10) // 400
+#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)
+
+struct root_backref {
+ struct list_head list;
+ unsigned int found_dir_item:1;
+ unsigned int found_dir_index:1;
+ unsigned int found_back_ref:1;
+ unsigned int found_forward_ref:1;
+ unsigned int reachable:1;
+ int errors;
+ u64 ref_root;
+ u64 dir;
+ u64 index;
+ u16 namelen;
+ char name[0];
+};
+
+struct root_record {
+ struct list_head backrefs;
+ struct cache_extent cache;
+ unsigned int found_root_item:1;
+ u64 objectid;
+ u32 found_ref;
+};
+
+struct ptr_node {
+ struct cache_extent cache;
+ void *data;
+};
+
+struct shared_node {
+ struct cache_extent cache;
+ struct cache_tree root_cache;
+ struct cache_tree inode_cache;
+ struct inode_record *current;
+ u32 refs;
+};
+
+struct block_info {
+ u64 start;
+ u32 size;
+};
+
+struct walk_control {
+ struct cache_tree shared;
+ struct shared_node *nodes[BTRFS_MAX_LEVEL];
+ int active_node;
+ int root_level;
+};
+
+struct bad_item {
+ struct btrfs_key key;
+ u64 root_id;
+ struct list_head list;
+};
+
+struct extent_entry {
+ u64 bytenr;
+ u64 bytes;
+ int count;
+ int broken;
+ struct list_head list;
+};
+
+struct root_item_info {
+ /* level of the root */
+ u8 level;
+ /* number of nodes at this level, must be 1 for a root */
+ int node_count;
+ u64 bytenr;
+ u64 gen;
+ struct cache_extent cache_extent;
+};
+
+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 */);
+
+ 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);
+ task_period_wait(priv->info);
+ }
+ return NULL;
+}
+
+static int print_status_return(void *p)
+{
+ printf("\n");
+ fflush(stdout);
+
+ return 0;
+}
+
/* Compatible function to allow reuse of old codes */
static u64 first_extent_gap(struct rb_root *holes)
{
return -EEXIST;
/*
- * Now there will be no overflap, delete the hole and re-add the
+ * Now there will be no overlap, delete the hole and re-add the
* split(s) if they exists.
*/
if (start > hole->start) {
}
}
-struct inode_record {
- struct list_head backrefs;
- unsigned int checked:1;
- unsigned int merging:1;
- unsigned int found_inode_item:1;
- unsigned int found_dir_item:1;
- unsigned int found_file_extent:1;
- unsigned int found_csum_item:1;
- unsigned int some_csum_missing:1;
- unsigned int nodatasum:1;
- int errors;
-
- u64 ino;
- u32 nlink;
- u32 imode;
- u64 isize;
- u64 nbytes;
-
- u32 found_link;
- u64 found_size;
- u64 extent_start;
- u64 extent_end;
- struct rb_root holes;
- struct list_head orphan_extents;
-
- u32 refs;
-};
-
-#define I_ERR_NO_INODE_ITEM (1 << 0)
-#define I_ERR_NO_ORPHAN_ITEM (1 << 1)
-#define I_ERR_DUP_INODE_ITEM (1 << 2)
-#define I_ERR_DUP_DIR_INDEX (1 << 3)
-#define I_ERR_ODD_DIR_ITEM (1 << 4)
-#define I_ERR_ODD_FILE_EXTENT (1 << 5)
-#define I_ERR_BAD_FILE_EXTENT (1 << 6)
-#define I_ERR_FILE_EXTENT_OVERLAP (1 << 7)
-#define I_ERR_FILE_EXTENT_DISCOUNT (1 << 8) // 100
-#define I_ERR_DIR_ISIZE_WRONG (1 << 9)
-#define I_ERR_FILE_NBYTES_WRONG (1 << 10) // 400
-#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)
-
-struct root_backref {
- struct list_head list;
- unsigned int found_dir_item:1;
- unsigned int found_dir_index:1;
- unsigned int found_back_ref:1;
- unsigned int found_forward_ref:1;
- unsigned int reachable:1;
- int errors;
- u64 ref_root;
- u64 dir;
- u64 index;
- u16 namelen;
- char name[0];
-};
-
-struct root_record {
- struct list_head backrefs;
- struct cache_extent cache;
- unsigned int found_root_item:1;
- u64 objectid;
- u32 found_ref;
-};
-
-struct ptr_node {
- struct cache_extent cache;
- void *data;
-};
-
-struct shared_node {
- struct cache_extent cache;
- struct cache_tree root_cache;
- struct cache_tree inode_cache;
- struct inode_record *current;
- u32 refs;
-};
-
-struct block_info {
- u64 start;
- u32 size;
-};
-
-struct walk_control {
- struct cache_tree shared;
- struct shared_node *nodes[BTRFS_MAX_LEVEL];
- int active_node;
- int root_level;
-};
-
-struct bad_item {
- struct btrfs_key key;
- u64 root_id;
- struct list_head list;
-};
-
static void reset_cached_block_groups(struct btrfs_fs_info *fs_info);
static void record_root_in_trans(struct btrfs_trans_handle *trans,
if (errors & REF_ERR_DUP_INODE_REF)
fprintf(stderr, ", dup inode ref");
if (errors & REF_ERR_INDEX_UNMATCH)
- fprintf(stderr, ", index unmatch");
+ fprintf(stderr, ", index mismatch");
if (errors & REF_ERR_FILETYPE_UNMATCH)
- fprintf(stderr, ", filetype unmatch");
+ fprintf(stderr, ", filetype mismatch");
if (errors & REF_ERR_NAME_TOO_LONG)
fprintf(stderr, ", name too long");
if (errors & REF_ERR_NO_ROOT_REF)
return;
nritems = btrfs_header_nritems(node);
- blocksize = btrfs_level_size(root, level - 1);
+ blocksize = root->nodesize;
for (i = slot; i < nritems; i++) {
bytenr = btrfs_node_blockptr(node, i);
ptr_gen = btrfs_node_ptr_generation(node, i);
}
bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
- blocksize = btrfs_level_size(root, *level - 1);
+ blocksize = root->nodesize;
ret = btrfs_lookup_extent_info(NULL, root, bytenr, *level - 1,
1, &refs, NULL);
if (ret < 0)
btrfs_add_corrupt_extent_record(root->fs_info,
&node_key,
path->nodes[*level]->start,
- root->leafsize, *level);
+ root->nodesize, *level);
err = -EIO;
goto out;
}
type_recovered = 1;
filetype = BTRFS_FT_REG_FILE;
} else{
- printf("Can't determint the filetype for inode %llu, assume it is a normal file\n",
+ printf("Can't determine the filetype for inode %llu, assume it is a normal file\n",
rec->ino);
type_recovered = 1;
filetype = BTRFS_FT_REG_FILE;
/*
* We need to record the highest inode number for later 'lost+found'
* dir creation.
- * We must select a ino not used/refered by any existing inode, or
+ * We must select an ino not used/referred by any existing inode, or
* 'lost+found' ino may be a missing ino in a corrupted leaf,
* this may cause 'lost+found' dir has wrong nlinks.
*/
} else {
/*
* Signal to callers we need to start the scan over
- * again since we'll have cow'ed blocks.
+ * again since we'll have cowed blocks.
*/
ret = -EAGAIN;
}
u64 parent, u64 root)
{
struct tree_backref *ref = malloc(sizeof(*ref));
+
+ if (!ref)
+ return NULL;
memset(&ref->node, 0, sizeof(ref->node));
if (parent > 0) {
ref->parent = parent;
u64 max_size)
{
struct data_backref *ref = malloc(sizeof(*ref));
+
+ if (!ref)
+ return NULL;
memset(&ref->node, 0, sizeof(ref->node));
ref->node.is_data = 1;
}
}
+/*
+ * Allocate a new extent record, fill default values from @tmpl and insert int
+ * @extent_cache. Caller is supposed to make sure the [start,nr) is not in
+ * the cache, otherwise it fails.
+ */
+static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
+ struct extent_record *tmpl)
+{
+ struct extent_record *rec;
+ int ret = 0;
+
+ rec = malloc(sizeof(*rec));
+ if (!rec)
+ return -ENOMEM;
+ rec->start = tmpl->start;
+ rec->max_size = tmpl->max_size;
+ rec->nr = max(tmpl->nr, tmpl->max_size);
+ rec->found_rec = tmpl->found_rec;
+ rec->content_checked = tmpl->content_checked;
+ rec->owner_ref_checked = tmpl->owner_ref_checked;
+ rec->num_duplicates = 0;
+ rec->metadata = tmpl->metadata;
+ rec->flag_block_full_backref = FLAG_UNSET;
+ rec->bad_full_backref = 0;
+ rec->crossing_stripes = 0;
+ rec->wrong_chunk_type = 0;
+ rec->is_root = tmpl->is_root;
+ rec->refs = tmpl->refs;
+ rec->extent_item_refs = tmpl->extent_item_refs;
+ rec->parent_generation = tmpl->parent_generation;
+ INIT_LIST_HEAD(&rec->backrefs);
+ INIT_LIST_HEAD(&rec->dups);
+ INIT_LIST_HEAD(&rec->list);
+ memcpy(&rec->parent_key, &tmpl->parent_key, sizeof(tmpl->parent_key));
+ rec->cache.start = tmpl->start;
+ rec->cache.size = tmpl->nr;
+ ret = insert_cache_extent(extent_cache, &rec->cache);
+ BUG_ON(ret);
+ bytes_used += rec->nr;
+
+ if (tmpl->metadata)
+ rec->crossing_stripes = check_crossing_stripes(rec->start,
+ global_info->tree_root->nodesize);
+ check_extent_type(rec);
+ return ret;
+}
+
+/*
+ * Lookup and modify an extent, some values of @tmpl are interpreted verbatim,
+ * some are hints:
+ * - refs - if found, increase refs
+ * - is_root - if found, set
+ * - content_checked - if found, set
+ * - owner_ref_checked - if found, set
+ *
+ * If not found, create a new one, initialize and insert.
+ */
static int add_extent_rec(struct cache_tree *extent_cache,
- struct btrfs_key *parent_key, u64 parent_gen,
- u64 start, u64 nr, u64 extent_item_refs,
- int is_root, int inc_ref, int set_checked,
- int metadata, int extent_rec, u64 max_size)
+ struct extent_record *tmpl)
{
struct extent_record *rec;
struct cache_extent *cache;
int ret = 0;
int dup = 0;
- cache = lookup_cache_extent(extent_cache, start, nr);
+ cache = lookup_cache_extent(extent_cache, tmpl->start, tmpl->nr);
if (cache) {
rec = container_of(cache, struct extent_record, cache);
- if (inc_ref)
+ if (tmpl->refs)
rec->refs++;
if (rec->nr == 1)
- rec->nr = max(nr, max_size);
+ rec->nr = max(tmpl->nr, tmpl->max_size);
/*
* We need to make sure to reset nr to whatever the extent
* record says was the real size, this way we can compare it to
* the backrefs.
*/
- if (extent_rec) {
- if (start != rec->start || rec->found_rec) {
+ if (tmpl->found_rec) {
+ if (tmpl->start != rec->start || rec->found_rec) {
struct extent_record *tmp;
dup = 1;
tmp = malloc(sizeof(*tmp));
if (!tmp)
return -ENOMEM;
- tmp->start = start;
- tmp->max_size = max_size;
- tmp->nr = nr;
+ tmp->start = tmpl->start;
+ tmp->max_size = tmpl->max_size;
+ tmp->nr = tmpl->nr;
tmp->found_rec = 1;
- tmp->metadata = metadata;
- tmp->extent_item_refs = extent_item_refs;
+ tmp->metadata = tmpl->metadata;
+ tmp->extent_item_refs = tmpl->extent_item_refs;
INIT_LIST_HEAD(&tmp->list);
list_add_tail(&tmp->list, &rec->dups);
rec->num_duplicates++;
} else {
- rec->nr = nr;
+ rec->nr = tmpl->nr;
rec->found_rec = 1;
}
}
- if (extent_item_refs && !dup) {
+ if (tmpl->extent_item_refs && !dup) {
if (rec->extent_item_refs) {
fprintf(stderr, "block %llu rec "
"extent_item_refs %llu, passed %llu\n",
- (unsigned long long)start,
+ (unsigned long long)tmpl->start,
(unsigned long long)
rec->extent_item_refs,
- (unsigned long long)extent_item_refs);
+ (unsigned long long)tmpl->extent_item_refs);
}
- rec->extent_item_refs = extent_item_refs;
+ rec->extent_item_refs = tmpl->extent_item_refs;
}
- if (is_root)
+ if (tmpl->is_root)
rec->is_root = 1;
- if (set_checked) {
+ if (tmpl->content_checked)
rec->content_checked = 1;
+ if (tmpl->owner_ref_checked)
rec->owner_ref_checked = 1;
- }
-
- if (parent_key)
- btrfs_cpu_key_to_disk(&rec->parent_key, parent_key);
- if (parent_gen)
- rec->parent_generation = parent_gen;
-
- if (rec->max_size < max_size)
- rec->max_size = max_size;
+ memcpy(&rec->parent_key, &tmpl->parent_key,
+ sizeof(tmpl->parent_key));
+ if (tmpl->parent_generation)
+ rec->parent_generation = tmpl->parent_generation;
+ if (rec->max_size < tmpl->max_size)
+ rec->max_size = tmpl->max_size;
/*
* A metadata extent can't cross stripe_len boundary, otherwise
* As now stripe_len is fixed to BTRFS_STRIPE_LEN, just check
* it.
*/
- if (metadata && check_crossing_stripes(rec->start,
- rec->max_size))
- rec->crossing_stripes = 1;
+ if (tmpl->metadata)
+ rec->crossing_stripes = check_crossing_stripes(
+ rec->start, global_info->tree_root->nodesize);
check_extent_type(rec);
maybe_free_extent_rec(extent_cache, rec);
return ret;
}
- rec = malloc(sizeof(*rec));
- rec->start = start;
- rec->max_size = max_size;
- rec->nr = max(nr, max_size);
- rec->found_rec = !!extent_rec;
- rec->content_checked = 0;
- rec->owner_ref_checked = 0;
- rec->num_duplicates = 0;
- rec->metadata = metadata;
- rec->flag_block_full_backref = -1;
- rec->bad_full_backref = 0;
- rec->crossing_stripes = 0;
- rec->wrong_chunk_type = 0;
- INIT_LIST_HEAD(&rec->backrefs);
- INIT_LIST_HEAD(&rec->dups);
- INIT_LIST_HEAD(&rec->list);
-
- if (is_root)
- rec->is_root = 1;
- else
- rec->is_root = 0;
-
- if (inc_ref)
- rec->refs = 1;
- else
- rec->refs = 0;
-
- if (extent_item_refs)
- rec->extent_item_refs = extent_item_refs;
- else
- rec->extent_item_refs = 0;
-
- if (parent_key)
- btrfs_cpu_key_to_disk(&rec->parent_key, parent_key);
- else
- memset(&rec->parent_key, 0, sizeof(*parent_key));
- if (parent_gen)
- rec->parent_generation = parent_gen;
- else
- rec->parent_generation = 0;
-
- rec->cache.start = start;
- rec->cache.size = nr;
- ret = insert_cache_extent(extent_cache, &rec->cache);
- BUG_ON(ret);
- bytes_used += nr;
- if (set_checked) {
- rec->content_checked = 1;
- rec->owner_ref_checked = 1;
- }
+ ret = add_extent_rec_nolookup(extent_cache, tmpl);
- if (metadata)
- if (check_crossing_stripes(rec->start, rec->max_size))
- rec->crossing_stripes = 1;
- check_extent_type(rec);
return ret;
}
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache) {
- add_extent_rec(extent_cache, NULL, 0, bytenr,
- 1, 0, 0, 0, 0, 1, 0, 0);
+ struct extent_record tmpl;
+
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = bytenr;
+ tmpl.nr = 1;
+ tmpl.metadata = 1;
+
+ add_extent_rec_nolookup(extent_cache, &tmpl);
+
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache)
abort();
}
back = find_tree_backref(rec, parent, root);
- if (!back)
+ if (!back) {
back = alloc_tree_backref(rec, parent, root);
+ BUG_ON(!back);
+ }
if (found_ref) {
if (back->node.found_ref) {
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache) {
- add_extent_rec(extent_cache, NULL, 0, bytenr, 1, 0, 0, 0, 0,
- 0, 0, max_size);
+ struct extent_record tmpl;
+
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = bytenr;
+ tmpl.nr = 1;
+ tmpl.max_size = max_size;
+
+ add_extent_rec_nolookup(extent_cache, &tmpl);
+
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache)
abort();
*/
back = find_data_backref(rec, parent, root, owner, offset, found_ref,
bytenr, max_size);
- if (!back)
+ if (!back) {
back = alloc_data_backref(rec, parent, root, owner, offset,
max_size);
+ BUG_ON(!back);
+ }
if (found_ref) {
BUG_ON(num_refs != 1);
struct btrfs_extent_data_ref *dref;
struct btrfs_shared_data_ref *sref;
struct btrfs_key key;
+ struct extent_record tmpl;
unsigned long end;
unsigned long ptr;
int type;
if (key.type == BTRFS_METADATA_ITEM_KEY) {
metadata = 1;
- num_bytes = root->leafsize;
+ num_bytes = root->nodesize;
} else {
num_bytes = key.offset;
}
#else
BUG();
#endif
- return add_extent_rec(extent_cache, NULL, 0, key.objectid,
- num_bytes, refs, 0, 0, 0, metadata, 1,
- num_bytes);
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = key.objectid;
+ tmpl.nr = num_bytes;
+ tmpl.extent_item_refs = refs;
+ tmpl.metadata = metadata;
+ tmpl.found_rec = 1;
+ tmpl.max_size = num_bytes;
+
+ return add_extent_rec(extent_cache, &tmpl);
}
ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
else
metadata = 0;
- add_extent_rec(extent_cache, NULL, 0, key.objectid, num_bytes,
- refs, 0, 0, 0, metadata, 1, num_bytes);
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = key.objectid;
+ tmpl.nr = num_bytes;
+ tmpl.extent_item_refs = refs;
+ tmpl.metadata = metadata;
+ tmpl.found_rec = 1;
+ tmpl.max_size = num_bytes;
+ add_extent_rec(extent_cache, &tmpl);
ptr = (unsigned long)(ei + 1);
if (btrfs_extent_flags(eb, ei) & BTRFS_EXTENT_FLAG_TREE_BLOCK &&
if (key.type == BTRFS_EXTENT_ITEM_KEY)
last = key.objectid + key.offset;
else
- last = key.objectid + root->leafsize;
+ last = key.objectid + root->nodesize;
path->slots[0]++;
continue;
}
if (key.type == BTRFS_EXTENT_ITEM_KEY)
last = key.objectid + key.offset;
else
- last = key.objectid + root->leafsize;
+ last = key.objectid + root->nodesize;
path->slots[0]++;
}
btrfs_remove_free_space_cache(cache);
}
- ret = load_free_space_cache(root->fs_info, cache);
- if (!ret)
- continue;
+ if (btrfs_fs_compat_ro(root->fs_info,
+ BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) {
+ ret = exclude_super_stripes(root, cache);
+ if (ret) {
+ fprintf(stderr, "could not exclude super stripes: %s\n",
+ strerror(-ret));
+ 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));
+ error++;
+ continue;
+ }
+ error += ret;
+ } else {
+ ret = load_free_space_cache(root->fs_info, cache);
+ if (!ret)
+ continue;
+ }
ret = verify_space_cache(root, cache);
if (ret) {
- fprintf(stderr, "cache appears valid but isnt %Lu\n",
+ fprintf(stderr, "cache appears valid but isn't %Lu\n",
cache->key.objectid);
error++;
}
path = btrfs_alloc_path();
if (!path) {
- fprintf(stderr, "Error allocing path\n");
+ fprintf(stderr, "Error allocating path\n");
return -ENOMEM;
}
/*
* Block group items come before extent items if they have the same
- * bytenr, so walk back one more just in case. Dear future traveler,
+ * bytenr, so walk back one more just in case. Dear future traveller,
* first congrats on mastering time travel. Now if it's not too much
* trouble could you go back to 2006 and tell Chris to make the
* BLOCK_GROUP_ITEM_KEY (and BTRFS_*_REF_KEY) lower than the
* 1) If BTRFS_HEADER_FLAG_RELOC is set then we have FULL_BACKREF set.
* 2) If btrfs_header_owner(buf) no longer points to buf then we have
* FULL_BACKREF set.
- * 3) We cow'ed the block walking down a reloc tree. This is impossible to tell
+ * 3) We cowed the block walking down a reloc tree. This is impossible to tell
* if it happened after the relocation occurred since we'll have dropped the
* reloc root, so it's entirely possible to have FULL_BACKREF set on buf and
* have no real way to know for sure.
goto full_backref;
normal:
*flags = 0;
- if (rec->flag_block_full_backref != -1 &&
+ if (rec->flag_block_full_backref != FLAG_UNSET &&
rec->flag_block_full_backref != 0)
rec->bad_full_backref = 1;
return 0;
full_backref:
*flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
- if (rec->flag_block_full_backref != -1 &&
+ if (rec->flag_block_full_backref != FLAG_UNSET &&
rec->flag_block_full_backref != 1)
rec->bad_full_backref = 1;
return 0;
btrfs_item_key_to_cpu(buf, &first_key, 0);
level = btrfs_header_level(buf);
for (i = 0; i < nritems; i++) {
+ struct extent_record tmpl;
+
ptr = btrfs_node_blockptr(buf, i);
- size = btrfs_level_size(root, level - 1);
+ size = root->nodesize;
btrfs_node_key_to_cpu(buf, &key, i);
if (ri != NULL) {
if ((level == ri->drop_level)
continue;
}
}
- ret = add_extent_rec(extent_cache, &key,
- btrfs_node_ptr_generation(buf, i),
- ptr, size, 0, 0, 1, 0, 1, 0,
- size);
+
+ memset(&tmpl, 0, sizeof(tmpl));
+ btrfs_cpu_key_to_disk(&tmpl.parent_key, &key);
+ tmpl.parent_generation = btrfs_node_ptr_generation(buf, i);
+ tmpl.start = ptr;
+ tmpl.nr = size;
+ tmpl.refs = 1;
+ tmpl.metadata = 1;
+ tmpl.max_size = size;
+ ret = add_extent_rec(extent_cache, &tmpl);
BUG_ON(ret);
add_tree_backref(extent_cache, ptr, parent, owner, 1);
struct cache_tree *nodes,
u64 objectid)
{
+ struct extent_record tmpl;
+
if (btrfs_header_level(buf) > 0)
add_pending(nodes, seen, buf->start, buf->len);
else
add_pending(pending, seen, buf->start, buf->len);
- add_extent_rec(extent_cache, NULL, 0, buf->start, buf->len,
- 0, 1, 1, 0, 1, 0, buf->len);
+
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = buf->start;
+ tmpl.nr = buf->len;
+ tmpl.is_root = 1;
+ tmpl.refs = 1;
+ tmpl.metadata = 1;
+ tmpl.max_size = buf->len;
+ add_extent_rec(extent_cache, &tmpl);
if (objectid == BTRFS_TREE_RELOC_OBJECTID ||
btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV)
if (found_key.type == BTRFS_EXTENT_ITEM_KEY ||
found_key.type == BTRFS_METADATA_ITEM_KEY) {
u64 bytes = (found_key.type == BTRFS_EXTENT_ITEM_KEY) ?
- found_key.offset : root->leafsize;
+ found_key.offset : root->nodesize;
ret = btrfs_update_block_group(trans, root, bytenr,
bytes, 0, 0);
if (!back->is_data)
rec->max_size = max_t(u64, rec->max_size,
- info->extent_root->leafsize);
+ info->extent_root->nodesize);
if (!allocated) {
u32 item_size = sizeof(*ei);
return ret;
}
-struct extent_entry {
- u64 bytenr;
- u64 bytes;
- int count;
- int broken;
- struct list_head list;
-};
-
static struct extent_entry *find_entry(struct list_head *entries,
u64 bytenr, u64 bytes)
{
if (tmp->start + tmp->nr < good->start + good->nr) {
fprintf(stderr, "Ok we have overlapping extents that "
- "aren't completely covered by eachother, this "
+ "aren't completely covered by each other, this "
"is going to require more careful thought. "
"The extents are [%Lu-%Lu] and [%Lu-%Lu]\n",
tmp->start, tmp->nr, good->start, good->nr);
level = btrfs_header_level(root1->node);
ret = add_root_item_to_list(&normal_trees, root1->root_key.objectid,
root1->node->start, 0, level, 0,
- btrfs_level_size(root1, level), NULL);
+ root1->nodesize, NULL);
if (ret < 0)
goto out;
root1 = root->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,
- btrfs_level_size(root1, level), NULL);
+ root1->nodesize, NULL);
if (ret < 0)
goto out;
btrfs_init_path(&path);
last_snapshot = btrfs_root_last_snapshot(&ri);
if (btrfs_disk_key_objectid(&ri.drop_progress) == 0) {
level = btrfs_root_level(&ri);
- level_size = btrfs_level_size(root, level);
+ level_size = root->nodesize;
ret = add_root_item_to_list(&normal_trees,
found_key.objectid,
btrfs_root_bytenr(&ri),
goto out;
} else {
level = btrfs_root_level(&ri);
- level_size = btrfs_level_size(root, level);
+ level_size = root->nodesize;
objectid = found_key.objectid;
btrfs_disk_key_to_cpu(&found_key,
&ri.drop_progress);
goto init;
}
c = btrfs_alloc_free_block(trans, root,
- btrfs_level_size(root, 0),
+ root->nodesize,
root->root_key.objectid,
&disk_key, level, 0, 0);
if (IS_ERR(c)) {
struct btrfs_root_item *ri;
struct btrfs_key key;
u64 bytenr;
- u32 leafsize;
+ u32 nodesize;
int level = btrfs_header_level(eb);
int nritems;
int ret;
btrfs_pin_extent(fs_info, eb->start, eb->len);
- leafsize = btrfs_super_leafsize(fs_info->super_copy);
+ nodesize = btrfs_super_nodesize(fs_info->super_copy);
nritems = btrfs_header_nritems(eb);
for (i = 0; i < nritems; i++) {
if (level == 0) {
* just pass in extent_root.
*/
tmp = read_tree_block(fs_info->extent_root, bytenr,
- leafsize, 0);
+ nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr, "Error reading root block\n");
return -EIO;
/* If we aren't the tree root don't read the block */
if (level == 1 && !tree_root) {
- btrfs_pin_extent(fs_info, bytenr, leafsize);
+ btrfs_pin_extent(fs_info, bytenr, nodesize);
continue;
}
tmp = read_tree_block(fs_info->extent_root, bytenr,
- leafsize, 0);
+ nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr, "Error reading tree block\n");
return -EIO;
ret = reset_balance(trans, fs_info);
if (ret)
- fprintf(stderr, "error reseting the pending balance\n");
+ fprintf(stderr, "error resetting the pending balance\n");
return ret;
}
return fill_csum_tree_from_extent(trans, csum_root);
}
-struct root_item_info {
- /* level of the root */
- u8 level;
- /* number of nodes at this level, must be 1 for a root */
- int node_count;
- u64 bytenr;
- u64 gen;
- struct cache_extent cache_extent;
-};
-
-static struct cache_tree *roots_info_cache = NULL;
-
static void free_roots_info_cache(void)
{
if (!roots_info_cache)
iref = (struct btrfs_extent_inline_ref *)(ei + 1);
level = found_key.offset;
} else {
- struct btrfs_tree_block_info *info;
+ struct btrfs_tree_block_info *binfo;
- info = (struct btrfs_tree_block_info *)(ei + 1);
- iref = (struct btrfs_extent_inline_ref *)(info + 1);
- level = btrfs_tree_block_level(leaf, info);
+ binfo = (struct btrfs_tree_block_info *)(ei + 1);
+ iref = (struct btrfs_extent_inline_ref *)(binfo + 1);
+ level = btrfs_tree_block_level(leaf, binfo);
}
/*
const char * const cmd_check_usage[] = {
"btrfs check [options] <device>",
- "Check structural inegrity of a filesystem (unmounted).",
- "Check structural inegrity of an unmounted filesystem. Verify internal",
+ "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",
"",
"-s|--super <superblock> use this superblock copy",
- "-b|--backup use the backup root copy",
+ "-b|--backup use the first valid backup root copy",
"--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",
- "--check-data-csum verify checkums of data blocks",
+ "--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",
NULL
};
u64 bytenr = 0;
u64 subvolid = 0;
u64 tree_root_bytenr = 0;
+ u64 chunk_root_bytenr = 0;
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
int ret;
u64 num;
while(1) {
int c;
- enum { OPT_REPAIR = 257, OPT_INIT_CSUM, OPT_INIT_EXTENT,
- OPT_CHECK_CSUM, OPT_READONLY };
+ enum { GETOPT_VAL_REPAIR = 257, GETOPT_VAL_INIT_CSUM,
+ GETOPT_VAL_INIT_EXTENT, GETOPT_VAL_CHECK_CSUM,
+ GETOPT_VAL_READONLY, GETOPT_VAL_CHUNK_TREE };
static const struct option long_options[] = {
{ "super", required_argument, NULL, 's' },
- { "repair", no_argument, NULL, OPT_REPAIR },
- { "readonly", no_argument, NULL, OPT_READONLY },
- { "init-csum-tree", no_argument, NULL, OPT_INIT_CSUM },
- { "init-extent-tree", no_argument, NULL, OPT_INIT_EXTENT },
- { "check-data-csum", no_argument, NULL, OPT_CHECK_CSUM },
+ { "repair", no_argument, NULL, GETOPT_VAL_REPAIR },
+ { "readonly", no_argument, NULL, GETOPT_VAL_READONLY },
+ { "init-csum-tree", no_argument, NULL,
+ GETOPT_VAL_INIT_CSUM },
+ { "init-extent-tree", no_argument, NULL,
+ GETOPT_VAL_INIT_EXTENT },
+ { "check-data-csum", no_argument, NULL,
+ GETOPT_VAL_CHECK_CSUM },
{ "backup", no_argument, NULL, 'b' },
{ "subvol-extents", required_argument, NULL, 'E' },
{ "qgroup-report", no_argument, NULL, 'Q' },
{ "tree-root", required_argument, NULL, 'r' },
+ { "chunk-root", required_argument, NULL,
+ GETOPT_VAL_CHUNK_TREE },
{ "progress", no_argument, NULL, 'p' },
{ NULL, 0, NULL, 0}
};
case 'r':
tree_root_bytenr = arg_strtou64(optarg);
break;
+ case GETOPT_VAL_CHUNK_TREE:
+ chunk_root_bytenr = arg_strtou64(optarg);
+ break;
case 'p':
ctx.progress_enabled = true;
break;
case '?':
case 'h':
usage(cmd_check_usage);
- case OPT_REPAIR:
+ case GETOPT_VAL_REPAIR:
printf("enabling repair mode\n");
repair = 1;
ctree_flags |= OPEN_CTREE_WRITES;
break;
- case OPT_READONLY:
+ case GETOPT_VAL_READONLY:
readonly = 1;
break;
- case OPT_INIT_CSUM:
+ case GETOPT_VAL_INIT_CSUM:
printf("Creating a new CRC tree\n");
init_csum_tree = 1;
repair = 1;
ctree_flags |= OPEN_CTREE_WRITES;
break;
- case OPT_INIT_EXTENT:
+ case GETOPT_VAL_INIT_EXTENT:
init_extent_tree = 1;
ctree_flags |= (OPEN_CTREE_WRITES |
OPEN_CTREE_NO_BLOCK_GROUPS);
repair = 1;
break;
- case OPT_CHECK_CSUM:
+ case GETOPT_VAL_CHECK_CSUM:
check_data_csum = 1;
break;
}
}
- argc = argc - optind;
- if (check_argc_exact(argc, 1))
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_check_usage);
if (ctx.progress_enabled) {
ctree_flags |= OPEN_CTREE_PARTIAL;
info = open_ctree_fs_info(argv[optind], bytenr, tree_root_bytenr,
- ctree_flags);
+ chunk_root_bytenr, ctree_flags);
if (!info) {
fprintf(stderr, "Couldn't open file system\n");
ret = -EIO;
uuidbuf);
ret = qgroup_verify_all(info);
if (ret == 0)
- print_qgroup_report(1);
+ ret = report_qgroups(1);
goto close_out;
}
if (subvolid) {
goto close_out;
}
- if (!ctx.progress_enabled)
- fprintf(stderr, "checking free space cache\n");
+ if (!ctx.progress_enabled) {
+ if (btrfs_fs_compat_ro(info, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE))
+ fprintf(stderr, "checking free space tree\n");
+ else
+ fprintf(stderr, "checking free space cache\n");
+ }
ret = check_space_cache(root);
if (ret)
goto out;
ret = 1;
}
out:
- print_qgroup_report(0);
+ /* Don't override original ret */
+ if (ret)
+ report_qgroups(0);
+ else
+ ret = report_qgroups(0);
if (found_old_backref) { /*
* there was a disk format change when mixed
* backref was in testing tree. The old format