btrfs-progs: convert: move implementation for interal conversion API to own file
[platform/upstream/btrfs-progs.git] / find-root.c
1 /*
2  * Copyright (C) 2015 Fujitsu.  All rights reserved.
3  *
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.
7  *
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.
12  *
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.
17  */
18
19 #include "kerncompat.h"
20
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"
29
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)
36 {
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;
44
45         if (owner != filter->objectid || level < filter->level ||
46             generation < filter->generation)
47                 return ret;
48
49         /*
50          * Get the generation cache or create one
51          *
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.
54          */
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);
65
66                 ret = insert_cache_extent(result, cache);
67                 if (ret < 0)
68                         return ret;
69         }
70         gen_cache = container_of(cache, struct btrfs_find_root_gen_cache,
71                                  cache);
72
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 */
78         }
79
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;
87         }
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);
95         }
96         return ret;
97 }
98
99 /*
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
103  */
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)
108 {
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;
116
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;
132                 }
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,
137                                                      0);
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;
145                 }
146         }
147 out:
148         fs_info->suppress_check_block_errors = suppress_errors;
149         return ret;
150 }