blob: 007cf32c1603beadf6af05fe581d9185edf8faf9 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Marek Behún21a14fa2017-09-03 17:00:28 +02002/*
3 * BTRFS filesystem implementation for U-Boot
4 *
5 * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
Marek Behún21a14fa2017-09-03 17:00:28 +02006 */
7
Marek Behún21a14fa2017-09-03 17:00:28 +02008#include <malloc.h>
Qu Wenruo92bc1792020-06-24 18:03:03 +02009#include "btrfs.h"
10#include "disk-io.h"
Marek Behún21a14fa2017-09-03 17:00:28 +020011
Qu Wenruocafffc52020-06-24 18:03:02 +020012u64 __btrfs_lookup_inode_ref(struct __btrfs_root *root, u64 inr,
Marek Behún21a14fa2017-09-03 17:00:28 +020013 struct btrfs_inode_ref *refp, char *name)
14{
Qu Wenruo33966de2020-06-24 18:02:56 +020015 struct __btrfs_path path;
Marek Behún21a14fa2017-09-03 17:00:28 +020016 struct btrfs_key *key;
17 struct btrfs_inode_ref *ref;
18 u64 res = -1ULL;
19
20 key = btrfs_search_tree_key_type(root, inr, BTRFS_INODE_REF_KEY,
21 &path);
22
23 if (!key)
24 return -1ULL;
25
26 ref = btrfs_path_item_ptr(&path, struct btrfs_inode_ref);
27 btrfs_inode_ref_to_cpu(ref);
28
29 if (refp)
30 *refp = *ref;
31
32 if (name) {
Qu Wenruo3b4b40c2020-06-24 18:02:47 +020033 if (ref->name_len > BTRFS_NAME_LEN) {
Marek Behún21a14fa2017-09-03 17:00:28 +020034 printf("%s: inode name too long: %u\n", __func__,
35 ref->name_len);
36 goto out;
37 }
38
39 memcpy(name, ref + 1, ref->name_len);
40 }
41
42 res = key->offset;
43out:
Qu Wenruo33966de2020-06-24 18:02:56 +020044 __btrfs_free_path(&path);
Marek Behún21a14fa2017-09-03 17:00:28 +020045 return res;
46}
47
Qu Wenruocafffc52020-06-24 18:03:02 +020048int __btrfs_lookup_inode(const struct __btrfs_root *root,
Marek Behún21a14fa2017-09-03 17:00:28 +020049 struct btrfs_key *location,
50 struct btrfs_inode_item *item,
Qu Wenruo207011b2020-06-24 18:02:57 +020051 struct __btrfs_root *new_root)
Marek Behún21a14fa2017-09-03 17:00:28 +020052{
Qu Wenruo207011b2020-06-24 18:02:57 +020053 struct __btrfs_root tmp_root = *root;
Qu Wenruo33966de2020-06-24 18:02:56 +020054 struct __btrfs_path path;
Marek Behún21a14fa2017-09-03 17:00:28 +020055 int res = -1;
56
57 if (location->type == BTRFS_ROOT_ITEM_KEY) {
58 if (btrfs_find_root(location->objectid, &tmp_root, NULL))
59 return -1;
60
61 location->objectid = tmp_root.root_dirid;
62 location->type = BTRFS_INODE_ITEM_KEY;
63 location->offset = 0;
64 }
65
66 if (btrfs_search_tree(&tmp_root, location, &path))
67 return res;
68
Qu Wenruo75b08172020-06-24 18:02:55 +020069 if (__btrfs_comp_keys(location, btrfs_path_leaf_key(&path)))
Marek Behún21a14fa2017-09-03 17:00:28 +020070 goto out;
71
72 if (item) {
73 *item = *btrfs_path_item_ptr(&path, struct btrfs_inode_item);
74 btrfs_inode_item_to_cpu(item);
75 }
76
77 if (new_root)
78 *new_root = tmp_root;
79
80 res = 0;
81
82out:
Qu Wenruo33966de2020-06-24 18:02:56 +020083 __btrfs_free_path(&path);
Marek Behún21a14fa2017-09-03 17:00:28 +020084 return res;
85}
86
Qu Wenruo92bc1792020-06-24 18:03:03 +020087/*
88 * Read the content of symlink inode @ino of @root, into @target.
89 * NOTE: @target will not be \0 termiated, caller should handle it properly.
90 *
91 * Return the number of read data.
92 * Return <0 for error.
93 */
94int btrfs_readlink(struct btrfs_root *root, u64 ino, char *target)
Marek Behún21a14fa2017-09-03 17:00:28 +020095{
Qu Wenruo92bc1792020-06-24 18:03:03 +020096 struct btrfs_path path;
Marek Behún21a14fa2017-09-03 17:00:28 +020097 struct btrfs_key key;
Qu Wenruo92bc1792020-06-24 18:03:03 +020098 struct btrfs_file_extent_item *fi;
99 int ret;
Marek Behún21a14fa2017-09-03 17:00:28 +0200100
Qu Wenruo92bc1792020-06-24 18:03:03 +0200101 key.objectid = ino;
Marek Behún21a14fa2017-09-03 17:00:28 +0200102 key.type = BTRFS_EXTENT_DATA_KEY;
103 key.offset = 0;
Qu Wenruo92bc1792020-06-24 18:03:03 +0200104 btrfs_init_path(&path);
Marek Behún21a14fa2017-09-03 17:00:28 +0200105
Qu Wenruo92bc1792020-06-24 18:03:03 +0200106 ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
107 if (ret < 0)
108 return ret;
109 if (ret > 0) {
110 ret = -ENOENT;
111 goto out;
112 }
113 fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
114 struct btrfs_file_extent_item);
115 if (btrfs_file_extent_type(path.nodes[0], fi) !=
116 BTRFS_FILE_EXTENT_INLINE) {
117 ret = -EUCLEAN;
118 error("Extent for symlink %llu must be INLINE type!", ino);
119 goto out;
120 }
121 if (btrfs_file_extent_compression(path.nodes[0], fi) !=
122 BTRFS_COMPRESS_NONE) {
123 ret = -EUCLEAN;
124 error("Extent for symlink %llu must not be compressed!", ino);
125 goto out;
126 }
127 if (btrfs_file_extent_ram_bytes(path.nodes[0], fi) >=
128 root->fs_info->sectorsize) {
129 ret = -EUCLEAN;
130 error("Symlink %llu extent data too large (%llu)!\n",
131 ino, btrfs_file_extent_ram_bytes(path.nodes[0], fi));
132 goto out;
133 }
134 read_extent_buffer(path.nodes[0], target,
135 btrfs_file_extent_inline_start(fi),
136 btrfs_file_extent_ram_bytes(path.nodes[0], fi));
137 ret = btrfs_file_extent_ram_bytes(path.nodes[0], fi);
138out:
139 btrfs_release_path(&path);
140 return ret;
141}
142
143int __btrfs_readlink(const struct __btrfs_root *root, u64 inr, char *target)
144{
145 struct btrfs_root *subvolume;
146 struct btrfs_fs_info *fs_info = current_fs_info;
147 struct btrfs_key key;
148 int ret;
149
150 ASSERT(fs_info);
151 key.objectid = root->objectid;
152 key.type = BTRFS_ROOT_ITEM_KEY;
153 key.offset = (u64)-1;
154 subvolume = btrfs_read_fs_root(fs_info, &key);
155 if (IS_ERR(subvolume))
Marek Behún21a14fa2017-09-03 17:00:28 +0200156 return -1;
157
Qu Wenruo92bc1792020-06-24 18:03:03 +0200158 ret = btrfs_readlink(subvolume, inr, target);
159 if (ret < 0)
160 return -1;
161 target[ret] = '\0';
162 return 0;
Marek Behún21a14fa2017-09-03 17:00:28 +0200163}
164
165/* inr must be a directory (for regular files with multiple hard links this
166 function returns only one of the parents of the file) */
Qu Wenruocafffc52020-06-24 18:03:02 +0200167static u64 __get_parent_inode(struct __btrfs_root *root, u64 inr,
Marek Behún21a14fa2017-09-03 17:00:28 +0200168 struct btrfs_inode_item *inode_item)
169{
170 struct btrfs_key key;
171 u64 res;
172
173 if (inr == BTRFS_FIRST_FREE_OBJECTID) {
174 if (root->objectid != btrfs_info.fs_root.objectid) {
175 u64 parent;
176 struct btrfs_root_ref ref;
177
178 parent = btrfs_lookup_root_ref(root->objectid, &ref,
179 NULL);
180 if (parent == -1ULL)
181 return -1ULL;
182
183 if (btrfs_find_root(parent, root, NULL))
184 return -1ULL;
185
186 inr = ref.dirid;
187 }
188
189 if (inode_item) {
190 key.objectid = inr;
191 key.type = BTRFS_INODE_ITEM_KEY;
192 key.offset = 0;
193
Qu Wenruocafffc52020-06-24 18:03:02 +0200194 if (__btrfs_lookup_inode(root, &key, inode_item, NULL))
Marek Behún21a14fa2017-09-03 17:00:28 +0200195 return -1ULL;
196 }
197
198 return inr;
199 }
200
Qu Wenruocafffc52020-06-24 18:03:02 +0200201 res = __btrfs_lookup_inode_ref(root, inr, NULL, NULL);
Marek Behún21a14fa2017-09-03 17:00:28 +0200202 if (res == -1ULL)
203 return -1ULL;
204
205 if (inode_item) {
206 key.objectid = res;
207 key.type = BTRFS_INODE_ITEM_KEY;
208 key.offset = 0;
209
Qu Wenruocafffc52020-06-24 18:03:02 +0200210 if (__btrfs_lookup_inode(root, &key, inode_item, NULL))
Marek Behún21a14fa2017-09-03 17:00:28 +0200211 return -1ULL;
212 }
213
214 return res;
215}
216
217static inline int next_length(const char *path)
218{
219 int res = 0;
220 while (*path != '\0' && *path != '/' && res <= BTRFS_NAME_LEN)
221 ++res, ++path;
222 return res;
223}
224
225static inline const char *skip_current_directories(const char *cur)
226{
227 while (1) {
228 if (cur[0] == '/')
229 ++cur;
230 else if (cur[0] == '.' && cur[1] == '/')
231 cur += 2;
232 else
233 break;
234 }
235
236 return cur;
237}
238
Qu Wenruocafffc52020-06-24 18:03:02 +0200239u64 __btrfs_lookup_path(struct __btrfs_root *root, u64 inr, const char *path,
Marek Behún21a14fa2017-09-03 17:00:28 +0200240 u8 *type_p, struct btrfs_inode_item *inode_item_p,
241 int symlink_limit)
242{
243 struct btrfs_dir_item item;
244 struct btrfs_inode_item inode_item;
245 u8 type = BTRFS_FT_DIR;
246 int len, have_inode = 0;
247 const char *cur = path;
248
249 if (*cur == '/') {
250 ++cur;
251 inr = root->root_dirid;
252 }
253
254 do {
255 cur = skip_current_directories(cur);
256
257 len = next_length(cur);
258 if (len > BTRFS_NAME_LEN) {
259 printf("%s: Name too long at \"%.*s\"\n", __func__,
260 BTRFS_NAME_LEN, cur);
261 return -1ULL;
262 }
263
264 if (len == 1 && cur[0] == '.')
265 break;
266
267 if (len == 2 && cur[0] == '.' && cur[1] == '.') {
268 cur += 2;
Qu Wenruocafffc52020-06-24 18:03:02 +0200269 inr = __get_parent_inode(root, inr, &inode_item);
Marek Behún21a14fa2017-09-03 17:00:28 +0200270 if (inr == -1ULL)
271 return -1ULL;
272
273 type = BTRFS_FT_DIR;
274 continue;
275 }
276
277 if (!*cur)
278 break;
279
Qu Wenruocafffc52020-06-24 18:03:02 +0200280 if (__btrfs_lookup_dir_item(root, inr, cur, len, &item))
Marek Behún21a14fa2017-09-03 17:00:28 +0200281 return -1ULL;
282
283 type = item.type;
284 have_inode = 1;
Qu Wenruocafffc52020-06-24 18:03:02 +0200285 if (__btrfs_lookup_inode(root, (struct btrfs_key *)&item.location,
Qu Wenruo3b4b40c2020-06-24 18:02:47 +0200286 &inode_item, root))
Marek Behún21a14fa2017-09-03 17:00:28 +0200287 return -1ULL;
288
289 if (item.type == BTRFS_FT_SYMLINK && symlink_limit >= 0) {
290 char *target;
291
292 if (!symlink_limit) {
293 printf("%s: Too much symlinks!\n", __func__);
294 return -1ULL;
295 }
296
297 target = malloc(min(inode_item.size + 1,
298 (u64) btrfs_info.sb.sectorsize));
299 if (!target)
300 return -1ULL;
301
Qu Wenruocafffc52020-06-24 18:03:02 +0200302 if (__btrfs_readlink(root, item.location.objectid,
Marek Behún21a14fa2017-09-03 17:00:28 +0200303 target)) {
304 free(target);
305 return -1ULL;
306 }
307
Qu Wenruocafffc52020-06-24 18:03:02 +0200308 inr = __btrfs_lookup_path(root, inr, target, &type,
Marek Behún21a14fa2017-09-03 17:00:28 +0200309 &inode_item, symlink_limit - 1);
310
311 free(target);
312
313 if (inr == -1ULL)
314 return -1ULL;
315 } else if (item.type != BTRFS_FT_DIR && cur[len]) {
316 printf("%s: \"%.*s\" not a directory\n", __func__,
317 (int) (cur - path + len), path);
318 return -1ULL;
319 } else {
320 inr = item.location.objectid;
321 }
322
323 cur += len;
324 } while (*cur);
325
326 if (type_p)
327 *type_p = type;
328
329 if (inode_item_p) {
330 if (!have_inode) {
331 struct btrfs_key key;
332
333 key.objectid = inr;
334 key.type = BTRFS_INODE_ITEM_KEY;
335 key.offset = 0;
336
Qu Wenruocafffc52020-06-24 18:03:02 +0200337 if (__btrfs_lookup_inode(root, &key, &inode_item, NULL))
Marek Behún21a14fa2017-09-03 17:00:28 +0200338 return -1ULL;
339 }
340
341 *inode_item_p = inode_item;
342 }
343
344 return inr;
345}
346
Qu Wenruo207011b2020-06-24 18:02:57 +0200347u64 btrfs_file_read(const struct __btrfs_root *root, u64 inr, u64 offset,
Marek Behún21a14fa2017-09-03 17:00:28 +0200348 u64 size, char *buf)
349{
Qu Wenruo33966de2020-06-24 18:02:56 +0200350 struct __btrfs_path path;
Marek Behún21a14fa2017-09-03 17:00:28 +0200351 struct btrfs_key key;
352 struct btrfs_file_extent_item *extent;
Marek Behúnecab8812017-10-06 15:04:57 +0200353 int res = 0;
Marek Behún21a14fa2017-09-03 17:00:28 +0200354 u64 rd, rd_all = -1ULL;
355
356 key.objectid = inr;
357 key.type = BTRFS_EXTENT_DATA_KEY;
358 key.offset = offset;
359
360 if (btrfs_search_tree(root, &key, &path))
361 return -1ULL;
362
Qu Wenruo75b08172020-06-24 18:02:55 +0200363 if (__btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)) < 0) {
Marek Behún21a14fa2017-09-03 17:00:28 +0200364 if (btrfs_prev_slot(&path))
365 goto out;
366
367 if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
368 goto out;
369 }
370
371 rd_all = 0;
372
373 do {
374 if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
375 break;
376
377 extent = btrfs_path_item_ptr(&path,
378 struct btrfs_file_extent_item);
379
380 if (extent->type == BTRFS_FILE_EXTENT_INLINE) {
381 btrfs_file_extent_item_to_cpu_inl(extent);
382 rd = btrfs_read_extent_inline(&path, extent, offset,
383 size, buf);
384 } else {
385 btrfs_file_extent_item_to_cpu(extent);
386 rd = btrfs_read_extent_reg(&path, extent, offset, size,
387 buf);
388 }
389
390 if (rd == -1ULL) {
391 printf("%s: Error reading extent\n", __func__);
392 rd_all = -1;
393 goto out;
394 }
395
396 offset = 0;
397 buf += rd;
398 rd_all += rd;
399 size -= rd;
400
401 if (!size)
402 break;
403 } while (!(res = btrfs_next_slot(&path)));
404
405 if (res)
406 return -1ULL;
407
408out:
Qu Wenruo33966de2020-06-24 18:02:56 +0200409 __btrfs_free_path(&path);
Marek Behún21a14fa2017-09-03 17:00:28 +0200410 return rd_all;
411}