fs: btrfs: support sparse extents
[oweals/u-boot.git] / fs / btrfs / inode.c
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
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
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
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) */
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
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
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
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
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 }