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