diff options
-rw-r--r-- | cmds-check.c | 86 |
1 files changed, 80 insertions, 6 deletions
diff --git a/cmds-check.c b/cmds-check.c index b23353e6..0cba4cc8 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -221,6 +221,8 @@ struct walk_control { int root_level; }; +static void reset_cached_block_groups(struct btrfs_fs_info *fs_info); + static u8 imode_to_type(u32 imode) { #define S_SHIFT 12 @@ -1344,8 +1346,64 @@ out: return ret; } +static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) +{ + struct btrfs_trans_handle *trans; + struct btrfs_path *path; + struct btrfs_inode_item *ei; + struct btrfs_key key; + int ret; + + /* So far we just fix dir isize wrong */ + if (!(rec->errors & I_ERR_DIR_ISIZE_WRONG)) + return 1; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + btrfs_free_path(path); + return PTR_ERR(trans); + } + + key.objectid = rec->ino; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + if (ret < 0) + goto out; + if (ret) { + if (!path->slots[0]) { + ret = -ENOENT; + goto out; + } + path->slots[0]--; + ret = 0; + } + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + if (key.objectid != rec->ino) { + ret = -ENOENT; + goto out; + } + + ei = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_inode_item); + btrfs_set_inode_size(path->nodes[0], ei, rec->found_size); + btrfs_mark_buffer_dirty(path->nodes[0]); + rec->errors &= ~I_ERR_DIR_ISIZE_WRONG; + printf("reset isize for dir %Lu root %Lu\n", rec->ino, + root->root_key.objectid); +out: + btrfs_commit_transaction(trans, root); + btrfs_free_path(path); + return ret; +} + static int check_inode_recs(struct btrfs_root *root, - struct cache_tree *inode_cache) + struct cache_tree *inode_cache, int repair) { struct cache_extent *cache; struct ptr_node *node; @@ -1400,6 +1458,15 @@ static int check_inode_recs(struct btrfs_root *root, } } + if (repair) { + ret = try_repair_inode(root, rec); + if (ret == 0 && can_free_inode_rec(rec)) { + free_inode_rec(rec); + continue; + } + ret = 0; + } + error++; if (!rec->found_inode_item) rec->errors |= I_ERR_NO_INODE_ITEM; @@ -1741,7 +1808,7 @@ static int process_root_ref(struct extent_buffer *eb, int slot, static int check_fs_root(struct btrfs_root *root, struct cache_tree *root_cache, - struct walk_control *wc) + struct walk_control *wc, int repair) { int ret = 0; int wret; @@ -1811,7 +1878,7 @@ static int check_fs_root(struct btrfs_root *root, root_node.current); } - ret = check_inode_recs(root, &root_node.inode_cache); + ret = check_inode_recs(root, &root_node.inode_cache, repair); return ret; } @@ -1827,7 +1894,8 @@ static int fs_root_objectid(u64 objectid) } static int check_fs_roots(struct btrfs_root *root, - struct cache_tree *root_cache) + struct cache_tree *root_cache, + int repair) { struct btrfs_path path; struct btrfs_key key; @@ -1838,6 +1906,12 @@ static int check_fs_roots(struct btrfs_root *root, int ret; int err = 0; + /* + * Just in case we made any changes to the extent tree that weren't + * reflected into the free space cache yet. + */ + if (repair) + reset_cached_block_groups(root->fs_info); memset(&wc, 0, sizeof(wc)); cache_tree_init(&wc.shared); btrfs_init_path(&path); @@ -1864,7 +1938,7 @@ static int check_fs_roots(struct btrfs_root *root, err = 1; goto next; } - ret = check_fs_root(tmp_root, root_cache, &wc); + ret = check_fs_root(tmp_root, root_cache, &wc, repair); if (ret) err = 1; btrfs_free_fs_root(tmp_root); @@ -5879,7 +5953,7 @@ int cmd_check(int argc, char **argv) goto out; fprintf(stderr, "checking fs roots\n"); - ret = check_fs_roots(root, &root_cache); + ret = check_fs_roots(root, &root_cache, repair); if (ret) goto out; |