f785b600fac20004ec739cd4ee1a75b9970a9991
[platform/kernel/u-boot.git] / fs / btrfs / inode.c
1 /*
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
12 u64 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;
43 out:
44         btrfs_free_path(&path);
45         return res;
46 }
47
48 int 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
82 out:
83         btrfs_free_path(&path);
84         return res;
85 }
86
87 int 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;
134 out:
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) */
141 static 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
191 static 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
199 static 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 u64 btrfs_lookup_path(struct btrfs_root *root, u64 inr, const char *path,
214                       u8 *type_p, struct btrfs_inode_item *inode_item_p,
215                       int symlink_limit)
216 {
217         struct btrfs_dir_item item;
218         struct btrfs_inode_item inode_item;
219         u8 type = BTRFS_FT_DIR;
220         int len, have_inode = 0;
221         const char *cur = path;
222
223         if (*cur == '/') {
224                 ++cur;
225                 inr = root->root_dirid;
226         }
227
228         do {
229                 cur = skip_current_directories(cur);
230
231                 len = next_length(cur);
232                 if (len > BTRFS_NAME_LEN) {
233                         printf("%s: Name too long at \"%.*s\"\n", __func__,
234                                BTRFS_NAME_LEN, cur);
235                         return -1ULL;
236                 }
237
238                 if (len == 1 && cur[0] == '.')
239                         break;
240
241                 if (len == 2 && cur[0] == '.' && cur[1] == '.') {
242                         cur += 2;
243                         inr = get_parent_inode(root, inr, &inode_item);
244                         if (inr == -1ULL)
245                                 return -1ULL;
246
247                         type = BTRFS_FT_DIR;
248                         continue;
249                 }
250
251                 if (!*cur)
252                         break;
253                 
254                 if (btrfs_lookup_dir_item(root, inr, cur, len, &item))
255                         return -1ULL;
256
257                 type = item.type;
258                 have_inode = 1;
259                 if (btrfs_lookup_inode(root, &item.location, &inode_item, root))
260                         return -1ULL;
261
262                 if (item.type == BTRFS_FT_SYMLINK && symlink_limit >= 0) {
263                         char *target;
264
265                         if (!symlink_limit) {
266                                 printf("%s: Too much symlinks!\n", __func__);
267                                 return -1ULL;
268                         }
269
270                         target = malloc(min(inode_item.size + 1,
271                                             (u64) btrfs_info.sb.sectorsize));
272                         if (!target)
273                                 return -1ULL;
274
275                         if (btrfs_readlink(root, item.location.objectid,
276                                            target)) {
277                                 free(target);
278                                 return -1ULL;
279                         }
280
281                         inr = btrfs_lookup_path(root, inr, target, &type,
282                                                 &inode_item, symlink_limit - 1);
283
284                         free(target);
285
286                         if (inr == -1ULL)
287                                 return -1ULL;
288                 } else if (item.type != BTRFS_FT_DIR && cur[len]) {
289                         printf("%s: \"%.*s\" not a directory\n", __func__,
290                                (int) (cur - path + len), path);
291                         return -1ULL;
292                 } else {
293                         inr = item.location.objectid;
294                 }
295
296                 cur += len;
297         } while (*cur);
298
299         if (type_p)
300                 *type_p = type;
301
302         if (inode_item_p) {
303                 if (!have_inode) {
304                         struct btrfs_key key;
305
306                         key.objectid = inr;
307                         key.type = BTRFS_INODE_ITEM_KEY;
308                         key.offset = 0;
309
310                         if (btrfs_lookup_inode(root, &key, &inode_item, NULL))
311                                 return -1ULL;
312                 }
313
314                 *inode_item_p = inode_item;
315         }
316
317         return inr;
318 }
319
320 u64 btrfs_file_read(const struct btrfs_root *root, u64 inr, u64 offset,
321                     u64 size, char *buf)
322 {
323         struct btrfs_path path;
324         struct btrfs_key key;
325         struct btrfs_file_extent_item *extent;
326         int res = 0;
327         u64 rd, rd_all = -1ULL;
328
329         key.objectid = inr;
330         key.type = BTRFS_EXTENT_DATA_KEY;
331         key.offset = offset;
332
333         if (btrfs_search_tree(root, &key, &path))
334                 return -1ULL;
335
336         if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)) < 0) {
337                 if (btrfs_prev_slot(&path))
338                         goto out;
339
340                 if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
341                         goto out;
342         }
343
344         rd_all = 0;
345
346         do {
347                 if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
348                         break;
349
350                 extent = btrfs_path_item_ptr(&path,
351                                              struct btrfs_file_extent_item);
352
353                 if (extent->type == BTRFS_FILE_EXTENT_INLINE) {
354                         btrfs_file_extent_item_to_cpu_inl(extent);
355                         rd = btrfs_read_extent_inline(&path, extent, offset,
356                                                       size, buf);
357                 } else {
358                         btrfs_file_extent_item_to_cpu(extent);
359                         rd = btrfs_read_extent_reg(&path, extent, offset, size,
360                                                    buf);
361                 }
362
363                 if (rd == -1ULL) {
364                         printf("%s: Error reading extent\n", __func__);
365                         rd_all = -1;
366                         goto out;
367                 }
368
369                 offset = 0;
370                 buf += rd;
371                 rd_all += rd;
372                 size -= rd;
373
374                 if (!size)
375                         break;
376         } while (!(res = btrfs_next_slot(&path)));
377
378         if (res)
379                 return -1ULL;
380
381 out:
382         btrfs_free_path(&path);
383         return rd_all;
384 }