btrfs-progs: check: Move check_child_node to check/common.c
[platform/upstream/btrfs-progs.git] / check / common.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #include <time.h>
18 #include "ctree.h"
19 #include "internal.h"
20 #include "messages.h"
21 #include "transaction.h"
22 #include "utils.h"
23 #include "disk-io.h"
24 #include "check/common.h"
25
26 /*
27  * Search in csum tree to find how many bytes of range [@start, @start + @len)
28  * has the corresponding csum item.
29  *
30  * @start:      range start
31  * @len:        range length
32  * @found:      return value of found csum bytes
33  *              unit is BYTE.
34  */
35 int count_csum_range(struct btrfs_fs_info *fs_info, u64 start,
36                      u64 len, u64 *found)
37 {
38         struct btrfs_key key;
39         struct btrfs_path path;
40         struct extent_buffer *leaf;
41         int ret;
42         size_t size;
43         *found = 0;
44         u64 csum_end;
45         u16 csum_size = btrfs_super_csum_size(fs_info->super_copy);
46
47         btrfs_init_path(&path);
48
49         key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
50         key.offset = start;
51         key.type = BTRFS_EXTENT_CSUM_KEY;
52
53         ret = btrfs_search_slot(NULL, fs_info->csum_root,
54                                 &key, &path, 0, 0);
55         if (ret < 0)
56                 goto out;
57         if (ret > 0 && path.slots[0] > 0) {
58                 leaf = path.nodes[0];
59                 btrfs_item_key_to_cpu(leaf, &key, path.slots[0] - 1);
60                 if (key.objectid == BTRFS_EXTENT_CSUM_OBJECTID &&
61                     key.type == BTRFS_EXTENT_CSUM_KEY)
62                         path.slots[0]--;
63         }
64
65         while (len > 0) {
66                 leaf = path.nodes[0];
67                 if (path.slots[0] >= btrfs_header_nritems(leaf)) {
68                         ret = btrfs_next_leaf(fs_info->csum_root, &path);
69                         if (ret > 0)
70                                 break;
71                         else if (ret < 0)
72                                 goto out;
73                         leaf = path.nodes[0];
74                 }
75
76                 btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
77                 if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID ||
78                     key.type != BTRFS_EXTENT_CSUM_KEY)
79                         break;
80
81                 btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
82                 if (key.offset >= start + len)
83                         break;
84
85                 if (key.offset > start)
86                         start = key.offset;
87
88                 size = btrfs_item_size_nr(leaf, path.slots[0]);
89                 csum_end = key.offset + (size / csum_size) *
90                            fs_info->sectorsize;
91                 if (csum_end > start) {
92                         size = min(csum_end - start, len);
93                         len -= size;
94                         start += size;
95                         *found += size;
96                 }
97
98                 path.slots[0]++;
99         }
100 out:
101         btrfs_release_path(&path);
102         if (ret < 0)
103                 return ret;
104         return 0;
105 }
106
107 /*
108  * Wrapper to insert one inode item into given @root
109  * Timestamp will be set to current time.
110  *
111  * @root:       the root to insert inode item into
112  * @ino:        inode number
113  * @size:       inode size
114  * @nbytes:     nbytes (real used size, without hole)
115  * @nlink:      number of links
116  * @mode:       file mode, including S_IF* bits
117  */
118 int insert_inode_item(struct btrfs_trans_handle *trans,
119                       struct btrfs_root *root, u64 ino, u64 size,
120                       u64 nbytes, u64 nlink, u32 mode)
121 {
122         struct btrfs_inode_item ii;
123         time_t now = time(NULL);
124         int ret;
125
126         btrfs_set_stack_inode_size(&ii, size);
127         btrfs_set_stack_inode_nbytes(&ii, nbytes);
128         btrfs_set_stack_inode_nlink(&ii, nlink);
129         btrfs_set_stack_inode_mode(&ii, mode);
130         btrfs_set_stack_inode_generation(&ii, trans->transid);
131         btrfs_set_stack_timespec_nsec(&ii.atime, 0);
132         btrfs_set_stack_timespec_sec(&ii.ctime, now);
133         btrfs_set_stack_timespec_nsec(&ii.ctime, 0);
134         btrfs_set_stack_timespec_sec(&ii.mtime, now);
135         btrfs_set_stack_timespec_nsec(&ii.mtime, 0);
136         btrfs_set_stack_timespec_sec(&ii.otime, 0);
137         btrfs_set_stack_timespec_nsec(&ii.otime, 0);
138
139         ret = btrfs_insert_inode(trans, root, ino, &ii);
140         ASSERT(!ret);
141
142         warning("root %llu inode %llu recreating inode item, this may "
143                 "be incomplete, please check permissions and content after "
144                 "the fsck completes.\n", (unsigned long long)root->objectid,
145                 (unsigned long long)ino);
146
147         return 0;
148 }
149
150 static int get_highest_inode(struct btrfs_trans_handle *trans,
151                              struct btrfs_root *root, struct btrfs_path *path,
152                              u64 *highest_ino)
153 {
154         struct btrfs_key key, found_key;
155         int ret;
156
157         btrfs_init_path(path);
158         key.objectid = BTRFS_LAST_FREE_OBJECTID;
159         key.offset = -1;
160         key.type = BTRFS_INODE_ITEM_KEY;
161         ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
162         if (ret == 1) {
163                 btrfs_item_key_to_cpu(path->nodes[0], &found_key,
164                                 path->slots[0] - 1);
165                 *highest_ino = found_key.objectid;
166                 ret = 0;
167         }
168         if (*highest_ino >= BTRFS_LAST_FREE_OBJECTID)
169                 ret = -EOVERFLOW;
170         btrfs_release_path(path);
171         return ret;
172 }
173
174 /*
175  * Link inode to dir 'lost+found'. Increase @ref_count.
176  *
177  * Returns 0 means success.
178  * Returns <0 means failure.
179  */
180 int link_inode_to_lostfound(struct btrfs_trans_handle *trans,
181                             struct btrfs_root *root,
182                             struct btrfs_path *path,
183                             u64 ino, char *namebuf, u32 name_len,
184                             u8 filetype, u64 *ref_count)
185 {
186         char *dir_name = "lost+found";
187         u64 lost_found_ino;
188         int ret;
189         u32 mode = 0700;
190
191         btrfs_release_path(path);
192         ret = get_highest_inode(trans, root, path, &lost_found_ino);
193         if (ret < 0)
194                 goto out;
195         lost_found_ino++;
196
197         ret = btrfs_mkdir(trans, root, dir_name, strlen(dir_name),
198                           BTRFS_FIRST_FREE_OBJECTID, &lost_found_ino,
199                           mode);
200         if (ret < 0) {
201                 error("failed to create '%s' dir: %s", dir_name, strerror(-ret));
202                 goto out;
203         }
204         ret = btrfs_add_link(trans, root, ino, lost_found_ino,
205                              namebuf, name_len, filetype, NULL, 1, 0);
206         /*
207          * Add ".INO" suffix several times to handle case where
208          * "FILENAME.INO" is already taken by another file.
209          */
210         while (ret == -EEXIST) {
211                 /*
212                  * Conflicting file name, add ".INO" as suffix * +1 for '.'
213                  */
214                 if (name_len + count_digits(ino) + 1 > BTRFS_NAME_LEN) {
215                         ret = -EFBIG;
216                         goto out;
217                 }
218                 snprintf(namebuf + name_len, BTRFS_NAME_LEN - name_len,
219                          ".%llu", ino);
220                 name_len += count_digits(ino) + 1;
221                 ret = btrfs_add_link(trans, root, ino, lost_found_ino, namebuf,
222                                      name_len, filetype, NULL, 1, 0);
223         }
224         if (ret < 0) {
225                 error("failed to link the inode %llu to %s dir: %s",
226                       ino, dir_name, strerror(-ret));
227                 goto out;
228         }
229
230         ++*ref_count;
231         printf("Moving file '%.*s' to '%s' dir since it has no valid backref\n",
232                name_len, namebuf, dir_name);
233 out:
234         btrfs_release_path(path);
235         if (ret)
236                 error("failed to move file '%.*s' to '%s' dir", name_len,
237                                 namebuf, dir_name);
238         return ret;
239 }
240
241 /*
242  * Extra (optional) check for dev_item size to report possbile problem on a new
243  * kernel.
244  */
245 void check_dev_size_alignment(u64 devid, u64 total_bytes, u32 sectorsize)
246 {
247         if (!IS_ALIGNED(total_bytes, sectorsize)) {
248                 warning(
249 "unaligned total_bytes detected for devid %llu, have %llu should be aligned to %u",
250                         devid, total_bytes, sectorsize);
251                 warning(
252 "this is OK for older kernel, but may cause kernel warning for newer kernels");
253                 warning("this can be fixed by 'btrfs rescue fix-device-size'");
254         }
255 }
256
257 void reada_walk_down(struct btrfs_root *root, struct extent_buffer *node,
258                      int slot)
259 {
260         struct btrfs_fs_info *fs_info = root->fs_info;
261         u64 bytenr;
262         u64 ptr_gen;
263         u32 nritems;
264         int i;
265         int level;
266
267         level = btrfs_header_level(node);
268         if (level != 1)
269                 return;
270
271         nritems = btrfs_header_nritems(node);
272         for (i = slot; i < nritems; i++) {
273                 bytenr = btrfs_node_blockptr(node, i);
274                 ptr_gen = btrfs_node_ptr_generation(node, i);
275                 readahead_tree_block(fs_info, bytenr, ptr_gen);
276         }
277 }
278
279 /*
280  * Check the child node/leaf by the following condition:
281  * 1. the first item key of the node/leaf should be the same with the one
282  *    in parent.
283  * 2. block in parent node should match the child node/leaf.
284  * 3. generation of parent node and child's header should be consistent.
285  *
286  * Or the child node/leaf pointed by the key in parent is not valid.
287  *
288  * We hope to check leaf owner too, but since subvol may share leaves,
289  * which makes leaf owner check not so strong, key check should be
290  * sufficient enough for that case.
291  */
292 int check_child_node(struct extent_buffer *parent, int slot,
293                      struct extent_buffer *child)
294 {
295         struct btrfs_key parent_key;
296         struct btrfs_key child_key;
297         int ret = 0;
298
299         btrfs_node_key_to_cpu(parent, &parent_key, slot);
300         if (btrfs_header_level(child) == 0)
301                 btrfs_item_key_to_cpu(child, &child_key, 0);
302         else
303                 btrfs_node_key_to_cpu(child, &child_key, 0);
304
305         if (memcmp(&parent_key, &child_key, sizeof(parent_key))) {
306                 ret = -EINVAL;
307                 fprintf(stderr,
308                         "Wrong key of child node/leaf, wanted: (%llu, %u, %llu), have: (%llu, %u, %llu)\n",
309                         parent_key.objectid, parent_key.type, parent_key.offset,
310                         child_key.objectid, child_key.type, child_key.offset);
311         }
312         if (btrfs_header_bytenr(child) != btrfs_node_blockptr(parent, slot)) {
313                 ret = -EINVAL;
314                 fprintf(stderr, "Wrong block of child node/leaf, wanted: %llu, have: %llu\n",
315                         btrfs_node_blockptr(parent, slot),
316                         btrfs_header_bytenr(child));
317         }
318         if (btrfs_node_ptr_generation(parent, slot) !=
319             btrfs_header_generation(child)) {
320                 ret = -EINVAL;
321                 fprintf(stderr, "Wrong generation of child node/leaf, wanted: %llu, have: %llu\n",
322                         btrfs_header_generation(child),
323                         btrfs_node_ptr_generation(parent, slot));
324         }
325         return ret;
326 }