fs: btrfs: support sparse extents
[oweals/u-boot.git] / fs / btrfs / extent-io.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 "btrfs.h"
9 #include <malloc.h>
10 #include <memalign.h>
11
12 u64 btrfs_read_extent_inline(struct btrfs_path *path,
13                              struct btrfs_file_extent_item *extent, u64 offset,
14                              u64 size, char *out)
15 {
16         u32 clen, dlen, orig_size = size, res;
17         const char *cbuf;
18         char *dbuf;
19         const int data_off = offsetof(struct btrfs_file_extent_item,
20                                       disk_bytenr);
21
22         clen = btrfs_path_item_size(path) - data_off;
23         cbuf = (const char *) extent + data_off;
24         dlen = extent->ram_bytes;
25
26         if (offset > dlen)
27                 return -1ULL;
28
29         if (size > dlen - offset)
30                 size = dlen - offset;
31
32         if (extent->compression == BTRFS_COMPRESS_NONE) {
33                 memcpy(out, cbuf + offset, size);
34                 return size;
35         }
36
37         if (dlen > orig_size) {
38                 dbuf = malloc(dlen);
39                 if (!dbuf)
40                         return -1ULL;
41         } else {
42                 dbuf = out;
43         }
44
45         res = btrfs_decompress(extent->compression, cbuf, clen, dbuf, dlen);
46         if (res == -1 || res != dlen)
47                 goto err;
48
49         if (dlen > orig_size) {
50                 memcpy(out, dbuf + offset, size);
51                 free(dbuf);
52         } else if (offset) {
53                 memmove(out, dbuf + offset, size);
54         }
55
56         return size;
57
58 err:
59         if (dlen > orig_size)
60                 free(dbuf);
61         return -1ULL;
62 }
63
64 u64 btrfs_read_extent_reg(struct btrfs_path *path,
65                           struct btrfs_file_extent_item *extent, u64 offset,
66                           u64 size, char *out)
67 {
68         u64 physical, clen, dlen, orig_size = size;
69         u32 res;
70         char *cbuf, *dbuf;
71
72         clen = extent->disk_num_bytes;
73         dlen = extent->num_bytes;
74
75         if (offset > dlen)
76                 return -1ULL;
77
78         if (size > dlen - offset)
79                 size = dlen - offset;
80
81         /* sparse extent */
82         if (extent->disk_bytenr == 0) {
83                 memset(out, 0, size);
84                 return size;
85         }
86
87         physical = btrfs_map_logical_to_physical(extent->disk_bytenr);
88         if (physical == -1ULL)
89                 return -1ULL;
90
91         if (extent->compression == BTRFS_COMPRESS_NONE) {
92                 physical += extent->offset + offset;
93                 if (!btrfs_devread(physical, size, out))
94                         return -1ULL;
95
96                 return size;
97         }
98
99         cbuf = malloc_cache_aligned(dlen > size ? clen + dlen : clen);
100         if (!cbuf)
101                 return -1ULL;
102
103         if (dlen > orig_size)
104                 dbuf = cbuf + clen;
105         else
106                 dbuf = out;
107
108         if (!btrfs_devread(physical, clen, cbuf))
109                 goto err;
110
111         res = btrfs_decompress(extent->compression, cbuf, clen, dbuf, dlen);
112         if (res == -1)
113                 goto err;
114
115         if (dlen > orig_size)
116                 memcpy(out, dbuf + offset, size);
117         else
118                 memmove(out, dbuf + offset, size);
119
120         free(cbuf);
121         return res;
122
123 err:
124         free(cbuf);
125         return -1ULL;
126 }