summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds-check.c160
1 files changed, 129 insertions, 31 deletions
diff --git a/cmds-check.c b/cmds-check.c
index 8b96aa6f..7d5625da 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -83,7 +83,8 @@ struct data_backref {
/*
* Much like data_backref, just removed the undetermined members
* and change it to use list_head.
- * Stored in the root->orphan_data_extents list
+ * During extent scan, it is stored in root->orphan_data_extent.
+ * During fs tree scan, it is then moved to inode_rec->orphan_data_extents.
*/
struct orphan_data_extent {
struct list_head list;
@@ -383,6 +384,7 @@ struct inode_record {
u64 extent_start;
u64 extent_end;
struct rb_root holes;
+ struct list_head orphan_extents;
u32 refs;
};
@@ -401,6 +403,7 @@ struct inode_record {
#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;
@@ -506,12 +509,15 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
struct inode_record *rec;
struct inode_backref *backref;
struct inode_backref *orig;
+ struct orphan_data_extent *src_orphan;
+ struct orphan_data_extent *dst_orphan;
size_t size;
rec = malloc(sizeof(*rec));
memcpy(rec, orig_rec, sizeof(*rec));
rec->refs = 1;
INIT_LIST_HEAD(&rec->backrefs);
+ INIT_LIST_HEAD(&rec->orphan_extents);
list_for_each_entry(orig, &orig_rec->backrefs, list) {
size = sizeof(*orig) + orig->namelen + 1;
@@ -519,9 +525,32 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
memcpy(backref, orig, size);
list_add_tail(&backref->list, &rec->backrefs);
}
+ list_for_each_entry(src_orphan, &orig_rec->orphan_extents, list) {
+ dst_orphan = malloc(sizeof(*dst_orphan));
+ /* TODO: Fix all the HELL of un-catched -ENOMEM case */
+ BUG_ON(!dst_orphan);
+ memcpy(dst_orphan, src_orphan, sizeof(*src_orphan));
+ list_add_tail(&dst_orphan->list, &rec->orphan_extents);
+ }
return rec;
}
+static void print_orphan_data_extents(struct list_head *orphan_extents,
+ u64 objectid)
+{
+ struct orphan_data_extent *orphan;
+
+ if (list_empty(orphan_extents))
+ return;
+ printf("The following data extent is lost in tree %llu:\n",
+ objectid);
+ list_for_each_entry(orphan, orphan_extents, list) {
+ printf("\tinode: %llu, offset:%llu, disk_bytenr: %llu, disk_len: %llu\n",
+ orphan->objectid, orphan->offset, orphan->disk_bytenr,
+ orphan->disk_len);
+ }
+}
+
static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
{
u64 root_objectid = root->root_key.objectid;
@@ -566,7 +595,13 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
fprintf(stderr, ", some csum missing");
if (errors & I_ERR_LINK_COUNT_WRONG)
fprintf(stderr, ", link count wrong");
+ if (errors & I_ERR_FILE_EXTENT_ORPHAN)
+ fprintf(stderr, ", orphan file extent");
fprintf(stderr, "\n");
+ /* Print the orphan extents if needed */
+ if (errors & I_ERR_FILE_EXTENT_ORPHAN)
+ print_orphan_data_extents(&rec->orphan_extents, root->objectid);
+
/* Print the holes if needed */
if (errors & I_ERR_FILE_EXTENT_DISCOUNT) {
struct file_extent_hole *hole;
@@ -637,6 +672,7 @@ static struct inode_record *get_inode_rec(struct cache_tree *inode_cache,
rec->extent_start = (u64)-1;
rec->refs = 1;
INIT_LIST_HEAD(&rec->backrefs);
+ INIT_LIST_HEAD(&rec->orphan_extents);
rec->holes = RB_ROOT;
node = malloc(sizeof(*node));
@@ -653,6 +689,18 @@ static struct inode_record *get_inode_rec(struct cache_tree *inode_cache,
return rec;
}
+static void free_orphan_data_extents(struct list_head *orphan_extents)
+{
+ struct orphan_data_extent *orphan;
+
+ while (!list_empty(orphan_extents)) {
+ orphan = list_entry(orphan_extents->next,
+ struct orphan_data_extent, list);
+ list_del(&orphan->list);
+ free(orphan);
+ }
+}
+
static void free_inode_rec(struct inode_record *rec)
{
struct inode_backref *backref;
@@ -666,6 +714,7 @@ static void free_inode_rec(struct inode_record *rec)
list_del(&backref->list);
free(backref);
}
+ free_orphan_data_extents(&rec->orphan_extents);
free_file_extent_holes(&rec->holes);
free(rec);
}
@@ -2487,6 +2536,67 @@ out:
return ret;
}
+static int repair_inode_orphan_extent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+ struct inode_record *rec)
+{
+ struct orphan_data_extent *orphan;
+ struct orphan_data_extent *tmp;
+ int ret = 0;
+
+ list_for_each_entry_safe(orphan, tmp, &rec->orphan_extents, list) {
+ /*
+ * Check for conflicting file extents
+ *
+ * Here we don't know whether the extents is compressed or not,
+ * so we can only assume it not compressed nor data offset,
+ * and use its disk_len as extent length.
+ */
+ ret = btrfs_get_extent(NULL, root, path, orphan->objectid,
+ orphan->offset, orphan->disk_len, 0);
+ btrfs_release_path(path);
+ if (ret < 0)
+ goto out;
+ if (!ret) {
+ fprintf(stderr,
+ "orphan extent (%llu, %llu) conflicts, delete the orphan\n",
+ orphan->disk_bytenr, orphan->disk_len);
+ ret = btrfs_free_extent(trans,
+ root->fs_info->extent_root,
+ orphan->disk_bytenr, orphan->disk_len,
+ 0, root->objectid, orphan->objectid,
+ orphan->offset);
+ if (ret < 0)
+ goto out;
+ }
+ ret = btrfs_insert_file_extent(trans, root, orphan->objectid,
+ orphan->offset, orphan->disk_bytenr,
+ orphan->disk_len, orphan->disk_len);
+ if (ret < 0)
+ goto out;
+
+ /* Update file size info */
+ rec->found_size += orphan->disk_len;
+ if (rec->found_size == rec->nbytes)
+ rec->errors &= ~I_ERR_FILE_NBYTES_WRONG;
+
+ /* Update the file extent hole info too */
+ ret = del_file_extent_hole(&rec->holes, orphan->offset,
+ orphan->disk_len);
+ if (ret < 0)
+ goto out;
+ if (RB_EMPTY_ROOT(&rec->holes))
+ rec->errors &= ~I_ERR_FILE_EXTENT_DISCOUNT;
+
+ list_del(&orphan->list);
+ free(orphan);
+ }
+ rec->errors &= ~I_ERR_FILE_EXTENT_ORPHAN;
+out:
+ return ret;
+}
+
static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
{
struct btrfs_trans_handle *trans;
@@ -2496,7 +2606,8 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
if (!(rec->errors & (I_ERR_DIR_ISIZE_WRONG |
I_ERR_NO_ORPHAN_ITEM |
I_ERR_LINK_COUNT_WRONG |
- I_ERR_NO_INODE_ITEM)))
+ I_ERR_NO_INODE_ITEM |
+ I_ERR_FILE_EXTENT_ORPHAN)))
return rec->errors;
path = btrfs_alloc_path();
@@ -2518,6 +2629,8 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
if (rec->errors & I_ERR_NO_INODE_ITEM)
ret = repair_inode_no_item(trans, root, path, rec);
+ if (!ret && rec->errors & I_ERR_FILE_EXTENT_ORPHAN)
+ ret = repair_inode_orphan_extent(trans, root, path, rec);
if (!ret && rec->errors & I_ERR_DIR_ISIZE_WRONG)
ret = repair_inode_isize(trans, root, path, rec);
if (!ret && rec->errors & I_ERR_NO_ORPHAN_ITEM)
@@ -3131,34 +3244,6 @@ out_free_path:
return ret;
}
-static void print_orphan_data_extents(struct list_head *orphan_extents,
- u64 objectid)
-{
- struct orphan_data_extent *orphan;
-
- if (list_empty(orphan_extents))
- return;
- printf("The following data extent is lost in tree %llu:\n",
- objectid);
- list_for_each_entry(orphan, orphan_extents, list) {
- printf("\tinode: %llu, offset:%llu, disk_bytenr: %llu, disk_len: %llu\n",
- orphan->objectid, orphan->offset, orphan->disk_bytenr,
- orphan->disk_len);
- }
-}
-
-static void free_orphan_data_extents(struct list_head *orphan_extents)
-{
- struct orphan_data_extent *orphan;
-
- while (!list_empty(orphan_extents)) {
- orphan = list_entry(orphan_extents->next,
- struct orphan_data_extent, list);
- list_del(&orphan->list);
- free(orphan);
- }
-}
-
static int check_fs_root(struct btrfs_root *root,
struct cache_tree *root_cache,
struct walk_control *wc)
@@ -3172,6 +3257,8 @@ static int check_fs_root(struct btrfs_root *root,
struct root_record *rec;
struct btrfs_root_item *root_item = &root->root_item;
struct cache_tree corrupt_blocks;
+ struct orphan_data_extent *orphan;
+ struct orphan_data_extent *tmp;
enum btrfs_tree_block_status status;
/*
@@ -3182,6 +3269,7 @@ static int check_fs_root(struct btrfs_root *root,
*/
cache_tree_init(&corrupt_blocks);
root->fs_info->corrupt_blocks = &corrupt_blocks;
+
if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
rec = get_root_rec(root_cache, root->root_key.objectid);
if (btrfs_root_refs(root_item) > 0)
@@ -3193,6 +3281,17 @@ static int check_fs_root(struct btrfs_root *root,
cache_tree_init(&root_node.root_cache);
cache_tree_init(&root_node.inode_cache);
+ /* Move the orphan extent record to corresponding inode_record */
+ list_for_each_entry_safe(orphan, tmp,
+ &root->orphan_data_extents, list) {
+ struct inode_record *inode;
+
+ inode = get_inode_rec(&root_node.inode_cache, orphan->objectid,
+ 1);
+ inode->errors |= I_ERR_FILE_EXTENT_ORPHAN;
+ list_move(&orphan->list, &inode->orphan_extents);
+ }
+
level = btrfs_header_level(root->node);
memset(wc->nodes, 0, sizeof(wc->nodes));
wc->nodes[level] = &root_node;
@@ -3290,7 +3389,6 @@ skip_walking:
free_corrupt_blocks_tree(&corrupt_blocks);
root->fs_info->corrupt_blocks = NULL;
- print_orphan_data_extents(&root->orphan_data_extents, root->objectid);
free_orphan_data_extents(&root->orphan_data_extents);
return ret;
}