1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * BTRFS filesystem implementation for U-Boot
4 *
5 * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
6 */
7
8 #include "btrfs.h"
9 #include <malloc.h>
10
btrfs_lookup_inode_ref(struct btrfs_root * root,u64 inr,struct btrfs_inode_ref * refp,char * name)11 u64 btrfs_lookup_inode_ref(struct btrfs_root *root, u64 inr,
12 struct btrfs_inode_ref *refp, char *name)
13 {
14 struct btrfs_path path;
15 struct btrfs_key *key;
16 struct btrfs_inode_ref *ref;
17 u64 res = -1ULL;
18
19 key = btrfs_search_tree_key_type(root, inr, BTRFS_INODE_REF_KEY,
20 &path);
21
22 if (!key)
23 return -1ULL;
24
25 ref = btrfs_path_item_ptr(&path, struct btrfs_inode_ref);
26 btrfs_inode_ref_to_cpu(ref);
27
28 if (refp)
29 *refp = *ref;
30
31 if (name) {
32 if (ref->name_len > BTRFS_NAME_MAX) {
33 printf("%s: inode name too long: %u\n", __func__,
34 ref->name_len);
35 goto out;
36 }
37
38 memcpy(name, ref + 1, ref->name_len);
39 }
40
41 res = key->offset;
42 out:
43 btrfs_free_path(&path);
44 return res;
45 }
46
btrfs_lookup_inode(const struct btrfs_root * root,struct btrfs_key * location,struct btrfs_inode_item * item,struct btrfs_root * new_root)47 int btrfs_lookup_inode(const struct btrfs_root *root,
48 struct btrfs_key *location,
49 struct btrfs_inode_item *item,
50 struct btrfs_root *new_root)
51 {
52 struct btrfs_root tmp_root = *root;
53 struct btrfs_path path;
54 int res = -1;
55
56 if (location->type == BTRFS_ROOT_ITEM_KEY) {
57 if (btrfs_find_root(location->objectid, &tmp_root, NULL))
58 return -1;
59
60 location->objectid = tmp_root.root_dirid;
61 location->type = BTRFS_INODE_ITEM_KEY;
62 location->offset = 0;
63 }
64
65 if (btrfs_search_tree(&tmp_root, location, &path))
66 return res;
67
68 if (btrfs_comp_keys(location, btrfs_path_leaf_key(&path)))
69 goto out;
70
71 if (item) {
72 *item = *btrfs_path_item_ptr(&path, struct btrfs_inode_item);
73 btrfs_inode_item_to_cpu(item);
74 }
75
76 if (new_root)
77 *new_root = tmp_root;
78
79 res = 0;
80
81 out:
82 btrfs_free_path(&path);
83 return res;
84 }
85
btrfs_readlink(const struct btrfs_root * root,u64 inr,char * target)86 int btrfs_readlink(const struct btrfs_root *root, u64 inr, char *target)
87 {
88 struct btrfs_path path;
89 struct btrfs_key key;
90 struct btrfs_file_extent_item *extent;
91 const char *data_ptr;
92 int res = -1;
93
94 key.objectid = inr;
95 key.type = BTRFS_EXTENT_DATA_KEY;
96 key.offset = 0;
97
98 if (btrfs_search_tree(root, &key, &path))
99 return -1;
100
101 if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)))
102 goto out;
103
104 extent = btrfs_path_item_ptr(&path, struct btrfs_file_extent_item);
105 if (extent->type != BTRFS_FILE_EXTENT_INLINE) {
106 printf("%s: Extent for symlink %llu not of INLINE type\n",
107 __func__, inr);
108 goto out;
109 }
110
111 btrfs_file_extent_item_to_cpu_inl(extent);
112
113 if (extent->compression != BTRFS_COMPRESS_NONE) {
114 printf("%s: Symlink %llu extent data compressed!\n", __func__,
115 inr);
116 goto out;
117 } else if (extent->encryption != 0) {
118 printf("%s: Symlink %llu extent data encrypted!\n", __func__,
119 inr);
120 goto out;
121 } else if (extent->ram_bytes >= btrfs_info.sb.sectorsize) {
122 printf("%s: Symlink %llu extent data too long (%llu)!\n",
123 __func__, inr, extent->ram_bytes);
124 goto out;
125 }
126
127 data_ptr = (const char *) extent
128 + offsetof(struct btrfs_file_extent_item, disk_bytenr);
129
130 memcpy(target, data_ptr, extent->ram_bytes);
131 target[extent->ram_bytes] = '\0';
132 res = 0;
133 out:
134 btrfs_free_path(&path);
135 return res;
136 }
137
138 /* inr must be a directory (for regular files with multiple hard links this
139 function returns only one of the parents of the file) */
get_parent_inode(struct btrfs_root * root,u64 inr,struct btrfs_inode_item * inode_item)140 static u64 get_parent_inode(struct btrfs_root *root, u64 inr,
141 struct btrfs_inode_item *inode_item)
142 {
143 struct btrfs_key key;
144 u64 res;
145
146 if (inr == BTRFS_FIRST_FREE_OBJECTID) {
147 if (root->objectid != btrfs_info.fs_root.objectid) {
148 u64 parent;
149 struct btrfs_root_ref ref;
150
151 parent = btrfs_lookup_root_ref(root->objectid, &ref,
152 NULL);
153 if (parent == -1ULL)
154 return -1ULL;
155
156 if (btrfs_find_root(parent, root, NULL))
157 return -1ULL;
158
159 inr = ref.dirid;
160 }
161
162 if (inode_item) {
163 key.objectid = inr;
164 key.type = BTRFS_INODE_ITEM_KEY;
165 key.offset = 0;
166
167 if (btrfs_lookup_inode(root, &key, inode_item, NULL))
168 return -1ULL;
169 }
170
171 return inr;
172 }
173
174 res = btrfs_lookup_inode_ref(root, inr, NULL, NULL);
175 if (res == -1ULL)
176 return -1ULL;
177
178 if (inode_item) {
179 key.objectid = res;
180 key.type = BTRFS_INODE_ITEM_KEY;
181 key.offset = 0;
182
183 if (btrfs_lookup_inode(root, &key, inode_item, NULL))
184 return -1ULL;
185 }
186
187 return res;
188 }
189
next_length(const char * path)190 static inline int next_length(const char *path)
191 {
192 int res = 0;
193 while (*path != '\0' && *path != '/' && res <= BTRFS_NAME_LEN)
194 ++res, ++path;
195 return res;
196 }
197
skip_current_directories(const char * cur)198 static inline const char *skip_current_directories(const char *cur)
199 {
200 while (1) {
201 if (cur[0] == '/')
202 ++cur;
203 else if (cur[0] == '.' && cur[1] == '/')
204 cur += 2;
205 else
206 break;
207 }
208
209 return cur;
210 }
211
btrfs_lookup_path(struct btrfs_root * root,u64 inr,const char * path,u8 * type_p,struct btrfs_inode_item * inode_item_p,int symlink_limit)212 u64 btrfs_lookup_path(struct btrfs_root *root, u64 inr, const char *path,
213 u8 *type_p, struct btrfs_inode_item *inode_item_p,
214 int symlink_limit)
215 {
216 struct btrfs_dir_item item;
217 struct btrfs_inode_item inode_item;
218 u8 type = BTRFS_FT_DIR;
219 int len, have_inode = 0;
220 const char *cur = path;
221
222 if (*cur == '/') {
223 ++cur;
224 inr = root->root_dirid;
225 }
226
227 do {
228 cur = skip_current_directories(cur);
229
230 len = next_length(cur);
231 if (len > BTRFS_NAME_LEN) {
232 printf("%s: Name too long at \"%.*s\"\n", __func__,
233 BTRFS_NAME_LEN, cur);
234 return -1ULL;
235 }
236
237 if (len == 1 && cur[0] == '.')
238 break;
239
240 if (len == 2 && cur[0] == '.' && cur[1] == '.') {
241 cur += 2;
242 inr = get_parent_inode(root, inr, &inode_item);
243 if (inr == -1ULL)
244 return -1ULL;
245
246 type = BTRFS_FT_DIR;
247 continue;
248 }
249
250 if (!*cur)
251 break;
252
253 if (btrfs_lookup_dir_item(root, inr, cur, len, &item))
254 return -1ULL;
255
256 type = item.type;
257 have_inode = 1;
258 if (btrfs_lookup_inode(root, &item.location, &inode_item, root))
259 return -1ULL;
260
261 if (item.type == BTRFS_FT_SYMLINK && symlink_limit >= 0) {
262 char *target;
263
264 if (!symlink_limit) {
265 printf("%s: Too much symlinks!\n", __func__);
266 return -1ULL;
267 }
268
269 target = malloc(min(inode_item.size + 1,
270 (u64) btrfs_info.sb.sectorsize));
271 if (!target)
272 return -1ULL;
273
274 if (btrfs_readlink(root, item.location.objectid,
275 target)) {
276 free(target);
277 return -1ULL;
278 }
279
280 inr = btrfs_lookup_path(root, inr, target, &type,
281 &inode_item, symlink_limit - 1);
282
283 free(target);
284
285 if (inr == -1ULL)
286 return -1ULL;
287 } else if (item.type != BTRFS_FT_DIR && cur[len]) {
288 printf("%s: \"%.*s\" not a directory\n", __func__,
289 (int) (cur - path + len), path);
290 return -1ULL;
291 } else {
292 inr = item.location.objectid;
293 }
294
295 cur += len;
296 } while (*cur);
297
298 if (type_p)
299 *type_p = type;
300
301 if (inode_item_p) {
302 if (!have_inode) {
303 struct btrfs_key key;
304
305 key.objectid = inr;
306 key.type = BTRFS_INODE_ITEM_KEY;
307 key.offset = 0;
308
309 if (btrfs_lookup_inode(root, &key, &inode_item, NULL))
310 return -1ULL;
311 }
312
313 *inode_item_p = inode_item;
314 }
315
316 return inr;
317 }
318
btrfs_file_read(const struct btrfs_root * root,u64 inr,u64 offset,u64 size,char * buf)319 u64 btrfs_file_read(const struct btrfs_root *root, u64 inr, u64 offset,
320 u64 size, char *buf)
321 {
322 struct btrfs_path path;
323 struct btrfs_key key;
324 struct btrfs_file_extent_item *extent;
325 int res = 0;
326 u64 rd, rd_all = -1ULL;
327
328 key.objectid = inr;
329 key.type = BTRFS_EXTENT_DATA_KEY;
330 key.offset = offset;
331
332 if (btrfs_search_tree(root, &key, &path))
333 return -1ULL;
334
335 if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)) < 0) {
336 if (btrfs_prev_slot(&path))
337 goto out;
338
339 if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
340 goto out;
341 }
342
343 rd_all = 0;
344
345 do {
346 if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
347 break;
348
349 extent = btrfs_path_item_ptr(&path,
350 struct btrfs_file_extent_item);
351
352 if (extent->type == BTRFS_FILE_EXTENT_INLINE) {
353 btrfs_file_extent_item_to_cpu_inl(extent);
354 rd = btrfs_read_extent_inline(&path, extent, offset,
355 size, buf);
356 } else {
357 btrfs_file_extent_item_to_cpu(extent);
358 rd = btrfs_read_extent_reg(&path, extent, offset, size,
359 buf);
360 }
361
362 if (rd == -1ULL) {
363 printf("%s: Error reading extent\n", __func__);
364 rd_all = -1;
365 goto out;
366 }
367
368 offset = 0;
369 buf += rd;
370 rd_all += rd;
371 size -= rd;
372
373 if (!size)
374 break;
375 } while (!(res = btrfs_next_slot(&path)));
376
377 if (res)
378 return -1ULL;
379
380 out:
381 btrfs_free_path(&path);
382 return rd_all;
383 }
384