@@ -371,6 +371,17 @@ struct root_item_info {
};
/*
+ * Use inode_item_fix_info as function check_inode_item's arg.
+ */
+struct inode_item_fix_info {
+ u64 ino;
+ u64 isize;
+ u64 nbytes;
+
+ int err;
+};
+
+/*
* Error bit for low memory mode check.
*
* Currently no caller cares about it yet. Just internal use for error
@@ -1866,13 +1877,16 @@ struct node_refs {
static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
struct node_refs *nrefs, u64 level);
static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
- unsigned int ext_ref);
-
+ unsigned int ext_ref,
+ struct inode_item_fix_info *info);
+static int repair_inode_item(struct btrfs_root *root,
+ struct inode_item_fix_info *info);
static int process_one_leaf_v2(struct btrfs_root *root, struct btrfs_path *path,
struct node_refs *nrefs, int *level, int ext_ref)
{
struct extent_buffer *cur = path->nodes[0];
struct btrfs_key key;
+ struct inode_item_fix_info info;
u64 cur_bytenr;
u32 nritems;
u64 first_ino = 0;
@@ -1881,6 +1895,7 @@ static int process_one_leaf_v2(struct btrfs_root *root, struct btrfs_path *path,
int ret = 0; /* Final return value */
int err = 0; /* Positive error bitmap */
+ memset(&info, 0, sizeof(info));
cur_bytenr = cur->start;
/* skip to first inode item or the first inode number change */
@@ -1900,8 +1915,27 @@ static int process_one_leaf_v2(struct btrfs_root *root, struct btrfs_path *path,
path->slots[0] = i;
again:
- err |= check_inode_item(root, path, ext_ref);
+ err |= check_inode_item(root, path, ext_ref, &info);
+
+ if (repair && (err & ~LAST_ITEM)) {
+ ret = repair_inode_item(root, &info);
+ if (ret < 0)
+ goto out;
+ /*
+ * if some errors was repaired, path shall be searched
+ * again since path has been changed
+ */
+ if (ret == 0) {
+ btrfs_item_key_to_cpu(path->nodes[0], &key,
+ path->slots[0]);
+ btrfs_release_path(path);
+ btrfs_search_slot(NULL, root, &key, path, 0, 0);
+
+ cur = path->nodes[0];
+ err = info.err;
+ }
+ }
if (err & LAST_ITEM)
goto out;
@@ -2211,7 +2245,8 @@ out:
}
static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
- unsigned int ext_ref);
+ unsigned int ext_ref,
+ struct inode_item_fix_info *info);
static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
int *level, struct node_refs *nrefs, int ext_ref)
@@ -2293,7 +2328,7 @@ static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
}
ret = check_child_node(root, cur, path->slots[*level], next);
- if (ret < 0)
+ if (ret < 0)
break;
if (btrfs_is_leaf(next))
@@ -2383,6 +2418,105 @@ out:
return ret;
}
+/*
+ * Set inode's nbytes to correct value in @info
+ *
+ * Returns <0 means on error
+ * Returns 0 means successful repair
+ */
+static int repair_inode_nbytes_lowmem(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct inode_item_fix_info *info)
+{
+ struct btrfs_inode_item *ei;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ int ret;
+
+ ASSERT(info);
+ key.objectid = info->ino;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ei = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_inode_item);
+ btrfs_set_inode_nbytes(path.nodes[0], ei, info->nbytes);
+ btrfs_mark_buffer_dirty(path.nodes[0]);
+ printf("reset nbytes for inode %llu root %llu\n", info->ino,
+ root->root_key.objectid);
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * repair_inode_item - repair inode item errors
+ *
+ * Repair the inode item if error can be repaired. Any caller should compare
+ * @info->err with the before, if err diffs, some contexts should be notified.
+ *
+ * @root: subvolume_root
+ * @info: contains correct values and error
+ * when it returns, @info stores the unrepairable errors
+ *
+ * Returns < 0 represents repair function error
+ * Returns 0 represents have fixed some errors or no error at all
+ * Returns > 0 represents it can't fix any error
+ */
+static int repair_inode_item(struct btrfs_root *root,
+ struct inode_item_fix_info *info)
+{
+ struct btrfs_trans_handle *trans;
+ int ret = 0;
+ int err;
+
+ ASSERT(info);
+ err = info->err;
+ if (!err) {
+ /* nothing to repair */
+ ret = 0;
+ goto out;
+ }
+ if (!(err & NBYTES_ERROR)) {
+ warning("root %llu INODE[%llu] have error(s) can't repair, error : %d",
+ root->objectid, info->ino, err);
+ /* can't fix any errors, ret should be positive */
+ ret = err;
+ goto out;
+ }
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ goto out;
+ }
+
+ if (err & NBYTES_ERROR) {
+ ret = repair_inode_nbytes_lowmem(trans, root, info);
+ if (ret == 0)
+ err &= ~NBYTES_ERROR;
+ else if (ret < 0)
+ goto out;
+ }
+
+ if (err != info->err) {
+ info->err = err;
+ ret = 0;
+ }
+
+ btrfs_commit_transaction(trans, root);
+out:
+ return ret;
+}
+
static int repair_inode_isize(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_path *path,
struct inode_record *rec)
@@ -4781,7 +4915,8 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
* Return >0 for error or hit the traversal is done(by error bitmap)
*/
static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
- unsigned int ext_ref)
+ unsigned int ext_ref,
+ struct inode_item_fix_info *info)
{
struct extent_buffer *node;
struct btrfs_inode_item *ii;
@@ -4821,6 +4956,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
dir = imode_to_type(mode) == BTRFS_FT_DIR;
nlink = btrfs_inode_nlink(node, ii);
nodatasum = btrfs_inode_flags(node, ii) & BTRFS_INODE_NODATASUM;
+ info->ino = inode_id;
while (1) {
ret = btrfs_next_item(root, path);
@@ -4924,11 +5060,13 @@ out:
if (nbytes != extent_size) {
err |= NBYTES_ERROR;
+ info->nbytes = extent_size;
error("root %llu INODE[%llu] nbytes(%llu) not equal to extent_size(%llu)",
root->objectid, inode_id, nbytes, extent_size);
}
}
+ info->err = err & ~LAST_ITEM;
return err;
}
@@ -4936,9 +5074,11 @@ static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
{
struct btrfs_path path;
struct btrfs_key key;
+ struct inode_item_fix_info info;
int err = 0;
int ret;
+ memset(&info, 0, sizeof(info));
btrfs_init_path(&path);
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
key.type = BTRFS_INODE_ITEM_KEY;
@@ -4952,12 +5092,17 @@ static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
err |= INODE_ITEM_MISSING;
}
- err |= check_inode_item(root, &path, ext_ref);
+ err |= check_inode_item(root, &path, ext_ref, &info);
err &= ~LAST_ITEM;
if (err && !ret)
ret = -EIO;
out:
btrfs_release_path(&path);
+ if (repair) {
+ ret = repair_inode_item(root, &info);
+ if (!info.err)
+ ret = 0;
+ }
return ret;
}
@@ -9322,7 +9467,7 @@ static int check_extent_refs(struct btrfs_root *root,
/*
* Although it's not a extent ref's problem, we reuse this
* routine for error reporting.
- * No repair function yet.
+ * No repair function yet
*/
if (rec->crossing_stripes) {
fprintf(stderr,
@@ -12719,11 +12864,10 @@ int cmd_check(int argc, char **argv)
}
/*
- * Not supported yet
+ * Support partailly
*/
if (repair && check_mode == CHECK_MODE_LOWMEM) {
- error("low memory mode doesn't support repair yet");
- exit(1);
+ warning("low memory mode supports repair partailly");
}
radix_tree_init();
Added 'repair_inode_item' which dispatches functions such as 'repair_inode__nbytes_lowmem' to correct errors and 'struct inode_item_fix_info' to store correct values and errors. v2: reassign err to info.err in process_one_leaf. Signed-off-by: Su Yue <suy.fnst@cn.fujitsu.com> --- cmds-check.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 11 deletions(-)