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