btrfs-progs: check: enhance return values of walk_down_tree_v2
[btrfs-progs-unstable/devel.git] / find-root.c
blob89d36119ebc9fc18f3ed3b43aa0e2a424a63e49b
1 /*
2 * Copyright (C) 2015 Fujitsu. All rights reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
19 #include "kerncompat.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include "ctree.h"
24 #include "utils.h"
25 #include "find-root.h"
26 #include "volumes.h"
27 #include "disk-io.h"
28 #include "extent-cache.h"
30 /* Return value is the same as btrfs_find_root_search(). */
31 static int add_eb_to_result(struct extent_buffer *eb,
32 struct cache_tree *result,
33 u32 nodesize,
34 struct btrfs_find_root_filter *filter,
35 struct cache_extent **match)
37 u64 generation = btrfs_header_generation(eb);
38 u64 level = btrfs_header_level(eb);
39 u64 owner = btrfs_header_owner(eb);
40 u64 start = eb->start;
41 struct cache_extent *cache;
42 struct btrfs_find_root_gen_cache *gen_cache = NULL;
43 int ret = 0;
45 if (owner != filter->objectid || level < filter->level ||
46 generation < filter->generation)
47 return ret;
50 * Get the generation cache or create one
52 * NOTE: search_cache_extent() may return cache that doesn't cover
53 * the range. So we need an extra check to make sure it's the right one.
55 cache = search_cache_extent(result, generation);
56 if (!cache || cache->start != generation) {
57 gen_cache = malloc(sizeof(*gen_cache));
58 BUG_ON(!gen_cache);
59 cache = &gen_cache->cache;
60 cache->start = generation;
61 cache->size = 1;
62 cache->objectid = 0;
63 gen_cache->highest_level = 0;
64 cache_tree_init(&gen_cache->eb_tree);
66 ret = insert_cache_extent(result, cache);
67 if (ret < 0)
68 return ret;
70 gen_cache = container_of(cache, struct btrfs_find_root_gen_cache,
71 cache);
73 /* Higher level, clean tree and insert the new one */
74 if (level > gen_cache->highest_level) {
75 free_extent_cache_tree(&gen_cache->eb_tree);
76 gen_cache->highest_level = level;
77 /* Fall into the insert routine */
80 /* Same level, insert it into the eb_tree */
81 if (level == gen_cache->highest_level) {
82 ret = add_cache_extent(&gen_cache->eb_tree,
83 start, nodesize);
84 if (ret < 0 && ret != -EEXIST)
85 return ret;
86 ret = 0;
88 if (generation == filter->match_gen &&
89 level == filter->match_level &&
90 !filter->search_all) {
91 ret = 1;
92 if (match)
93 *match = search_cache_extent(&gen_cache->eb_tree,
94 start);
96 return ret;
100 * Return 0 if iterating all the metadata extents.
101 * Return 1 if found root with given gen/level and set *match to it.
102 * Return <0 if error happens
104 int btrfs_find_root_search(struct btrfs_fs_info *fs_info,
105 struct btrfs_find_root_filter *filter,
106 struct cache_tree *result,
107 struct cache_extent **match)
109 struct extent_buffer *eb;
110 u64 chunk_offset = 0;
111 u64 chunk_size = 0;
112 u64 offset = 0;
113 u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
114 int suppress_errors = 0;
115 int ret = 0;
117 suppress_errors = fs_info->suppress_check_block_errors;
118 fs_info->suppress_check_block_errors = 1;
119 while (1) {
120 if (filter->objectid != BTRFS_CHUNK_TREE_OBJECTID)
121 ret = btrfs_next_bg_metadata(&fs_info->mapping_tree,
122 &chunk_offset,
123 &chunk_size);
124 else
125 ret = btrfs_next_bg_system(&fs_info->mapping_tree,
126 &chunk_offset,
127 &chunk_size);
128 if (ret) {
129 if (ret == -ENOENT)
130 ret = 0;
131 break;
133 for (offset = chunk_offset;
134 offset < chunk_offset + chunk_size;
135 offset += nodesize) {
136 eb = read_tree_block_fs_info(fs_info, offset, nodesize,
138 if (!eb || IS_ERR(eb))
139 continue;
140 ret = add_eb_to_result(eb, result, nodesize, filter,
141 match);
142 free_extent_buffer(eb);
143 if (ret)
144 goto out;
147 out:
148 fs_info->suppress_check_block_errors = suppress_errors;
149 return ret;