btrfs-progs: check: get the highest inode for lost+found
[platform/upstream/btrfs-progs.git] / cmds-check.c
index 4cd0fcd..a55d00d 100644 (file)
@@ -2853,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,
@@ -2898,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,
@@ -3266,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.
         *
@@ -4972,7 +4980,7 @@ out:
  */
 static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
 {
-       struct btrfs_path *path;
+       struct btrfs_path path;
        struct node_refs nrefs;
        struct btrfs_root_item *root_item = &root->root_item;
        int ret, wret;
@@ -4988,38 +4996,35 @@ static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
        if (ret < 0)
                return ret;
 
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-
        memset(&nrefs, 0, sizeof(nrefs));
        level = btrfs_header_level(root->node);
+       btrfs_init_path(&path);
 
        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;
+               path.nodes[level] = root->node;
+               path.slots[level] = 0;
                extent_buffer_get(root->node);
        } else {
                struct btrfs_key key;
 
                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);
+               path.lowest_level = level;
+               ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
                if (ret < 0)
                        goto out;
                ret = 0;
        }
 
        while (1) {
-               wret = walk_down_tree_v2(root, path, &level, &nrefs, ext_ref);
+               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);
+               wret = walk_up_tree_v2(root, &path, &level);
                if (wret < 0)
                        ret = wret;
                if (wret != 0)
@@ -5027,7 +5032,7 @@ static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
        }
 
 out:
-       btrfs_free_path(path);
+       btrfs_release_path(&path);
        return ret;
 }
 
@@ -5134,7 +5139,7 @@ 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_path path;
        struct btrfs_key key;
        struct extent_buffer *node;
        unsigned int ext_ref;
@@ -5144,15 +5149,12 @@ static int check_fs_roots_v2(struct btrfs_fs_info *fs_info)
 
        ext_ref = btrfs_fs_incompat(fs_info, EXTENDED_IREF);
 
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-
+       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);
+       ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
        if (ret < 0) {
                err = ret;
                goto out;
@@ -5162,8 +5164,8 @@ static int check_fs_roots_v2(struct btrfs_fs_info *fs_info)
        }
 
        while (1) {
-               node = path->nodes[0];
-               slot = path->slots[0];
+               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;
@@ -5195,7 +5197,7 @@ static int check_fs_roots_v2(struct btrfs_fs_info *fs_info)
                        err |= ret;
                }
 next:
-               ret = btrfs_next_item(tree_root, path);
+               ret = btrfs_next_item(tree_root, &path);
                if (ret > 0)
                        goto out;
                if (ret < 0) {
@@ -5205,7 +5207,7 @@ next:
        }
 
 out:
-       btrfs_free_path(path);
+       btrfs_release_path(&path);
        return err;
 }
 
@@ -8047,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;
@@ -9004,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;
 }
@@ -9061,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 */
@@ -9188,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) {
                /*
@@ -9261,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;
@@ -9271,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;
                }
 
@@ -9285,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);
@@ -9340,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;
                }
                /*
@@ -9354,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;
                }
 
@@ -9362,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,
@@ -9395,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)
@@ -10005,10 +9983,15 @@ 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, SKINNY_METADATA))
@@ -10096,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)
@@ -10149,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;
@@ -10166,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 ||
@@ -10215,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(
@@ -10225,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);
@@ -10459,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.
  */
@@ -10480,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;
 
@@ -11417,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;
@@ -11426,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)