7392ed6b472f3f24a8f0d63b8a8a15f47f67dbc6
[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 "check/common.h"
24
25 /*
26  * Search in csum tree to find how many bytes of range [@start, @start + @len)
27  * has the corresponding csum item.
28  *
29  * @start:      range start
30  * @len:        range length
31  * @found:      return value of found csum bytes
32  *              unit is BYTE.
33  */
34 int count_csum_range(struct btrfs_fs_info *fs_info, u64 start,
35                      u64 len, u64 *found)
36 {
37         struct btrfs_key key;
38         struct btrfs_path path;
39         struct extent_buffer *leaf;
40         int ret;
41         size_t size;
42         *found = 0;
43         u64 csum_end;
44         u16 csum_size = btrfs_super_csum_size(fs_info->super_copy);
45
46         btrfs_init_path(&path);
47
48         key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
49         key.offset = start;
50         key.type = BTRFS_EXTENT_CSUM_KEY;
51
52         ret = btrfs_search_slot(NULL, fs_info->csum_root,
53                                 &key, &path, 0, 0);
54         if (ret < 0)
55                 goto out;
56         if (ret > 0 && path.slots[0] > 0) {
57                 leaf = path.nodes[0];
58                 btrfs_item_key_to_cpu(leaf, &key, path.slots[0] - 1);
59                 if (key.objectid == BTRFS_EXTENT_CSUM_OBJECTID &&
60                     key.type == BTRFS_EXTENT_CSUM_KEY)
61                         path.slots[0]--;
62         }
63
64         while (len > 0) {
65                 leaf = path.nodes[0];
66                 if (path.slots[0] >= btrfs_header_nritems(leaf)) {
67                         ret = btrfs_next_leaf(fs_info->csum_root, &path);
68                         if (ret > 0)
69                                 break;
70                         else if (ret < 0)
71                                 goto out;
72                         leaf = path.nodes[0];
73                 }
74
75                 btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
76                 if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID ||
77                     key.type != BTRFS_EXTENT_CSUM_KEY)
78                         break;
79
80                 btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
81                 if (key.offset >= start + len)
82                         break;
83
84                 if (key.offset > start)
85                         start = key.offset;
86
87                 size = btrfs_item_size_nr(leaf, path.slots[0]);
88                 csum_end = key.offset + (size / csum_size) *
89                            fs_info->sectorsize;
90                 if (csum_end > start) {
91                         size = min(csum_end - start, len);
92                         len -= size;
93                         start += size;
94                         *found += size;
95                 }
96
97                 path.slots[0]++;
98         }
99 out:
100         btrfs_release_path(&path);
101         if (ret < 0)
102                 return ret;
103         return 0;
104 }
105
106 /*
107  * Wrapper to insert one inode item into given @root
108  * Timestamp will be set to current time.
109  *
110  * @root:       the root to insert inode item into
111  * @ino:        inode number
112  * @size:       inode size
113  * @nbytes:     nbytes (real used size, without hole)
114  * @nlink:      number of links
115  * @mode:       file mode, including S_IF* bits
116  */
117 int insert_inode_item(struct btrfs_trans_handle *trans,
118                       struct btrfs_root *root, u64 ino, u64 size,
119                       u64 nbytes, u64 nlink, u32 mode)
120 {
121         struct btrfs_inode_item ii;
122         time_t now = time(NULL);
123         int ret;
124
125         btrfs_set_stack_inode_size(&ii, size);
126         btrfs_set_stack_inode_nbytes(&ii, nbytes);
127         btrfs_set_stack_inode_nlink(&ii, nlink);
128         btrfs_set_stack_inode_mode(&ii, mode);
129         btrfs_set_stack_inode_generation(&ii, trans->transid);
130         btrfs_set_stack_timespec_nsec(&ii.atime, 0);
131         btrfs_set_stack_timespec_sec(&ii.ctime, now);
132         btrfs_set_stack_timespec_nsec(&ii.ctime, 0);
133         btrfs_set_stack_timespec_sec(&ii.mtime, now);
134         btrfs_set_stack_timespec_nsec(&ii.mtime, 0);
135         btrfs_set_stack_timespec_sec(&ii.otime, 0);
136         btrfs_set_stack_timespec_nsec(&ii.otime, 0);
137
138         ret = btrfs_insert_inode(trans, root, ino, &ii);
139         ASSERT(!ret);
140
141         warning("root %llu inode %llu recreating inode item, this may "
142                 "be incomplete, please check permissions and content after "
143                 "the fsck completes.\n", (unsigned long long)root->objectid,
144                 (unsigned long long)ino);
145
146         return 0;
147 }
148
149 static int get_highest_inode(struct btrfs_trans_handle *trans,
150                              struct btrfs_root *root, struct btrfs_path *path,
151                              u64 *highest_ino)
152 {
153         struct btrfs_key key, found_key;
154         int ret;
155
156         btrfs_init_path(path);
157         key.objectid = BTRFS_LAST_FREE_OBJECTID;
158         key.offset = -1;
159         key.type = BTRFS_INODE_ITEM_KEY;
160         ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
161         if (ret == 1) {
162                 btrfs_item_key_to_cpu(path->nodes[0], &found_key,
163                                 path->slots[0] - 1);
164                 *highest_ino = found_key.objectid;
165                 ret = 0;
166         }
167         if (*highest_ino >= BTRFS_LAST_FREE_OBJECTID)
168                 ret = -EOVERFLOW;
169         btrfs_release_path(path);
170         return ret;
171 }
172
173 /*
174  * Link inode to dir 'lost+found'. Increase @ref_count.
175  *
176  * Returns 0 means success.
177  * Returns <0 means failure.
178  */
179 int link_inode_to_lostfound(struct btrfs_trans_handle *trans,
180                             struct btrfs_root *root,
181                             struct btrfs_path *path,
182                             u64 ino, char *namebuf, u32 name_len,
183                             u8 filetype, u64 *ref_count)
184 {
185         char *dir_name = "lost+found";
186         u64 lost_found_ino;
187         int ret;
188         u32 mode = 0700;
189
190         btrfs_release_path(path);
191         ret = get_highest_inode(trans, root, path, &lost_found_ino);
192         if (ret < 0)
193                 goto out;
194         lost_found_ino++;
195
196         ret = btrfs_mkdir(trans, root, dir_name, strlen(dir_name),
197                           BTRFS_FIRST_FREE_OBJECTID, &lost_found_ino,
198                           mode);
199         if (ret < 0) {
200                 error("failed to create '%s' dir: %s", dir_name, strerror(-ret));
201                 goto out;
202         }
203         ret = btrfs_add_link(trans, root, ino, lost_found_ino,
204                              namebuf, name_len, filetype, NULL, 1, 0);
205         /*
206          * Add ".INO" suffix several times to handle case where
207          * "FILENAME.INO" is already taken by another file.
208          */
209         while (ret == -EEXIST) {
210                 /*
211                  * Conflicting file name, add ".INO" as suffix * +1 for '.'
212                  */
213                 if (name_len + count_digits(ino) + 1 > BTRFS_NAME_LEN) {
214                         ret = -EFBIG;
215                         goto out;
216                 }
217                 snprintf(namebuf + name_len, BTRFS_NAME_LEN - name_len,
218                          ".%llu", ino);
219                 name_len += count_digits(ino) + 1;
220                 ret = btrfs_add_link(trans, root, ino, lost_found_ino, namebuf,
221                                      name_len, filetype, NULL, 1, 0);
222         }
223         if (ret < 0) {
224                 error("failed to link the inode %llu to %s dir: %s",
225                       ino, dir_name, strerror(-ret));
226                 goto out;
227         }
228
229         ++*ref_count;
230         printf("Moving file '%.*s' to '%s' dir since it has no valid backref\n",
231                name_len, namebuf, dir_name);
232 out:
233         btrfs_release_path(path);
234         if (ret)
235                 error("failed to move file '%.*s' to '%s' dir", name_len,
236                                 namebuf, dir_name);
237         return ret;
238 }
239
240 /*
241  * Extra (optional) check for dev_item size to report possbile problem on a new
242  * kernel.
243  */
244 void check_dev_size_alignment(u64 devid, u64 total_bytes, u32 sectorsize)
245 {
246         if (!IS_ALIGNED(total_bytes, sectorsize)) {
247                 warning(
248 "unaligned total_bytes detected for devid %llu, have %llu should be aligned to %u",
249                         devid, total_bytes, sectorsize);
250                 warning(
251 "this is OK for older kernel, but may cause kernel warning for newer kernels");
252                 warning("this can be fixed by 'btrfs rescue fix-device-size'");
253         }
254 }