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