]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
21a14fac MB |
2 | /* |
3 | * BTRFS filesystem implementation for U-Boot | |
4 | * | |
61143f74 | 5 | * 2017 Marek Behún, CZ.NIC, kabel@kernel.org |
21a14fac MB |
6 | */ |
7 | ||
a26a6bed | 8 | #include <linux/kernel.h> |
21a14fac | 9 | #include <malloc.h> |
a26a6bed | 10 | #include <memalign.h> |
92bc179c QW |
11 | #include "btrfs.h" |
12 | #include "disk-io.h" | |
a26a6bed | 13 | #include "volumes.h" |
21a14fac | 14 | |
92bc179c QW |
15 | /* |
16 | * Read the content of symlink inode @ino of @root, into @target. | |
17 | * NOTE: @target will not be \0 termiated, caller should handle it properly. | |
18 | * | |
19 | * Return the number of read data. | |
20 | * Return <0 for error. | |
21 | */ | |
22 | int btrfs_readlink(struct btrfs_root *root, u64 ino, char *target) | |
21a14fac | 23 | { |
92bc179c | 24 | struct btrfs_path path; |
21a14fac | 25 | struct btrfs_key key; |
92bc179c QW |
26 | struct btrfs_file_extent_item *fi; |
27 | int ret; | |
21a14fac | 28 | |
92bc179c | 29 | key.objectid = ino; |
21a14fac MB |
30 | key.type = BTRFS_EXTENT_DATA_KEY; |
31 | key.offset = 0; | |
92bc179c | 32 | btrfs_init_path(&path); |
21a14fac | 33 | |
92bc179c QW |
34 | ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); |
35 | if (ret < 0) | |
36 | return ret; | |
37 | if (ret > 0) { | |
38 | ret = -ENOENT; | |
21a14fac MB |
39 | goto out; |
40 | } | |
92bc179c QW |
41 | fi = btrfs_item_ptr(path.nodes[0], path.slots[0], |
42 | struct btrfs_file_extent_item); | |
43 | if (btrfs_file_extent_type(path.nodes[0], fi) != | |
44 | BTRFS_FILE_EXTENT_INLINE) { | |
45 | ret = -EUCLEAN; | |
46 | error("Extent for symlink %llu must be INLINE type!", ino); | |
21a14fac | 47 | goto out; |
92bc179c QW |
48 | } |
49 | if (btrfs_file_extent_compression(path.nodes[0], fi) != | |
50 | BTRFS_COMPRESS_NONE) { | |
51 | ret = -EUCLEAN; | |
52 | error("Extent for symlink %llu must not be compressed!", ino); | |
21a14fac | 53 | goto out; |
92bc179c QW |
54 | } |
55 | if (btrfs_file_extent_ram_bytes(path.nodes[0], fi) >= | |
56 | root->fs_info->sectorsize) { | |
57 | ret = -EUCLEAN; | |
58 | error("Symlink %llu extent data too large (%llu)!\n", | |
59 | ino, btrfs_file_extent_ram_bytes(path.nodes[0], fi)); | |
21a14fac MB |
60 | goto out; |
61 | } | |
92bc179c QW |
62 | read_extent_buffer(path.nodes[0], target, |
63 | btrfs_file_extent_inline_start(fi), | |
64 | btrfs_file_extent_ram_bytes(path.nodes[0], fi)); | |
65 | ret = btrfs_file_extent_ram_bytes(path.nodes[0], fi); | |
66 | out: | |
67 | btrfs_release_path(&path); | |
68 | return ret; | |
69 | } | |
21a14fac | 70 | |
c921aa20 QW |
71 | static int lookup_root_ref(struct btrfs_fs_info *fs_info, |
72 | u64 rootid, u64 *root_ret, u64 *dir_ret) | |
73 | { | |
74 | struct btrfs_root *root = fs_info->tree_root; | |
75 | struct btrfs_root_ref *root_ref; | |
76 | struct btrfs_path path; | |
77 | struct btrfs_key key; | |
78 | int ret; | |
79 | ||
80 | btrfs_init_path(&path); | |
81 | key.objectid = rootid; | |
82 | key.type = BTRFS_ROOT_BACKREF_KEY; | |
83 | key.offset = (u64)-1; | |
84 | ||
85 | ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); | |
86 | if (ret < 0) | |
87 | return ret; | |
88 | /* Should not happen */ | |
89 | if (ret == 0) { | |
90 | ret = -EUCLEAN; | |
91 | goto out; | |
92 | } | |
93 | ret = btrfs_previous_item(root, &path, rootid, BTRFS_ROOT_BACKREF_KEY); | |
94 | if (ret < 0) | |
95 | goto out; | |
96 | if (ret > 0) { | |
97 | ret = -ENOENT; | |
98 | goto out; | |
99 | } | |
100 | btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); | |
101 | root_ref = btrfs_item_ptr(path.nodes[0], path.slots[0], | |
102 | struct btrfs_root_ref); | |
103 | *root_ret = key.offset; | |
104 | *dir_ret = btrfs_root_ref_dirid(path.nodes[0], root_ref); | |
105 | out: | |
106 | btrfs_release_path(&path); | |
107 | return ret; | |
108 | } | |
109 | ||
110 | /* | |
111 | * To get the parent inode of @ino of @root. | |
112 | * | |
113 | * @root_ret and @ino_ret will be filled. | |
114 | * | |
115 | * NOTE: This function is not reliable. It can only get one parent inode. | |
116 | * The get the proper parent inode, we need a full VFS inodes stack to | |
117 | * resolve properly. | |
118 | */ | |
119 | static int get_parent_inode(struct btrfs_root *root, u64 ino, | |
120 | struct btrfs_root **root_ret, u64 *ino_ret) | |
121 | { | |
122 | struct btrfs_fs_info *fs_info = root->fs_info; | |
123 | struct btrfs_path path; | |
124 | struct btrfs_key key; | |
125 | int ret; | |
126 | ||
127 | if (ino == BTRFS_FIRST_FREE_OBJECTID) { | |
128 | u64 parent_root = -1; | |
129 | ||
130 | /* It's top level already, no more parent */ | |
131 | if (root->root_key.objectid == BTRFS_FS_TREE_OBJECTID) { | |
132 | *root_ret = fs_info->fs_root; | |
133 | *ino_ret = BTRFS_FIRST_FREE_OBJECTID; | |
134 | return 0; | |
135 | } | |
136 | ||
137 | ret = lookup_root_ref(fs_info, root->root_key.objectid, | |
138 | &parent_root, ino_ret); | |
139 | if (ret < 0) | |
140 | return ret; | |
141 | ||
142 | key.objectid = parent_root; | |
143 | key.type = BTRFS_ROOT_ITEM_KEY; | |
144 | key.offset = (u64)-1; | |
145 | *root_ret = btrfs_read_fs_root(fs_info, &key); | |
146 | if (IS_ERR(*root_ret)) | |
147 | return PTR_ERR(*root_ret); | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | btrfs_init_path(&path); | |
153 | key.objectid = ino; | |
154 | key.type = BTRFS_INODE_REF_KEY; | |
155 | key.offset = (u64)-1; | |
156 | ||
157 | ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); | |
158 | if (ret < 0) | |
159 | return ret; | |
160 | /* Should not happen */ | |
161 | if (ret == 0) { | |
162 | ret = -EUCLEAN; | |
163 | goto out; | |
164 | } | |
165 | ret = btrfs_previous_item(root, &path, ino, BTRFS_INODE_REF_KEY); | |
166 | if (ret < 0) | |
167 | goto out; | |
168 | if (ret > 0) { | |
169 | ret = -ENOENT; | |
170 | goto out; | |
171 | } | |
172 | btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); | |
173 | *root_ret = root; | |
174 | *ino_ret = key.offset; | |
175 | out: | |
176 | btrfs_release_path(&path); | |
177 | return ret; | |
178 | } | |
179 | ||
21a14fac MB |
180 | static inline int next_length(const char *path) |
181 | { | |
182 | int res = 0; | |
5bdcb374 QW |
183 | while (*path != '\0' && *path != '/') { |
184 | ++res; | |
185 | ++path; | |
186 | if (res > BTRFS_NAME_LEN) | |
187 | break; | |
188 | } | |
21a14fac MB |
189 | return res; |
190 | } | |
191 | ||
192 | static inline const char *skip_current_directories(const char *cur) | |
193 | { | |
194 | while (1) { | |
195 | if (cur[0] == '/') | |
196 | ++cur; | |
197 | else if (cur[0] == '.' && cur[1] == '/') | |
198 | cur += 2; | |
199 | else | |
200 | break; | |
201 | } | |
202 | ||
203 | return cur; | |
204 | } | |
205 | ||
c921aa20 QW |
206 | /* |
207 | * Resolve one filename of @ino of @root. | |
208 | * | |
209 | * key_ret: The child key (either INODE_ITEM or ROOT_ITEM type) | |
210 | * type_ret: BTRFS_FT_* of the child inode. | |
211 | * | |
212 | * Return 0 with above members filled. | |
213 | * Return <0 for error. | |
214 | */ | |
215 | static int resolve_one_filename(struct btrfs_root *root, u64 ino, | |
216 | const char *name, int namelen, | |
217 | struct btrfs_key *key_ret, u8 *type_ret) | |
218 | { | |
219 | struct btrfs_dir_item *dir_item; | |
220 | struct btrfs_path path; | |
221 | int ret = 0; | |
222 | ||
223 | btrfs_init_path(&path); | |
224 | ||
225 | dir_item = btrfs_lookup_dir_item(NULL, root, &path, ino, name, | |
226 | namelen, 0); | |
227 | if (IS_ERR(dir_item)) { | |
228 | ret = PTR_ERR(dir_item); | |
229 | goto out; | |
230 | } | |
231 | ||
232 | btrfs_dir_item_key_to_cpu(path.nodes[0], dir_item, key_ret); | |
233 | *type_ret = btrfs_dir_type(path.nodes[0], dir_item); | |
234 | out: | |
235 | btrfs_release_path(&path); | |
236 | return ret; | |
237 | } | |
238 | ||
239 | /* | |
240 | * Resolve a full path @filename. The start point is @ino of @root. | |
241 | * | |
242 | * The result will be filled into @root_ret, @ino_ret and @type_ret. | |
243 | */ | |
244 | int btrfs_lookup_path(struct btrfs_root *root, u64 ino, const char *filename, | |
245 | struct btrfs_root **root_ret, u64 *ino_ret, | |
246 | u8 *type_ret, int symlink_limit) | |
247 | { | |
248 | struct btrfs_fs_info *fs_info = root->fs_info; | |
249 | struct btrfs_root *next_root; | |
250 | struct btrfs_key key; | |
251 | const char *cur = filename; | |
252 | u64 next_ino; | |
253 | u8 next_type; | |
9b5546c3 | 254 | u8 type = BTRFS_FT_UNKNOWN; |
c921aa20 QW |
255 | int len; |
256 | int ret = 0; | |
257 | ||
258 | /* If the path is absolute path, also search from fs root */ | |
259 | if (*cur == '/') { | |
260 | root = fs_info->fs_root; | |
261 | ino = btrfs_root_dirid(&root->root_item); | |
262 | type = BTRFS_FT_DIR; | |
263 | } | |
264 | ||
265 | while (*cur != '\0') { | |
266 | cur = skip_current_directories(cur); | |
267 | ||
268 | len = next_length(cur); | |
269 | if (len > BTRFS_NAME_LEN) { | |
270 | error("%s: Name too long at \"%.*s\"", __func__, | |
271 | BTRFS_NAME_LEN, cur); | |
272 | return -ENAMETOOLONG; | |
273 | } | |
274 | ||
275 | if (len == 1 && cur[0] == '.') | |
276 | break; | |
277 | ||
278 | if (len == 2 && cur[0] == '.' && cur[1] == '.') { | |
279 | /* Go one level up */ | |
280 | ret = get_parent_inode(root, ino, &next_root, &next_ino); | |
281 | if (ret < 0) | |
282 | return ret; | |
283 | root = next_root; | |
284 | ino = next_ino; | |
285 | goto next; | |
286 | } | |
287 | ||
288 | if (!*cur) | |
289 | break; | |
290 | ||
291 | ret = resolve_one_filename(root, ino, cur, len, &key, &type); | |
292 | if (ret < 0) | |
293 | return ret; | |
294 | ||
295 | if (key.type == BTRFS_ROOT_ITEM_KEY) { | |
296 | /* Child inode is a subvolume */ | |
297 | ||
298 | next_root = btrfs_read_fs_root(fs_info, &key); | |
299 | if (IS_ERR(next_root)) | |
300 | return PTR_ERR(next_root); | |
301 | root = next_root; | |
302 | ino = btrfs_root_dirid(&root->root_item); | |
303 | } else if (type == BTRFS_FT_SYMLINK && symlink_limit >= 0) { | |
304 | /* Child inode is a symlink */ | |
305 | ||
306 | char *target; | |
307 | ||
308 | if (symlink_limit == 0) { | |
309 | error("%s: Too much symlinks!", __func__); | |
310 | return -EMLINK; | |
311 | } | |
312 | target = malloc(fs_info->sectorsize); | |
313 | if (!target) | |
314 | return -ENOMEM; | |
315 | ret = btrfs_readlink(root, key.objectid, target); | |
316 | if (ret < 0) { | |
317 | free(target); | |
318 | return ret; | |
319 | } | |
320 | target[ret] = '\0'; | |
321 | ||
322 | ret = btrfs_lookup_path(root, ino, target, &next_root, | |
323 | &next_ino, &next_type, | |
324 | symlink_limit); | |
325 | if (ret < 0) | |
326 | return ret; | |
327 | root = next_root; | |
328 | ino = next_ino; | |
329 | type = next_type; | |
330 | } else { | |
331 | /* Child inode is an inode */ | |
332 | ino = key.objectid; | |
333 | } | |
334 | next: | |
335 | cur += len; | |
336 | } | |
337 | ||
9b5546c3 QW |
338 | /* We haven't found anything, but still get no error? */ |
339 | if (type == BTRFS_FT_UNKNOWN && !ret) | |
340 | ret = -EUCLEAN; | |
341 | ||
c921aa20 QW |
342 | if (!ret) { |
343 | *root_ret = root; | |
344 | *ino_ret = ino; | |
345 | *type_ret = type; | |
346 | } | |
347 | ||
348 | return ret; | |
349 | } | |
350 | ||
a26a6bed QW |
351 | /* |
352 | * Read out inline extent. | |
353 | * | |
354 | * Since inline extent should only exist for offset 0, no need for extra | |
355 | * parameters. | |
356 | * Truncating should be handled by the caller. | |
357 | * | |
358 | * Return the number of bytes read. | |
359 | * Return <0 for error. | |
360 | */ | |
361 | int btrfs_read_extent_inline(struct btrfs_path *path, | |
362 | struct btrfs_file_extent_item *fi, char *dest) | |
363 | { | |
364 | struct extent_buffer *leaf = path->nodes[0]; | |
365 | int slot = path->slots[0]; | |
366 | char *cbuf = NULL; | |
367 | char *dbuf = NULL; | |
368 | u32 csize; | |
369 | u32 dsize; | |
370 | int ret; | |
371 | ||
372 | csize = btrfs_file_extent_inline_item_len(leaf, btrfs_item_nr(slot)); | |
373 | if (btrfs_file_extent_compression(leaf, fi) == BTRFS_COMPRESS_NONE) { | |
374 | /* Uncompressed, just read it out */ | |
375 | read_extent_buffer(leaf, dest, | |
376 | btrfs_file_extent_inline_start(fi), | |
377 | csize); | |
378 | return csize; | |
379 | } | |
380 | ||
381 | /* Compressed extent, prepare the compressed and data buffer */ | |
382 | dsize = btrfs_file_extent_ram_bytes(leaf, fi); | |
383 | cbuf = malloc(csize); | |
384 | dbuf = malloc(dsize); | |
385 | if (!cbuf || !dbuf) { | |
386 | ret = -ENOMEM; | |
387 | goto out; | |
388 | } | |
389 | read_extent_buffer(leaf, cbuf, btrfs_file_extent_inline_start(fi), | |
390 | csize); | |
391 | ret = btrfs_decompress(btrfs_file_extent_compression(leaf, fi), | |
392 | cbuf, csize, dbuf, dsize); | |
dae9aeda | 393 | if (ret == (u32)-1) { |
a26a6bed QW |
394 | ret = -EIO; |
395 | goto out; | |
396 | } | |
dae9aeda QW |
397 | /* |
398 | * The compressed part ends before sector boundary, the remaining needs | |
399 | * to be zeroed out. | |
400 | */ | |
401 | if (ret < dsize) | |
402 | memset(dbuf + ret, 0, dsize - ret); | |
a26a6bed QW |
403 | memcpy(dest, dbuf, dsize); |
404 | ret = dsize; | |
405 | out: | |
406 | free(cbuf); | |
407 | free(dbuf); | |
408 | return ret; | |
409 | } | |
410 | ||
411 | /* | |
412 | * Read out regular extent. | |
413 | * | |
414 | * Truncating should be handled by the caller. | |
415 | * | |
416 | * @offset and @len should not cross the extent boundary. | |
417 | * Return the number of bytes read. | |
418 | * Return <0 for error. | |
419 | */ | |
420 | int btrfs_read_extent_reg(struct btrfs_path *path, | |
421 | struct btrfs_file_extent_item *fi, u64 offset, | |
422 | int len, char *dest) | |
423 | { | |
424 | struct extent_buffer *leaf = path->nodes[0]; | |
425 | struct btrfs_fs_info *fs_info = leaf->fs_info; | |
426 | struct btrfs_key key; | |
427 | u64 extent_num_bytes; | |
428 | u64 disk_bytenr; | |
429 | u64 read; | |
430 | char *cbuf = NULL; | |
431 | char *dbuf = NULL; | |
432 | u32 csize; | |
433 | u32 dsize; | |
434 | bool finished = false; | |
435 | int num_copies; | |
436 | int i; | |
437 | int slot = path->slots[0]; | |
438 | int ret; | |
439 | ||
440 | btrfs_item_key_to_cpu(leaf, &key, slot); | |
441 | extent_num_bytes = btrfs_file_extent_num_bytes(leaf, fi); | |
442 | ASSERT(IS_ALIGNED(offset, fs_info->sectorsize) && | |
443 | IS_ALIGNED(len, fs_info->sectorsize)); | |
444 | ASSERT(offset >= key.offset && | |
445 | offset + len <= key.offset + extent_num_bytes); | |
446 | ||
447 | /* Preallocated or hole , fill @dest with zero */ | |
448 | if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_PREALLOC || | |
449 | btrfs_file_extent_disk_bytenr(leaf, fi) == 0) { | |
450 | memset(dest, 0, len); | |
451 | return len; | |
452 | } | |
453 | ||
454 | if (btrfs_file_extent_compression(leaf, fi) == BTRFS_COMPRESS_NONE) { | |
455 | u64 logical; | |
456 | ||
457 | logical = btrfs_file_extent_disk_bytenr(leaf, fi) + | |
458 | btrfs_file_extent_offset(leaf, fi) + | |
459 | offset - key.offset; | |
460 | read = len; | |
461 | ||
462 | num_copies = btrfs_num_copies(fs_info, logical, len); | |
463 | for (i = 1; i <= num_copies; i++) { | |
464 | ret = read_extent_data(fs_info, dest, logical, &read, i); | |
465 | if (ret < 0 || read != len) | |
466 | continue; | |
467 | finished = true; | |
468 | break; | |
469 | } | |
470 | if (!finished) | |
471 | return -EIO; | |
472 | return len; | |
473 | } | |
474 | ||
475 | csize = btrfs_file_extent_disk_num_bytes(leaf, fi); | |
476 | dsize = btrfs_file_extent_ram_bytes(leaf, fi); | |
477 | disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); | |
478 | num_copies = btrfs_num_copies(fs_info, disk_bytenr, csize); | |
479 | ||
480 | cbuf = malloc_cache_aligned(csize); | |
481 | dbuf = malloc_cache_aligned(dsize); | |
482 | if (!cbuf || !dbuf) { | |
483 | ret = -ENOMEM; | |
484 | goto out; | |
485 | } | |
486 | /* For compressed extent, we must read the whole on-disk extent */ | |
487 | for (i = 1; i <= num_copies; i++) { | |
488 | read = csize; | |
489 | ret = read_extent_data(fs_info, cbuf, disk_bytenr, | |
490 | &read, i); | |
491 | if (ret < 0 || read != csize) | |
492 | continue; | |
493 | finished = true; | |
494 | break; | |
495 | } | |
496 | if (!finished) { | |
497 | ret = -EIO; | |
498 | goto out; | |
499 | } | |
500 | ||
501 | ret = btrfs_decompress(btrfs_file_extent_compression(leaf, fi), cbuf, | |
502 | csize, dbuf, dsize); | |
dae9aeda | 503 | if (ret == (u32)-1) { |
a26a6bed QW |
504 | ret = -EIO; |
505 | goto out; | |
506 | } | |
dae9aeda QW |
507 | /* |
508 | * The compressed part ends before sector boundary, the remaining needs | |
509 | * to be zeroed out. | |
510 | */ | |
511 | if (ret < dsize) | |
512 | memset(dbuf + ret, 0, dsize - ret); | |
a26a6bed QW |
513 | /* Then copy the needed part */ |
514 | memcpy(dest, dbuf + btrfs_file_extent_offset(leaf, fi), len); | |
515 | ret = len; | |
516 | out: | |
517 | free(cbuf); | |
518 | free(dbuf); | |
519 | return ret; | |
520 | } | |
01347f64 QW |
521 | |
522 | /* | |
523 | * Get the first file extent that covers bytenr @file_offset. | |
524 | * | |
525 | * @file_offset must be aligned to sectorsize. | |
526 | * | |
527 | * return 0 for found, and path points to the file extent. | |
528 | * return >0 for not found, and fill @next_offset. | |
529 | * @next_offset can be 0 if there is no next file extent. | |
530 | * return <0 for error. | |
531 | */ | |
532 | static int lookup_data_extent(struct btrfs_root *root, struct btrfs_path *path, | |
533 | u64 ino, u64 file_offset, u64 *next_offset) | |
534 | { | |
535 | struct btrfs_key key; | |
536 | struct btrfs_file_extent_item *fi; | |
537 | u8 extent_type; | |
538 | int ret = 0; | |
539 | ||
540 | ASSERT(IS_ALIGNED(file_offset, root->fs_info->sectorsize)); | |
541 | key.objectid = ino; | |
542 | key.type = BTRFS_EXTENT_DATA_KEY; | |
543 | key.offset = file_offset; | |
544 | ||
545 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); | |
546 | /* Error or we're already at the file extent */ | |
547 | if (ret <= 0) | |
548 | return ret; | |
89ab1e28 HS |
549 | /* Check previous file extent */ |
550 | ret = btrfs_previous_item(root, path, ino, BTRFS_EXTENT_DATA_KEY); | |
551 | if (ret < 0) | |
552 | return ret; | |
553 | if (ret > 0) | |
554 | goto check_next; | |
01347f64 QW |
555 | /* Now the key.offset must be smaller than @file_offset */ |
556 | btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); | |
557 | if (key.objectid != ino || | |
558 | key.type != BTRFS_EXTENT_DATA_KEY) | |
559 | goto check_next; | |
560 | ||
561 | fi = btrfs_item_ptr(path->nodes[0], path->slots[0], | |
562 | struct btrfs_file_extent_item); | |
563 | extent_type = btrfs_file_extent_type(path->nodes[0], fi); | |
564 | if (extent_type == BTRFS_FILE_EXTENT_INLINE) { | |
565 | if (file_offset == 0) | |
566 | return 0; | |
567 | /* Inline extent should be the only extent, no next extent. */ | |
568 | *next_offset = 0; | |
569 | return 1; | |
570 | } | |
571 | ||
572 | /* This file extent covers @file_offset */ | |
573 | if (key.offset <= file_offset && key.offset + | |
574 | btrfs_file_extent_num_bytes(path->nodes[0], fi) > file_offset) | |
575 | return 0; | |
576 | check_next: | |
577 | ret = btrfs_next_item(root, path); | |
578 | if (ret < 0) | |
579 | return ret; | |
580 | if (ret > 0) { | |
581 | *next_offset = 0; | |
582 | return 1; | |
583 | } | |
584 | ||
585 | btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); | |
586 | fi = btrfs_item_ptr(path->nodes[0], path->slots[0], | |
587 | struct btrfs_file_extent_item); | |
588 | /* Next next data extent */ | |
589 | if (key.objectid != ino || | |
590 | key.type != BTRFS_EXTENT_DATA_KEY) { | |
591 | *next_offset = 0; | |
592 | return 1; | |
593 | } | |
594 | /* Current file extent already beyond @file_offset */ | |
595 | if (key.offset > file_offset) { | |
596 | *next_offset = key.offset; | |
597 | return 1; | |
598 | } | |
599 | /* This file extent covers @file_offset */ | |
600 | if (key.offset <= file_offset && key.offset + | |
601 | btrfs_file_extent_num_bytes(path->nodes[0], fi) > file_offset) | |
602 | return 0; | |
603 | /* This file extent ends before @file_offset, check next */ | |
604 | ret = btrfs_next_item(root, path); | |
605 | if (ret < 0) | |
606 | return ret; | |
607 | if (ret > 0) { | |
608 | *next_offset = 0; | |
609 | return 1; | |
610 | } | |
611 | btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); | |
612 | if (key.type != BTRFS_EXTENT_DATA_KEY || key.objectid != ino) { | |
613 | *next_offset = 0; | |
614 | return 1; | |
615 | } | |
616 | *next_offset = key.offset; | |
617 | return 1; | |
618 | } | |
e3427184 QW |
619 | |
620 | static int read_and_truncate_page(struct btrfs_path *path, | |
621 | struct btrfs_file_extent_item *fi, | |
622 | int start, int len, char *dest) | |
623 | { | |
624 | struct extent_buffer *leaf = path->nodes[0]; | |
625 | struct btrfs_fs_info *fs_info = leaf->fs_info; | |
626 | u64 aligned_start = round_down(start, fs_info->sectorsize); | |
627 | u8 extent_type; | |
628 | char *buf; | |
629 | int page_off = start - aligned_start; | |
630 | int page_len = fs_info->sectorsize - page_off; | |
631 | int ret; | |
632 | ||
633 | ASSERT(start + len <= aligned_start + fs_info->sectorsize); | |
634 | buf = malloc_cache_aligned(fs_info->sectorsize); | |
635 | if (!buf) | |
636 | return -ENOMEM; | |
637 | ||
638 | extent_type = btrfs_file_extent_type(leaf, fi); | |
639 | if (extent_type == BTRFS_FILE_EXTENT_INLINE) { | |
640 | ret = btrfs_read_extent_inline(path, fi, buf); | |
641 | memcpy(dest, buf + page_off, min(page_len, ret)); | |
642 | free(buf); | |
643 | return len; | |
644 | } | |
645 | ||
646 | ret = btrfs_read_extent_reg(path, fi, | |
647 | round_down(start, fs_info->sectorsize), | |
648 | fs_info->sectorsize, buf); | |
649 | if (ret < 0) { | |
650 | free(buf); | |
651 | return ret; | |
652 | } | |
653 | memcpy(dest, buf + page_off, page_len); | |
654 | free(buf); | |
655 | return len; | |
656 | } | |
657 | ||
658 | int btrfs_file_read(struct btrfs_root *root, u64 ino, u64 file_offset, u64 len, | |
659 | char *dest) | |
660 | { | |
661 | struct btrfs_fs_info *fs_info = root->fs_info; | |
662 | struct btrfs_file_extent_item *fi; | |
663 | struct btrfs_path path; | |
664 | struct btrfs_key key; | |
665 | u64 aligned_start = round_down(file_offset, fs_info->sectorsize); | |
666 | u64 aligned_end = round_down(file_offset + len, fs_info->sectorsize); | |
667 | u64 next_offset; | |
668 | u64 cur = aligned_start; | |
669 | int ret = 0; | |
670 | ||
671 | btrfs_init_path(&path); | |
672 | ||
673 | /* Set the whole dest all zero, so we won't need to bother holes */ | |
674 | memset(dest, 0, len); | |
675 | ||
676 | /* Read out the leading unaligned part */ | |
677 | if (aligned_start != file_offset) { | |
678 | ret = lookup_data_extent(root, &path, ino, aligned_start, | |
679 | &next_offset); | |
680 | if (ret < 0) | |
681 | goto out; | |
682 | if (ret == 0) { | |
683 | /* Read the unaligned part out*/ | |
684 | fi = btrfs_item_ptr(path.nodes[0], path.slots[0], | |
685 | struct btrfs_file_extent_item); | |
686 | ret = read_and_truncate_page(&path, fi, file_offset, | |
687 | round_up(file_offset, fs_info->sectorsize) - | |
688 | file_offset, dest); | |
689 | if (ret < 0) | |
690 | goto out; | |
691 | cur += fs_info->sectorsize; | |
692 | } else { | |
693 | /* The whole file is a hole */ | |
694 | if (!next_offset) { | |
695 | memset(dest, 0, len); | |
696 | return len; | |
697 | } | |
698 | cur = next_offset; | |
699 | } | |
700 | } | |
701 | ||
702 | /* Read the aligned part */ | |
703 | while (cur < aligned_end) { | |
704 | u64 extent_num_bytes; | |
705 | u8 type; | |
706 | ||
707 | btrfs_release_path(&path); | |
708 | ret = lookup_data_extent(root, &path, ino, cur, &next_offset); | |
709 | if (ret < 0) | |
710 | goto out; | |
711 | if (ret > 0) { | |
712 | /* No next, direct exit */ | |
713 | if (!next_offset) { | |
714 | ret = 0; | |
715 | goto out; | |
716 | } | |
7c075433 QW |
717 | /* |
718 | * Find a extent gap, mostly caused by NO_HOLE feature. | |
719 | * Just to next offset directly. | |
720 | */ | |
721 | if (next_offset > cur) { | |
722 | cur = next_offset; | |
723 | continue; | |
724 | } | |
e3427184 QW |
725 | } |
726 | fi = btrfs_item_ptr(path.nodes[0], path.slots[0], | |
727 | struct btrfs_file_extent_item); | |
728 | btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); | |
729 | type = btrfs_file_extent_type(path.nodes[0], fi); | |
730 | if (type == BTRFS_FILE_EXTENT_INLINE) { | |
731 | ret = btrfs_read_extent_inline(&path, fi, dest); | |
732 | goto out; | |
733 | } | |
734 | /* Skip holes, as we have zeroed the dest */ | |
735 | if (type == BTRFS_FILE_EXTENT_PREALLOC || | |
736 | btrfs_file_extent_disk_bytenr(path.nodes[0], fi) == 0) { | |
737 | cur = key.offset + btrfs_file_extent_num_bytes( | |
738 | path.nodes[0], fi); | |
739 | continue; | |
740 | } | |
741 | ||
742 | /* Read the remaining part of the extent */ | |
743 | extent_num_bytes = btrfs_file_extent_num_bytes(path.nodes[0], | |
744 | fi); | |
745 | ret = btrfs_read_extent_reg(&path, fi, cur, | |
746 | min(extent_num_bytes, aligned_end - cur), | |
747 | dest + cur - file_offset); | |
748 | if (ret < 0) | |
749 | goto out; | |
750 | cur += min(extent_num_bytes, aligned_end - cur); | |
751 | } | |
752 | ||
753 | /* Read the tailing unaligned part*/ | |
754 | if (file_offset + len != aligned_end) { | |
755 | btrfs_release_path(&path); | |
756 | ret = lookup_data_extent(root, &path, ino, aligned_end, | |
757 | &next_offset); | |
758 | /* <0 is error, >0 means no extent */ | |
759 | if (ret) | |
760 | goto out; | |
761 | fi = btrfs_item_ptr(path.nodes[0], path.slots[0], | |
762 | struct btrfs_file_extent_item); | |
763 | ret = read_and_truncate_page(&path, fi, aligned_end, | |
764 | file_offset + len - aligned_end, | |
765 | dest + aligned_end - file_offset); | |
766 | } | |
767 | out: | |
768 | btrfs_release_path(&path); | |
769 | if (ret < 0) | |
770 | return ret; | |
771 | return len; | |
772 | } |