btrfs-progs: check: get the highest inode for lost+found
[platform/upstream/btrfs-progs.git] / cmds-check.c
index fc19e14..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.
         *
@@ -10131,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;
@@ -10148,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 ||
@@ -10197,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(
@@ -10207,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);
@@ -10441,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.
  */
@@ -10462,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;