btrfs-progs: Introduce function to fix super block total bytes
[platform/upstream/btrfs-progs.git] / cmds-check.c
index 3e709b8..af8c62c 100644 (file)
@@ -133,6 +133,7 @@ struct data_backref {
 #define DIR_INDEX_MISSING       (1<<18) /* INODE_INDEX not found */
 #define DIR_INDEX_MISMATCH      (1<<19) /* INODE_INDEX found but not match */
 #define DIR_COUNT_AGAIN         (1<<20) /* DIR isize should be recalculated */
+#define BG_ACCOUNTING_ERROR     (1<<21) /* Block group accounting error */
 
 static inline struct data_backref* to_data_backref(struct extent_backref *back)
 {
@@ -10828,6 +10829,7 @@ static int check_extent_refs(struct btrfs_root *root,
        struct cache_extent *cache;
        int ret = 0;
        int had_dups = 0;
+       int err = 0;
 
        if (repair) {
                /*
@@ -10971,6 +10973,7 @@ static int check_extent_refs(struct btrfs_root *root,
                        cur_err = 1;
                }
 
+               err = cur_err;
                remove_cache_extent(extent_cache, cache);
                free_all_extent_backrefs(rec);
                if (!init_extent_tree && repair && (!cur_err || fix))
@@ -11003,7 +11006,10 @@ repair_abort:
                }
                return ret;
        }
-       return 0;
+
+       if (err)
+               err = -EIO;
+       return err;
 }
 
 u64 calc_stripe_length(u64 type, u64 length, int num_stripes)
@@ -11574,6 +11580,29 @@ loop:
        goto again;
 }
 
+static int check_extent_inline_ref(struct extent_buffer *eb,
+                  struct btrfs_key *key, struct btrfs_extent_inline_ref *iref)
+{
+       int ret;
+       u8 type = btrfs_extent_inline_ref_type(eb, iref);
+
+       switch (type) {
+       case BTRFS_TREE_BLOCK_REF_KEY:
+       case BTRFS_EXTENT_DATA_REF_KEY:
+       case BTRFS_SHARED_BLOCK_REF_KEY:
+       case BTRFS_SHARED_DATA_REF_KEY:
+               ret = 0;
+               break;
+       default:
+               error("extent[%llu %u %llu] has unknown ref type: %d",
+                     key->objectid, key->type, key->offset, type);
+               ret = UNKNOWN_TYPE;
+               break;
+       }
+
+       return ret;
+}
+
 /*
  * Check backrefs of a tree block given by @bytenr or @eb.
  *
@@ -11708,6 +11737,11 @@ static int check_tree_block_ref(struct btrfs_root *root,
                type = btrfs_extent_inline_ref_type(leaf, iref);
                offset = btrfs_extent_inline_ref_offset(leaf, iref);
 
+               ret = check_extent_inline_ref(leaf, &key, iref);
+               if (ret) {
+                       err |= ret;
+                       break;
+               }
                if (type == BTRFS_TREE_BLOCK_REF_KEY) {
                        if (offset == root->objectid)
                                found_ref = 1;
@@ -11985,6 +12019,11 @@ static int check_extent_data_item(struct btrfs_root *root,
                type = btrfs_extent_inline_ref_type(leaf, iref);
                dref = (struct btrfs_extent_data_ref *)(&iref->offset);
 
+               ret = check_extent_inline_ref(leaf, &dbref_key, iref);
+               if (ret) {
+                       err |= ret;
+                       break;
+               }
                if (type == BTRFS_EXTENT_DATA_REF_KEY) {
                        ref_root = btrfs_extent_data_ref_root(leaf, dref);
                        if (ref_root == root->objectid)
@@ -12897,7 +12936,7 @@ out:
                error(
                "block group[%llu %llu] used %llu but extent items used %llu",
                        bg_key.objectid, bg_key.offset, used, total);
-               err |= ACCOUNTING_MISMATCH;
+               err |= BG_ACCOUNTING_ERROR;
        }
        return err;
 }
@@ -13291,6 +13330,8 @@ out:
                ret = btrfs_fix_block_accounting(trans, root);
                if (ret)
                        err |= ret;
+               else
+                       err &= ~BG_ACCOUNTING_ERROR;
        }
 
        if (trans)
@@ -14803,32 +14844,34 @@ int cmd_check(int argc, char **argv)
                goto close_out;
        }
 
+       if (!init_extent_tree) {
+               ret = repair_root_items(info);
+               if (ret < 0) {
+                       err = !!ret;
+                       error("failed to repair root items: %s", strerror(-ret));
+                       goto close_out;
+               }
+               if (repair) {
+                       fprintf(stderr, "Fixed %d roots.\n", ret);
+                       ret = 0;
+               } else if (ret > 0) {
+                       fprintf(stderr,
+                               "Found %d roots with an outdated root item.\n",
+                               ret);
+                       fprintf(stderr,
+       "Please run a filesystem check with the option --repair to fix them.\n");
+                       ret = 1;
+                       err |= ret;
+                       goto close_out;
+               }
+       }
+
        ret = do_check_chunks_and_extents(info);
        err |= !!ret;
        if (ret)
                error(
                "errors found in extent allocation tree or chunk allocation");
 
-       ret = repair_root_items(info);
-       err |= !!ret;
-       if (ret < 0) {
-               error("failed to repair root items: %s", strerror(-ret));
-               goto close_out;
-       }
-       if (repair) {
-               fprintf(stderr, "Fixed %d roots.\n", ret);
-               ret = 0;
-       } else if (ret > 0) {
-               fprintf(stderr,
-                      "Found %d roots with an outdated root item.\n",
-                      ret);
-               fprintf(stderr,
-                       "Please run a filesystem check with the option --repair to fix them.\n");
-               ret = 1;
-               err |= !!ret;
-               goto close_out;
-       }
-
        if (!ctx.progress_enabled) {
                if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
                        fprintf(stderr, "checking free space tree\n");