xref: /openbmc/u-boot/fs/btrfs/inode.c (revision 70c56c138f3776b68f24acb09a9bc3ba67855d0a)
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  }
385