btrfs-progs: check: get the highest inode for lost+found
[platform/upstream/btrfs-progs.git] / cmds-check.c
index ede612d..a55d00d 100644 (file)
@@ -113,6 +113,24 @@ struct data_backref {
        u32 found_ref;
 };
 
+#define ROOT_DIR_ERROR         (1<<1)  /* bad ROOT_DIR */
+#define DIR_ITEM_MISSING       (1<<2)  /* DIR_ITEM not found */
+#define DIR_ITEM_MISMATCH      (1<<3)  /* DIR_ITEM found but not match */
+#define INODE_REF_MISSING      (1<<4)  /* INODE_REF/INODE_EXTREF not found */
+#define INODE_ITEM_MISSING     (1<<5)  /* INODE_ITEM not found */
+#define INODE_ITEM_MISMATCH    (1<<6)  /* INODE_ITEM found but not match */
+#define FILE_EXTENT_ERROR      (1<<7)  /* bad FILE_EXTENT */
+#define ODD_CSUM_ITEM          (1<<8)  /* CSUM_ITEM error */
+#define CSUM_ITEM_MISSING      (1<<9)  /* CSUM_ITEM not found */
+#define LINK_COUNT_ERROR       (1<<10) /* INODE_ITEM nlink count error */
+#define NBYTES_ERROR           (1<<11) /* INODE_ITEM nbytes count error */
+#define ISIZE_ERROR            (1<<12) /* INODE_ITEM size count error */
+#define ORPHAN_ITEM            (1<<13) /* INODE_ITEM no reference */
+#define NO_INODE_ITEM          (1<<14) /* no inode_item */
+#define LAST_ITEM              (1<<15) /* Complete this tree traversal */
+#define ROOT_REF_MISSING       (1<<16) /* ROOT_REF not found */
+#define ROOT_REF_MISMATCH      (1<<17) /* ROOT_REF found but not match */
+
 static inline struct data_backref* to_data_backref(struct extent_backref *back)
 {
        return container_of(back, struct data_backref, node);
@@ -1839,6 +1857,96 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
        return ret;
 }
 
+struct node_refs {
+       u64 bytenr[BTRFS_MAX_LEVEL];
+       u64 refs[BTRFS_MAX_LEVEL];
+       int need_check[BTRFS_MAX_LEVEL];
+};
+
+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);
+
+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;
+       u64 cur_bytenr;
+       u32 nritems;
+       u64 first_ino = 0;
+       int root_level = btrfs_header_level(root->node);
+       int i;
+       int ret = 0; /* Final return value */
+       int err = 0; /* Positive error bitmap */
+
+       cur_bytenr = cur->start;
+
+       /* skip to first inode item or the first inode number change */
+       nritems = btrfs_header_nritems(cur);
+       for (i = 0; i < nritems; i++) {
+               btrfs_item_key_to_cpu(cur, &key, i);
+               if (i == 0)
+                       first_ino = key.objectid;
+               if (key.type == BTRFS_INODE_ITEM_KEY ||
+                   (first_ino && first_ino != key.objectid))
+                       break;
+       }
+       if (i == nritems) {
+               path->slots[0] = nritems;
+               return 0;
+       }
+       path->slots[0] = i;
+
+again:
+       err |= check_inode_item(root, path, ext_ref);
+
+       if (err & LAST_ITEM)
+               goto out;
+
+       /* still have inode items in thie leaf */
+       if (cur->start == cur_bytenr)
+               goto again;
+
+       /*
+        * we have switched to another leaf, above nodes may
+        * have changed, here walk down the path, if a node
+        * or leaf is shared, check whether we can skip this
+        * node or leaf.
+        */
+       for (i = root_level; i >= 0; i--) {
+               if (path->nodes[i]->start == nrefs->bytenr[i])
+                       continue;
+
+               ret = update_nodes_refs(root,
+                               path->nodes[i]->start,
+                               nrefs, i);
+               if (ret)
+                       goto out;
+
+               if (!nrefs->need_check[i]) {
+                       *level += 1;
+                       break;
+               }
+       }
+
+       for (i = 0; i < *level; i++) {
+               free_extent_buffer(path->nodes[i]);
+               path->nodes[i] = NULL;
+       }
+out:
+       err &= ~LAST_ITEM;
+       /*
+        * Convert any error bitmap to -EIO, as we should avoid
+        * mixing positive and negative return value to represent
+        * error
+        */
+       if (err && !ret)
+               ret = -EIO;
+       return ret;
+}
+
 static void reada_walk_down(struct btrfs_root *root,
                            struct extent_buffer *node, int slot)
 {
@@ -1912,10 +2020,66 @@ static int check_child_node(struct btrfs_root *root,
        return ret;
 }
 
-struct node_refs {
-       u64 bytenr[BTRFS_MAX_LEVEL];
-       u64 refs[BTRFS_MAX_LEVEL];
-};
+/*
+ * for a tree node or leaf, if it's shared, indeed we don't need to iterate it
+ * in every fs or file tree check. Here we find its all root ids, and only check
+ * it in the fs or file tree which has the smallest root id.
+ */
+static int need_check(struct btrfs_root *root, struct ulist *roots)
+{
+       struct rb_node *node;
+       struct ulist_node *u;
+
+       if (roots->nnodes == 1)
+               return 1;
+
+       node = rb_first(&roots->root);
+       u = rb_entry(node, struct ulist_node, rb_node);
+       /*
+        * current root id is not smallest, we skip it and let it be checked
+        * in the fs or file tree who hash the smallest root id.
+        */
+       if (root->objectid != u->val)
+               return 0;
+
+       return 1;
+}
+
+/*
+ * for a tree node or leaf, we record its reference count, so later if we still
+ * process this node or leaf, don't need to compute its reference count again.
+ */
+static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
+                            struct node_refs *nrefs, u64 level)
+{
+       int check, ret;
+       u64 refs;
+       struct ulist *roots;
+
+       if (nrefs->bytenr[level] != bytenr) {
+               ret = btrfs_lookup_extent_info(NULL, root, bytenr,
+                                      level, 1, &refs, NULL);
+               if (ret < 0)
+                       return ret;
+
+               nrefs->bytenr[level] = bytenr;
+               nrefs->refs[level] = refs;
+               if (refs > 1) {
+                       ret = btrfs_find_all_roots(NULL, root->fs_info, bytenr,
+                                                  0, &roots);
+                       if (ret)
+                               return -EIO;
+
+                       check = need_check(root, roots);
+                       ulist_free(roots);
+                       nrefs->need_check[level] = check;
+               } else {
+                       nrefs->need_check[level] = 1;
+               }
+       }
+
+       return 0;
+}
 
 static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
                          struct walk_control *wc, int *level,
@@ -2046,6 +2210,110 @@ out:
        return err;
 }
 
+static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
+                           unsigned int ext_ref);
+
+static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
+                            int *level, struct node_refs *nrefs, int ext_ref)
+{
+       enum btrfs_tree_block_status status;
+       u64 bytenr;
+       u64 ptr_gen;
+       struct extent_buffer *next;
+       struct extent_buffer *cur;
+       u32 blocksize;
+       int ret;
+
+       WARN_ON(*level < 0);
+       WARN_ON(*level >= BTRFS_MAX_LEVEL);
+
+       ret = update_nodes_refs(root, path->nodes[*level]->start,
+                               nrefs, *level);
+       if (ret < 0)
+               return ret;
+
+       while (*level >= 0) {
+               WARN_ON(*level < 0);
+               WARN_ON(*level >= BTRFS_MAX_LEVEL);
+               cur = path->nodes[*level];
+
+               if (btrfs_header_level(cur) != *level)
+                       WARN_ON(1);
+
+               if (path->slots[*level] >= btrfs_header_nritems(cur))
+                       break;
+               /* Don't forgot to check leaf/node validation */
+               if (*level == 0) {
+                       ret = btrfs_check_leaf(root, NULL, cur);
+                       if (ret != BTRFS_TREE_BLOCK_CLEAN) {
+                               ret = -EIO;
+                               break;
+                       }
+                       ret = process_one_leaf_v2(root, path, nrefs,
+                                                 level, ext_ref);
+                       break;
+               } else {
+                       ret = btrfs_check_node(root, NULL, cur);
+                       if (ret != BTRFS_TREE_BLOCK_CLEAN) {
+                               ret = -EIO;
+                               break;
+                       }
+               }
+               bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
+               ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
+               blocksize = root->nodesize;
+
+               ret = update_nodes_refs(root, bytenr, nrefs, *level - 1);
+               if (ret)
+                       break;
+               if (!nrefs->need_check[*level - 1]) {
+                       path->slots[*level]++;
+                       continue;
+               }
+
+               next = btrfs_find_tree_block(root, bytenr, blocksize);
+               if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) {
+                       free_extent_buffer(next);
+                       reada_walk_down(root, cur, path->slots[*level]);
+                       next = read_tree_block(root, bytenr, blocksize,
+                                              ptr_gen);
+                       if (!extent_buffer_uptodate(next)) {
+                               struct btrfs_key node_key;
+
+                               btrfs_node_key_to_cpu(path->nodes[*level],
+                                                     &node_key,
+                                                     path->slots[*level]);
+                               btrfs_add_corrupt_extent_record(root->fs_info,
+                                               &node_key,
+                                               path->nodes[*level]->start,
+                                               root->nodesize, *level);
+                               ret = -EIO;
+                               break;
+                       }
+               }
+
+               ret = check_child_node(root, cur, path->slots[*level], next);
+               if (ret < 0) 
+                       break;
+
+               if (btrfs_is_leaf(next))
+                       status = btrfs_check_leaf(root, NULL, next);
+               else
+                       status = btrfs_check_node(root, NULL, next);
+               if (status != BTRFS_TREE_BLOCK_CLEAN) {
+                       free_extent_buffer(next);
+                       ret = -EIO;
+                       break;
+               }
+
+               *level = *level - 1;
+               free_extent_buffer(path->nodes[*level]);
+               path->nodes[*level] = next;
+               path->slots[*level] = 0;
+       }
+       return ret;
+}
+
 static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path,
                        struct walk_control *wc, int *level)
 {
@@ -2070,6 +2338,27 @@ static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path,
        return 1;
 }
 
+static int walk_up_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
+                          int *level)
+{
+       int i;
+       struct extent_buffer *leaf;
+
+       for (i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
+               leaf = path->nodes[i];
+               if (path->slots[i] + 1 < btrfs_header_nritems(leaf)) {
+                       path->slots[i]++;
+                       *level = i;
+                       return 0;
+               } else {
+                       free_extent_buffer(path->nodes[*level]);
+                       path->nodes[*level] = NULL;
+                       *level = i + 1;
+               }
+       }
+       return 1;
+}
+
 static int check_root_dir(struct inode_record *rec)
 {
        struct inode_backref *backref;
@@ -2564,6 +2853,31 @@ out:
        return ret;
 }
 
+static int get_highest_inode(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               struct btrfs_path *path,
+                               u64 *highest_ino)
+{
+       struct btrfs_key key, found_key;
+       int ret;
+
+       btrfs_init_path(path);
+       key.objectid = BTRFS_LAST_FREE_OBJECTID;
+       key.offset = -1;
+       key.type = BTRFS_INODE_ITEM_KEY;
+       ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+       if (ret == 1) {
+               btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+                               path->slots[0] - 1);
+               *highest_ino = found_key.objectid;
+               ret = 0;
+       }
+       if (*highest_ino >= BTRFS_LAST_FREE_OBJECTID)
+               ret = -EOVERFLOW;
+       btrfs_release_path(path);
+       return ret;
+}
+
 static int repair_inode_nlinks(struct btrfs_trans_handle *trans,
                               struct btrfs_root *root,
                               struct btrfs_path *path,
@@ -2609,11 +2923,9 @@ static int repair_inode_nlinks(struct btrfs_trans_handle *trans,
        }
 
        if (rec->found_link == 0) {
-               lost_found_ino = root->highest_inode;
-               if (lost_found_ino >= BTRFS_LAST_FREE_OBJECTID) {
-                       ret = -EOVERFLOW;
+               ret = get_highest_inode(trans, root, path, &lost_found_ino);
+               if (ret < 0)
                        goto out;
-               }
                lost_found_ino++;
                ret = btrfs_mkdir(trans, root, dir_name, strlen(dir_name),
                                  BTRFS_FIRST_FREE_OBJECTID, &lost_found_ino,
@@ -2977,21 +3289,6 @@ static int check_inode_recs(struct btrfs_root *root,
        }
 
        /*
-        * We need to record the highest inode number for later 'lost+found'
-        * dir creation.
-        * 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.
-        */
-       cache = last_cache_extent(inode_cache);
-       if (cache) {
-               node = container_of(cache, struct ptr_node, cache);
-               rec = node->data;
-               if (rec->ino > root->highest_inode)
-                       root->highest_inode = rec->ino;
-       }
-
-       /*
         * We need to repair backrefs first because we could change some of the
         * errors in the inode recs.
         *
@@ -3827,24 +4124,6 @@ out:
        return err;
 }
 
-#define ROOT_DIR_ERROR         (1<<1)  /* bad ROOT_DIR */
-#define DIR_ITEM_MISSING       (1<<2)  /* DIR_ITEM not found */
-#define DIR_ITEM_MISMATCH      (1<<3)  /* DIR_ITEM found but not match */
-#define INODE_REF_MISSING      (1<<4)  /* INODE_REF/INODE_EXTREF not found */
-#define INODE_ITEM_MISSING     (1<<5)  /* INODE_ITEM not found */
-#define INODE_ITEM_MISMATCH    (1<<6)  /* INODE_ITEM found but not match */
-#define FILE_EXTENT_ERROR      (1<<7)  /* bad FILE_EXTENT */
-#define ODD_CSUM_ITEM          (1<<8)  /* CSUM_ITEM error */
-#define CSUM_ITEM_MISSING      (1<<9)  /* CSUM_ITEM not found */
-#define LINK_COUNT_ERROR       (1<<10) /* INODE_ITEM nlink count error */
-#define NBYTES_ERROR           (1<<11) /* INODE_ITEM nbytes count error */
-#define ISIZE_ERROR            (1<<12) /* INODE_ITEM size count error */
-#define ORPHAN_ITEM            (1<<13) /* INODE_ITEM no reference */
-#define NO_INODE_ITEM          (1<<14) /* no inode_item */
-#define LAST_ITEM              (1<<15) /* Complete this tree traversal */
-#define ROOT_REF_MISSING       (1<<16) /* ROOT_REF not found */
-#define ROOT_REF_MISMATCH      (1<<17) /* ROOT_REF found but not match */
-
 /*
  * Find DIR_ITEM/DIR_INDEX for the given key and check it with the specified
  * INODE_REF/INODE_EXTREF match.
@@ -4255,7 +4534,7 @@ extref:
                        len = ref_namelen;
                } else {
                        len = BTRFS_NAME_LEN;
-                       warning("Warning: root %llu INODE %s[%llu %llu] name too long\n",
+                       warning("root %llu INODE %s[%llu %llu] name too long",
                                root->objectid,
                                key->type == BTRFS_INODE_REF_KEY ?
                                        "REF" : "EXTREF",
@@ -4661,6 +4940,35 @@ out:
        return err;
 }
 
+static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
+{
+       struct btrfs_path path;
+       struct btrfs_key key;
+       int err = 0;
+       int ret;
+
+       btrfs_init_path(&path);
+       key.objectid = BTRFS_FIRST_FREE_OBJECTID;
+       key.type = BTRFS_INODE_ITEM_KEY;
+       key.offset = 0;
+
+       ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+       if (ret < 0)
+               goto out;
+       if (ret > 0) {
+               ret = 0;
+               err |= INODE_ITEM_MISSING;
+       }
+
+       err |= check_inode_item(root, &path, ext_ref);
+       err &= ~LAST_ITEM;
+       if (err && !ret)
+               ret = -EIO;
+out:
+       btrfs_release_path(&path);
+       return ret;
+}
+
 /*
  * Iterate all item on the tree and call check_inode_item() to check.
  *
@@ -4669,70 +4977,62 @@ out:
  *
  * Return 0 if no error found.
  * Return <0 for error.
- * All internal error bitmap will be converted to -EIO, to avoid
- * mixing negative and postive return value.
  */
 static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
 {
-       struct btrfs_path *path;
-       struct btrfs_key key;
-       u64 inode_id;
-       int ret, err = 0;
-
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-
-       key.objectid = 0;
-       key.type = 0;
-       key.offset = 0;
+       struct btrfs_path path;
+       struct node_refs nrefs;
+       struct btrfs_root_item *root_item = &root->root_item;
+       int ret, wret;
+       int level;
 
-       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+       /*
+        * We need to manually check the first inode item(256)
+        * As the following traversal function will only start from
+        * the first inode item in the leaf, if inode item(256) is missing
+        * we will just skip it forever.
+        */
+       ret = check_fs_first_inode(root, ext_ref);
        if (ret < 0)
-               goto out;
+               return ret;
 
-       while (1) {
-               btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+       memset(&nrefs, 0, sizeof(nrefs));
+       level = btrfs_header_level(root->node);
+       btrfs_init_path(&path);
 
-               /*
-                * All check must start with inode item, skip if not
-                */
-               if (key.type == BTRFS_INODE_ITEM_KEY) {
-                       ret = check_inode_item(root, path, ext_ref);
-                       err |= ret;
-                       if (err & LAST_ITEM)
-                               goto out;
-                       continue;
-               }
-               error("root %llu ITEM[%llu %u %llu] isn't INODE_ITEM, skip to next inode",
-                     root->objectid, key.objectid, key.type,
-                     key.offset);
+       if (btrfs_root_refs(root_item) > 0 ||
+           btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
+               path.nodes[level] = root->node;
+               path.slots[level] = 0;
+               extent_buffer_get(root->node);
+       } else {
+               struct btrfs_key key;
 
-               err |= NO_INODE_ITEM;
-               inode_id = key.objectid;
+               btrfs_disk_key_to_cpu(&key, &root_item->drop_progress);
+               level = root_item->drop_level;
+               path.lowest_level = level;
+               ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+               if (ret < 0)
+                       goto out;
+               ret = 0;
+       }
 
-               /*
-                * skip to next inode
-                * TODO: Maybe search_slot() will be faster?
-                */
-               do {
-                       ret = btrfs_next_item(root, path);
-                       if (ret > 0) {
-                               goto out;
-                       } else if (ret < 0) {
-                               err = ret;
-                               goto out;
-                       }
-                       btrfs_item_key_to_cpu(path->nodes[0], &key,
-                                             path->slots[0]);
-               } while (inode_id == key.objectid);
+       while (1) {
+               wret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
+               if (wret < 0)
+                       ret = wret;
+               if (wret != 0)
+                       break;
+
+               wret = walk_up_tree_v2(root, &path, &level);
+               if (wret < 0)
+                       ret = wret;
+               if (wret != 0)
+                       break;
        }
 
 out:
-       err &= ~LAST_ITEM;
-       if (err && !ret)
-               ret = -EIO;
-       btrfs_free_path(path);
+       btrfs_release_path(&path);
        return ret;
 }
 
@@ -4747,12 +5047,12 @@ out:
 static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
                          struct extent_buffer *node, int slot)
 {
-       struct btrfs_path *path;
+       struct btrfs_path path;
        struct btrfs_key key;
        struct btrfs_root_ref *ref;
        struct btrfs_root_ref *backref;
-       char ref_name[BTRFS_NAME_LEN];
-       char backref_name[BTRFS_NAME_LEN];
+       char ref_name[BTRFS_NAME_LEN] = {0};
+       char backref_name[BTRFS_NAME_LEN] = {0};
        u64 ref_dirid;
        u64 ref_seq;
        u32 ref_namelen;
@@ -4784,8 +5084,8 @@ static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
        key.type = BTRFS_ROOT_BACKREF_KEY + BTRFS_ROOT_REF_KEY - ref_key->type;
        key.offset = ref_key->objectid;
 
-       path = btrfs_alloc_path();
-       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+       btrfs_init_path(&path);
+       ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
        if (ret) {
                err |= ROOT_REF_MISSING;
                error("%s[%llu %llu] couldn't find relative ref",
@@ -4795,11 +5095,11 @@ static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
                goto out;
        }
 
-       backref = btrfs_item_ptr(path->nodes[0], path->slots[0],
+       backref = btrfs_item_ptr(path.nodes[0], path.slots[0],
                                 struct btrfs_root_ref);
-       backref_dirid = btrfs_root_ref_dirid(path->nodes[0], backref);
-       backref_seq = btrfs_root_ref_sequence(path->nodes[0], backref);
-       backref_namelen = btrfs_root_ref_name_len(path->nodes[0], backref);
+       backref_dirid = btrfs_root_ref_dirid(path.nodes[0], backref);
+       backref_seq = btrfs_root_ref_sequence(path.nodes[0], backref);
+       backref_namelen = btrfs_root_ref_name_len(path.nodes[0], backref);
 
        if (backref_namelen <= BTRFS_NAME_LEN) {
                len = backref_namelen;
@@ -4810,7 +5110,7 @@ static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
                        "ROOT_REF" : "ROOT_BACKREF",
                        key.objectid, key.offset);
        }
-       read_extent_buffer(path->nodes[0], backref_name,
+       read_extent_buffer(path.nodes[0], backref_name,
                           (unsigned long)(backref + 1), len);
 
        if (ref_dirid != backref_dirid || ref_seq != backref_seq ||
@@ -4823,7 +5123,91 @@ static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
                      ref_key->objectid, ref_key->offset);
        }
 out:
-       btrfs_free_path(path);
+       btrfs_release_path(&path);
+       return err;
+}
+
+/*
+ * Check all fs/file tree in low_memory mode.
+ *
+ * 1. for fs tree root item, call check_fs_root_v2()
+ * 2. for fs tree root ref/backref, call check_root_ref()
+ *
+ * Return 0 if no error occurred.
+ */
+static int check_fs_roots_v2(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_root *tree_root = fs_info->tree_root;
+       struct btrfs_root *cur_root = NULL;
+       struct btrfs_path path;
+       struct btrfs_key key;
+       struct extent_buffer *node;
+       unsigned int ext_ref;
+       int slot;
+       int ret;
+       int err = 0;
+
+       ext_ref = btrfs_fs_incompat(fs_info, EXTENDED_IREF);
+
+       btrfs_init_path(&path);
+       key.objectid = BTRFS_FS_TREE_OBJECTID;
+       key.offset = 0;
+       key.type = BTRFS_ROOT_ITEM_KEY;
+
+       ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
+       if (ret < 0) {
+               err = ret;
+               goto out;
+       } else if (ret > 0) {
+               err = -ENOENT;
+               goto out;
+       }
+
+       while (1) {
+               node = path.nodes[0];
+               slot = path.slots[0];
+               btrfs_item_key_to_cpu(node, &key, slot);
+               if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
+                       goto out;
+               if (key.type == BTRFS_ROOT_ITEM_KEY &&
+                   fs_root_objectid(key.objectid)) {
+                       if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
+                               cur_root = btrfs_read_fs_root_no_cache(fs_info,
+                                                                      &key);
+                       } else {
+                               key.offset = (u64)-1;
+                               cur_root = btrfs_read_fs_root(fs_info, &key);
+                       }
+
+                       if (IS_ERR(cur_root)) {
+                               error("Fail to read fs/subvol tree: %lld",
+                                     key.objectid);
+                               err = -EIO;
+                               goto next;
+                       }
+
+                       ret = check_fs_root_v2(cur_root, ext_ref);
+                       err |= ret;
+
+                       if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+                               btrfs_free_fs_root(cur_root);
+               } else if (key.type == BTRFS_ROOT_REF_KEY ||
+                               key.type == BTRFS_ROOT_BACKREF_KEY) {
+                       ret = check_root_ref(tree_root, &key, node, slot);
+                       err |= ret;
+               }
+next:
+               ret = btrfs_next_item(tree_root, &path);
+               if (ret > 0)
+                       goto out;
+               if (ret < 0) {
+                       err = ret;
+                       goto out;
+               }
+       }
+
+out:
+       btrfs_release_path(&path);
        return err;
 }
 
@@ -6653,8 +7037,7 @@ static int check_space_cache(struct btrfs_root *root)
                        btrfs_remove_free_space_cache(cache);
                }
 
-               if (btrfs_fs_compat_ro(root->fs_info,
-                                      BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) {
+               if (btrfs_fs_compat_ro(root->fs_info, FREE_SPACE_TREE)) {
                        ret = exclude_super_stripes(root, cache);
                        if (ret) {
                                fprintf(stderr, "could not exclude super stripes: %s\n",
@@ -7666,7 +8049,7 @@ static int record_extent(struct btrfs_trans_handle *trans,
                         struct extent_backref *back,
                         int allocated, u64 flags)
 {
-       int ret;
+       int ret = 0;
        struct btrfs_root *extent_root = info->extent_root;
        struct extent_buffer *leaf;
        struct btrfs_key ins_key;
@@ -8623,6 +9006,10 @@ out:
                        ret = err;
        }
 
+       if (!ret)
+               fprintf(stderr, "Repaired extent references for %llu\n",
+                               (unsigned long long)rec->start);
+
        btrfs_release_path(&path);
        return ret;
 }
@@ -8680,7 +9067,12 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info,
        btrfs_set_extent_flags(path.nodes[0], ei, flags);
        btrfs_mark_buffer_dirty(path.nodes[0]);
        btrfs_release_path(&path);
-       return btrfs_commit_transaction(trans, root);
+       ret = btrfs_commit_transaction(trans, root);
+       if (!ret)
+               fprintf(stderr, "Repaired extent flags for %llu\n",
+                               (unsigned long long)rec->start);
+
+       return ret;
 }
 
 /* right now we only prune from the extent allocation tree */
@@ -8807,11 +9199,8 @@ static int check_extent_refs(struct btrfs_root *root,
 {
        struct extent_record *rec;
        struct cache_extent *cache;
-       int err = 0;
        int ret = 0;
-       int fixed = 0;
        int had_dups = 0;
-       int recorded = 0;
 
        if (repair) {
                /*
@@ -8880,9 +9269,8 @@ static int check_extent_refs(struct btrfs_root *root,
 
        while(1) {
                int cur_err = 0;
+               int fix = 0;
 
-               fixed = 0;
-               recorded = 0;
                cache = search_cache_extent(extent_cache, 0);
                if (!cache)
                        break;
@@ -8890,7 +9278,6 @@ static int check_extent_refs(struct btrfs_root *root,
                if (rec->num_duplicates) {
                        fprintf(stderr, "extent item %llu has multiple extent "
                                "items\n", (unsigned long long)rec->start);
-                       err = 1;
                        cur_err = 1;
                }
 
@@ -8904,54 +9291,31 @@ static int check_extent_refs(struct btrfs_root *root,
                        ret = record_orphan_data_extents(root->fs_info, rec);
                        if (ret < 0)
                                goto repair_abort;
-                       if (ret == 0) {
-                               recorded = 1;
-                       } else {
-                               /*
-                                * we can't use the extent to repair file
-                                * extent, let the fallback method handle it.
-                                */
-                               if (!fixed && repair) {
-                                       ret = fixup_extent_refs(
-                                                       root->fs_info,
-                                                       extent_cache, rec);
-                                       if (ret)
-                                               goto repair_abort;
-                                       fixed = 1;
-                               }
-                       }
-                       err = 1;
+                       fix = ret;
                        cur_err = 1;
                }
                if (all_backpointers_checked(rec, 1)) {
                        fprintf(stderr, "backpointer mismatch on [%llu %llu]\n",
                                (unsigned long long)rec->start,
                                (unsigned long long)rec->nr);
-
-                       if (!fixed && !recorded && repair) {
-                               ret = fixup_extent_refs(root->fs_info,
-                                                       extent_cache, rec);
-                               if (ret)
-                                       goto repair_abort;
-                               fixed = 1;
-                       }
+                       fix = 1;
                        cur_err = 1;
-                       err = 1;
                }
                if (!rec->owner_ref_checked) {
                        fprintf(stderr, "owner ref check failed [%llu %llu]\n",
                                (unsigned long long)rec->start,
                                (unsigned long long)rec->nr);
-                       if (!fixed && !recorded && repair) {
-                               ret = fixup_extent_refs(root->fs_info,
-                                                       extent_cache, rec);
-                               if (ret)
-                                       goto repair_abort;
-                               fixed = 1;
-                       }
-                       err = 1;
+                       fix = 1;
                        cur_err = 1;
                }
+
+               if (repair && fix) {
+                       ret = fixup_extent_refs(root->fs_info, extent_cache, rec);
+                       if (ret)
+                               goto repair_abort;
+               }
+
+
                if (rec->bad_full_backref) {
                        fprintf(stderr, "bad full backref, on [%llu]\n",
                                (unsigned long long)rec->start);
@@ -8959,9 +9323,8 @@ static int check_extent_refs(struct btrfs_root *root,
                                ret = fixup_extent_flags(root->fs_info, rec);
                                if (ret)
                                        goto repair_abort;
-                               fixed = 1;
+                               fix = 1;
                        }
-                       err = 1;
                        cur_err = 1;
                }
                /*
@@ -8973,7 +9336,6 @@ static int check_extent_refs(struct btrfs_root *root,
                        fprintf(stderr,
                                "bad metadata [%llu, %llu) crossing stripe boundary\n",
                                rec->start, rec->start + rec->max_size);
-                       err = 1;
                        cur_err = 1;
                }
 
@@ -8981,13 +9343,12 @@ static int check_extent_refs(struct btrfs_root *root,
                        fprintf(stderr,
                                "bad extent [%llu, %llu), type mismatch with chunk\n",
                                rec->start, rec->start + rec->max_size);
-                       err = 1;
                        cur_err = 1;
                }
 
                remove_cache_extent(extent_cache, cache);
                free_all_extent_backrefs(rec);
-               if (!init_extent_tree && repair && (!cur_err || fixed))
+               if (!init_extent_tree && repair && (!cur_err || fix))
                        clear_extent_dirty(root->fs_info->excluded_extents,
                                           rec->start,
                                           rec->start + rec->max_size - 1,
@@ -9014,11 +9375,9 @@ repair_abort:
                        if (ret)
                                goto repair_abort;
                }
-               if (err)
-                       fprintf(stderr, "repaired damaged extent references\n");
                return ret;
        }
-       return err;
+       return 0;
 }
 
 u64 calc_stripe_length(u64 type, u64 length, int num_stripes)
@@ -9624,14 +9983,18 @@ static int check_tree_block_ref(struct btrfs_root *root,
        u32 nodesize = root->nodesize;
        u32 item_size;
        u64 offset;
+       int tree_reloc_root = 0;
        int found_ref = 0;
        int err = 0;
        int ret;
 
+       if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID &&
+           btrfs_header_bytenr(root->node) == bytenr)
+               tree_reloc_root = 1;
+
        btrfs_init_path(&path);
        key.objectid = bytenr;
-       if (btrfs_fs_incompat(root->fs_info,
-                             BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA))
+       if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA))
                key.type = BTRFS_METADATA_ITEM_KEY;
        else
                key.type = BTRFS_EXTENT_ITEM_KEY;
@@ -9716,9 +10079,16 @@ static int check_tree_block_ref(struct btrfs_root *root,
                        (offset == root->objectid || offset == owner)) {
                        found_ref = 1;
                } else if (type == BTRFS_SHARED_BLOCK_REF_KEY) {
+                       /*
+                        * Backref of tree reloc root points to itself, no need
+                        * to check backref any more.
+                        */
+                       if (tree_reloc_root)
+                               found_ref = 1;
+                       else
                        /* Check if the backref points to valid referencer */
-                       found_ref = !check_tree_block_ref(root, NULL, offset,
-                                                         level + 1, owner);
+                               found_ref = !check_tree_block_ref(root, NULL,
+                                               offset, level + 1, owner);
                }
 
                if (found_ref)
@@ -9769,12 +10139,10 @@ static int check_extent_data_item(struct btrfs_root *root,
        struct btrfs_extent_inline_ref *iref;
        struct btrfs_extent_data_ref *dref;
        u64 owner;
-       u64 file_extent_gen;
        u64 disk_bytenr;
        u64 disk_num_bytes;
        u64 extent_num_bytes;
        u64 extent_flags;
-       u64 extent_gen;
        u32 item_size;
        unsigned long end;
        unsigned long ptr;
@@ -9786,7 +10154,6 @@ static int check_extent_data_item(struct btrfs_root *root,
 
        btrfs_item_key_to_cpu(eb, &fi_key, slot);
        fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
-       file_extent_gen = btrfs_file_extent_generation(eb, fi);
 
        /* Nothing to check for hole and inline data extents */
        if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE ||
@@ -9835,7 +10202,6 @@ static int check_extent_data_item(struct btrfs_root *root,
        ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
 
        extent_flags = btrfs_extent_flags(leaf, ei);
-       extent_gen = btrfs_extent_generation(leaf, ei);
 
        if (!(extent_flags & BTRFS_EXTENT_FLAG_DATA)) {
                error(
@@ -9845,14 +10211,6 @@ static int check_extent_data_item(struct btrfs_root *root,
                err |= BACKREF_MISMATCH;
        }
 
-       if (file_extent_gen < extent_gen) {
-               error(
-"extent[%llu %llu] backref generation mismatch, wanted: <=%llu, have: %llu",
-                       disk_bytenr, disk_num_bytes, file_extent_gen,
-                       extent_gen);
-               err |= BACKREF_MISMATCH;
-       }
-
        /* Check data backref inside that extent item */
        item_size = btrfs_item_size_nr(leaf, path.slots[0]);
        iref = (struct btrfs_extent_inline_ref *)(ei + 1);
@@ -10079,6 +10437,34 @@ out:
 }
 
 /*
+ * Check if tree block @eb is tree reloc root.
+ * Return 0 if it's not or any problem happens
+ * Return 1 if it's a tree reloc root
+ */
+static int is_tree_reloc_root(struct btrfs_fs_info *fs_info,
+                                struct extent_buffer *eb)
+{
+       struct btrfs_root *tree_reloc_root;
+       struct btrfs_key key;
+       u64 bytenr = btrfs_header_bytenr(eb);
+       u64 owner = btrfs_header_owner(eb);
+       int ret = 0;
+
+       key.objectid = BTRFS_TREE_RELOC_OBJECTID;
+       key.offset = owner;
+       key.type = BTRFS_ROOT_ITEM_KEY;
+
+       tree_reloc_root = btrfs_read_fs_root_no_cache(fs_info, &key);
+       if (IS_ERR(tree_reloc_root))
+               return 0;
+
+       if (bytenr == btrfs_header_bytenr(tree_reloc_root->node))
+               ret = 1;
+       btrfs_free_fs_root(tree_reloc_root);
+       return ret;
+}
+
+/*
  * Check referencer for shared block backref
  * If level == -1, this function will resolve the level.
  */
@@ -10100,6 +10486,13 @@ static int check_shared_block_backref(struct btrfs_fs_info *fs_info,
        if (level < 0)
                goto out;
 
+       /* It's possible it's a tree reloc root */
+       if (parent == bytenr) {
+               if (is_tree_reloc_root(fs_info, eb))
+                       found_parent = 1;
+               goto out;
+       }
+
        if (level + 1 != btrfs_header_level(eb))
                goto out;
 
@@ -11037,7 +11430,11 @@ static int check_chunks_and_extents_v2(struct btrfs_root *root)
                        goto next;
                key.offset = (u64)-1;
 
-               cur_root = btrfs_read_fs_root(root->fs_info, &key);
+               if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+                       cur_root = btrfs_read_fs_root_no_cache(root->fs_info,
+                                       &key);
+               else
+                       cur_root = btrfs_read_fs_root(root->fs_info, &key);
                if (IS_ERR(cur_root) || !cur_root) {
                        error("failed to read tree: %lld", key.objectid);
                        goto next;
@@ -11046,6 +11443,8 @@ static int check_chunks_and_extents_v2(struct btrfs_root *root)
                ret = traverse_tree_block(cur_root, cur_root->node);
                err |= ret;
 
+               if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+                       btrfs_free_fs_root(cur_root);
 next:
                ret = btrfs_next_item(root1, &path);
                if (ret)
@@ -11403,7 +11802,7 @@ static int reinit_extent_tree(struct btrfs_trans_handle *trans,
         * the leaves of any fs roots and pin down the bytes for any file
         * extents we find.  Not hard but why do it if we don't have to?
         */
-       if (btrfs_fs_incompat(fs_info, BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)) {
+       if (btrfs_fs_incompat(fs_info, MIXED_GROUPS)) {
                fprintf(stderr, "We don't support re-initing the extent tree "
                        "for mixed block groups yet, please notify a btrfs "
                        "developer you want to do this so they can add this "
@@ -12190,6 +12589,7 @@ int cmd_check(int argc, char **argv)
        u64 chunk_root_bytenr = 0;
        char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
        int ret;
+       int err = 0;
        u64 num;
        int init_csum_tree = 0;
        int readonly = 0;
@@ -12339,10 +12739,12 @@ int cmd_check(int argc, char **argv)
 
        if((ret = check_mounted(argv[optind])) < 0) {
                error("could not check mount status: %s", strerror(-ret));
+               err |= !!ret;
                goto err_out;
        } else if(ret) {
                error("%s is currently mounted, aborting", argv[optind]);
                ret = -EBUSY;
+               err |= !!ret;
                goto err_out;
        }
 
@@ -12355,14 +12757,14 @@ int cmd_check(int argc, char **argv)
        if (!info) {
                error("cannot open file system");
                ret = -EIO;
+               err |= !!ret;
                goto err_out;
        }
 
        global_info = info;
        root = info->fs_root;
        if (clear_space_cache == 1) {
-               if (btrfs_fs_compat_ro(info,
-                               BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) {
+               if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
                        error(
                "free space cache v2 detected, use --clear-space-cache v2");
                        ret = 1;
@@ -12378,8 +12780,7 @@ int cmd_check(int argc, char **argv)
                }
                goto close_out;
        } else if (clear_space_cache == 2) {
-               if (!btrfs_fs_compat_ro(info,
-                                       BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) {
+               if (!btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
                        printf("no free space cache v2 to clear\n");
                        ret = 0;
                        goto close_out;
@@ -12403,9 +12804,11 @@ int cmd_check(int argc, char **argv)
                ret = ask_user("repair mode will force to clear out log tree, are you sure?");
                if (!ret) {
                        ret = 1;
+                       err |= !!ret;
                        goto close_out;
                }
                ret = zero_log_tree(root);
+               err |= !!ret;
                if (ret) {
                        error("failed to zero log tree: %d", ret);
                        goto close_out;
@@ -12417,6 +12820,7 @@ int cmd_check(int argc, char **argv)
                printf("Print quota groups for %s\nUUID: %s\n", argv[optind],
                       uuidbuf);
                ret = qgroup_verify_all(info);
+               err |= !!ret;
                if (ret == 0)
                        report_qgroups(1);
                goto close_out;
@@ -12425,6 +12829,7 @@ int cmd_check(int argc, char **argv)
                printf("Print extent state for subvolume %llu on %s\nUUID: %s\n",
                       subvolid, argv[optind], uuidbuf);
                ret = print_extent_state(info, subvolid);
+               err |= !!ret;
                goto close_out;
        }
        printf("Checking filesystem on %s\nUUID: %s\n", argv[optind], uuidbuf);
@@ -12433,6 +12838,7 @@ int cmd_check(int argc, char **argv)
            !extent_buffer_uptodate(info->dev_root->node) ||
            !extent_buffer_uptodate(info->chunk_root->node)) {
                error("critical roots corrupted, unable to check the filesystem");
+               err |= !!ret;
                ret = -EIO;
                goto close_out;
        }
@@ -12444,12 +12850,14 @@ int cmd_check(int argc, char **argv)
                if (IS_ERR(trans)) {
                        error("error starting transaction");
                        ret = PTR_ERR(trans);
+                       err |= !!ret;
                        goto close_out;
                }
 
                if (init_extent_tree) {
                        printf("Creating a new extent tree\n");
                        ret = reinit_extent_tree(trans, info);
+                       err |= !!ret;
                        if (ret)
                                goto close_out;
                }
@@ -12461,11 +12869,13 @@ int cmd_check(int argc, char **argv)
                                error("checksum tree initialization failed: %d",
                                                ret);
                                ret = -EIO;
+                               err |= !!ret;
                                goto close_out;
                        }
 
                        ret = fill_csum_tree(trans, info->csum_root,
                                             init_extent_tree);
+                       err |= !!ret;
                        if (ret) {
                                error("checksum tree refilling failed: %d", ret);
                                return -EIO;
@@ -12476,17 +12886,20 @@ int cmd_check(int argc, char **argv)
                 * extent entries for all of the items it finds.
                 */
                ret = btrfs_commit_transaction(trans, info->extent_root);
+               err |= !!ret;
                if (ret)
                        goto close_out;
        }
        if (!extent_buffer_uptodate(info->extent_root->node)) {
                error("critical: extent_root, unable to check the filesystem");
                ret = -EIO;
+               err |= !!ret;
                goto close_out;
        }
        if (!extent_buffer_uptodate(info->csum_root->node)) {
                error("critical: csum_root, unable to check the filesystem");
                ret = -EIO;
+               err |= !!ret;
                goto close_out;
        }
 
@@ -12496,11 +12909,13 @@ int cmd_check(int argc, char **argv)
                ret = check_chunks_and_extents_v2(root);
        else
                ret = check_chunks_and_extents(root);
+       err |= !!ret;
        if (ret)
                error(
                "errors found in extent allocation tree or chunk allocation");
 
        ret = repair_root_items(info);
+       err |= !!ret;
        if (ret < 0)
                goto close_out;
        if (repair) {
@@ -12513,16 +12928,18 @@ int cmd_check(int argc, char **argv)
                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, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE))
+               if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
                        fprintf(stderr, "checking free space tree\n");
                else
                        fprintf(stderr, "checking free space cache\n");
        }
        ret = check_space_cache(root);
+       err |= !!ret;
        if (ret)
                goto out;
 
@@ -12532,23 +12949,31 @@ int cmd_check(int argc, char **argv)
         * are no gaps in the file extents for inodes, otherwise we can just
         * ignore it when this happens.
         */
-       no_holes = btrfs_fs_incompat(root->fs_info,
-                                    BTRFS_FEATURE_INCOMPAT_NO_HOLES);
+       no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
        if (!ctx.progress_enabled)
                fprintf(stderr, "checking fs roots\n");
-       ret = check_fs_roots(root, &root_cache);
+       if (check_mode == CHECK_MODE_LOWMEM)
+               ret = check_fs_roots_v2(root->fs_info);
+       else
+               ret = check_fs_roots(root, &root_cache);
+       err |= !!ret;
        if (ret)
                goto out;
 
        fprintf(stderr, "checking csums\n");
        ret = check_csums(root);
+       err |= !!ret;
        if (ret)
                goto out;
 
        fprintf(stderr, "checking root refs\n");
-       ret = check_root_refs(root, &root_cache);
-       if (ret)
-               goto out;
+       /* For low memory mode, check_fs_roots_v2 handles root refs */
+       if (check_mode != CHECK_MODE_LOWMEM) {
+               ret = check_root_refs(root, &root_cache);
+               err |= !!ret;
+               if (ret)
+                       goto out;
+       }
 
        while (repair && !list_empty(&root->fs_info->recow_ebs)) {
                struct extent_buffer *eb;
@@ -12557,6 +12982,7 @@ int cmd_check(int argc, char **argv)
                                      struct extent_buffer, recow);
                list_del_init(&eb->recow);
                ret = recow_extent_buffer(root, eb);
+               err |= !!ret;
                if (ret)
                        break;
        }
@@ -12566,32 +12992,33 @@ int cmd_check(int argc, char **argv)
 
                bad = list_first_entry(&delete_items, struct bad_item, list);
                list_del_init(&bad->list);
-               if (repair)
+               if (repair) {
                        ret = delete_bad_item(root, bad);
+                       err |= !!ret;
+               }
                free(bad);
        }
 
        if (info->quota_enabled) {
-               int err;
                fprintf(stderr, "checking quota groups\n");
-               err = qgroup_verify_all(info);
-               if (err)
+               ret = qgroup_verify_all(info);
+               err |= !!ret;
+               if (ret)
                        goto out;
                report_qgroups(0);
-               err = repair_qgroups(info, &qgroups_repaired);
+               ret = repair_qgroups(info, &qgroups_repaired);
+               err |= !!ret;
                if (err)
                        goto out;
+               ret = 0;
        }
 
        if (!list_empty(&root->fs_info->recow_ebs)) {
                error("transid errors in file system");
                ret = 1;
+               err |= !!ret;
        }
 out:
-       /* Don't override original ret */
-       if (!ret && qgroups_repaired)
-               ret = qgroups_repaired;
-
        if (found_old_backref) { /*
                 * there was a disk format change when mixed
                 * backref was in testing tree. The old format
@@ -12601,7 +13028,7 @@ out:
                       "The old format is not supported! *"
                       "\n * Please mount the FS in readonly mode, "
                       "backup data and re-format the FS. *\n\n");
-               ret = 1;
+               err |= 1;
        }
        printf("found %llu bytes used err is %d\n",
               (unsigned long long)bytes_used, ret);
@@ -12626,5 +13053,5 @@ err_out:
        if (ctx.progress_enabled)
                task_deinit(ctx.info);
 
-       return ret;
+       return err;
 }