MAINTAINERS: POWERPC MPC8XX: Update email address
[oweals/u-boot.git] / fs / btrfs / compression.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 <linux/lzo.h>
11 #include <linux/zstd.h>
12 #include <linux/compat.h>
13 #include <u-boot/zlib.h>
14 #include <asm/unaligned.h>
15
16 /* Header for each segment, LE32, recording the compressed size */
17 #define LZO_LEN         4
18 static u32 decompress_lzo(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
19 {
20         u32 tot_len, tot_in, in_len, res;
21         size_t out_len;
22         int ret;
23
24         if (clen < LZO_LEN)
25                 return -1;
26
27         tot_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
28         tot_in = 0;
29         cbuf += LZO_LEN;
30         clen -= LZO_LEN;
31         tot_len -= LZO_LEN;
32         tot_in += LZO_LEN;
33
34         if (tot_len == 0 && dlen)
35                 return -1;
36         if (tot_len < LZO_LEN)
37                 return -1;
38
39         res = 0;
40
41         while (tot_len > LZO_LEN) {
42                 u32 rem_page;
43
44                 in_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
45                 cbuf += LZO_LEN;
46                 clen -= LZO_LEN;
47
48                 if (in_len > clen || tot_len < LZO_LEN + in_len)
49                         return -1;
50
51                 tot_len -= (LZO_LEN + in_len);
52                 tot_in += (LZO_LEN + in_len);
53
54                 out_len = dlen;
55                 ret = lzo1x_decompress_safe(cbuf, in_len, dbuf, &out_len);
56                 if (ret != LZO_E_OK)
57                         return -1;
58
59                 cbuf += in_len;
60                 clen -= in_len;
61                 dbuf += out_len;
62                 dlen -= out_len;
63
64                 res += out_len;
65
66                 /*
67                  * If the 4 bytes header does not fit to the rest of the page we
68                  * have to move to next one, or we read some garbage.
69                  */
70                 rem_page = PAGE_SIZE - (tot_in % PAGE_SIZE);
71                 if (rem_page < LZO_LEN) {
72                         cbuf += rem_page;
73                         tot_in += rem_page;
74                         clen -= rem_page;
75                         tot_len -= rem_page;
76                 }
77         }
78
79         return res;
80 }
81
82 /* from zutil.h */
83 #define PRESET_DICT 0x20
84
85 static u32 decompress_zlib(const u8 *_cbuf, u32 clen, u8 *dbuf, u32 dlen)
86 {
87         int wbits = MAX_WBITS, ret = -1;
88         z_stream stream;
89         u8 *cbuf;
90         u32 res;
91
92         memset(&stream, 0, sizeof(stream));
93
94         cbuf = (u8 *) _cbuf;
95
96         stream.total_in = 0;
97
98         stream.next_out = dbuf;
99         stream.avail_out = dlen;
100         stream.total_out = 0;
101
102         /* skip adler32 check if deflate and no dictionary */
103         if (clen > 2 && !(cbuf[1] & PRESET_DICT) &&
104             ((cbuf[0] & 0x0f) == Z_DEFLATED) &&
105             !(((cbuf[0] << 8) + cbuf[1]) % 31)) {
106                 wbits = -((cbuf[0] >> 4) + 8);
107                 cbuf += 2;
108                 clen -= 2;
109         }
110
111         if (Z_OK != inflateInit2(&stream, wbits))
112                 return -1;
113
114         while (stream.total_in < clen) {
115                 stream.next_in = cbuf + stream.total_in;
116                 stream.avail_in = min((u32) (clen - stream.total_in),
117                                       (u32) btrfs_info.sb.sectorsize);
118
119                 ret = inflate(&stream, Z_NO_FLUSH);
120                 if (ret != Z_OK)
121                         break;
122         }
123
124         res = stream.total_out;
125         inflateEnd(&stream);
126
127         if (ret != Z_STREAM_END)
128                 return -1;
129
130         return res;
131 }
132
133 #define ZSTD_BTRFS_MAX_WINDOWLOG 17
134 #define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
135
136 static u32 decompress_zstd(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
137 {
138         ZSTD_DStream *dstream;
139         ZSTD_inBuffer in_buf;
140         ZSTD_outBuffer out_buf;
141         void *workspace;
142         size_t wsize;
143         u32 res = -1;
144
145         wsize = ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT);
146         workspace = malloc(wsize);
147         if (!workspace) {
148                 debug("%s: cannot allocate workspace of size %zu\n", __func__,
149                       wsize);
150                 return -1;
151         }
152
153         dstream = ZSTD_initDStream(ZSTD_BTRFS_MAX_INPUT, workspace, wsize);
154         if (!dstream) {
155                 printf("%s: ZSTD_initDStream failed\n", __func__);
156                 goto err_free;
157         }
158
159         in_buf.src = cbuf;
160         in_buf.pos = 0;
161         in_buf.size = clen;
162
163         out_buf.dst = dbuf;
164         out_buf.pos = 0;
165         out_buf.size = dlen;
166
167         while (1) {
168                 size_t ret;
169
170                 ret = ZSTD_decompressStream(dstream, &out_buf, &in_buf);
171                 if (ZSTD_isError(ret)) {
172                         printf("%s: ZSTD_decompressStream error %d\n", __func__,
173                                ZSTD_getErrorCode(ret));
174                         goto err_free;
175                 }
176
177                 if (in_buf.pos >= clen || !ret)
178                         break;
179         }
180
181         res = out_buf.pos;
182
183 err_free:
184         free(workspace);
185         return res;
186 }
187
188 u32 btrfs_decompress(u8 type, const char *c, u32 clen, char *d, u32 dlen)
189 {
190         u32 res;
191         const u8 *cbuf;
192         u8 *dbuf;
193
194         cbuf = (const u8 *) c;
195         dbuf = (u8 *) d;
196
197         switch (type) {
198         case BTRFS_COMPRESS_NONE:
199                 res = dlen < clen ? dlen : clen;
200                 memcpy(dbuf, cbuf, res);
201                 return res;
202         case BTRFS_COMPRESS_ZLIB:
203                 return decompress_zlib(cbuf, clen, dbuf, dlen);
204         case BTRFS_COMPRESS_LZO:
205                 return decompress_lzo(cbuf, clen, dbuf, dlen);
206         case BTRFS_COMPRESS_ZSTD:
207                 return decompress_zstd(cbuf, clen, dbuf, dlen);
208         default:
209                 printf("%s: Unsupported compression in extent: %i\n", __func__,
210                        type);
211                 return -1;
212         }
213 }