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