blob: 23efefa199796562cc4f2b484abb99aacc6d905c [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Marek Behún29387542017-09-03 17:00:28 +02002/*
3 * BTRFS filesystem implementation for U-Boot
4 *
5 * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
Marek Behún29387542017-09-03 17:00:28 +02006 */
7
8#include "btrfs.h"
Simon Glass0f2af882020-05-10 11:40:05 -06009#include <log.h>
Marek Behún0611ae52019-04-29 22:40:45 +020010#include <malloc.h>
Marek Behún29387542017-09-03 17:00:28 +020011#include <linux/lzo.h>
Marek Behún0611ae52019-04-29 22:40:45 +020012#include <linux/zstd.h>
Qu Wenruo2aebf5b2020-03-26 13:35:56 +080013#include <linux/compat.h>
Marek Behún29387542017-09-03 17:00:28 +020014#include <u-boot/zlib.h>
Alberto Sánchez Molerod1f43332018-01-20 09:17:57 +020015#include <asm/unaligned.h>
Marek Behún29387542017-09-03 17:00:28 +020016
Qu Wenruoe7691f42020-03-26 13:35:54 +080017/* Header for each segment, LE32, recording the compressed size */
18#define LZO_LEN 4
Marek Behún29387542017-09-03 17:00:28 +020019static u32 decompress_lzo(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
20{
Qu Wenruo2aebf5b2020-03-26 13:35:56 +080021 u32 tot_len, tot_in, in_len, res;
Marek Behún29387542017-09-03 17:00:28 +020022 size_t out_len;
23 int ret;
24
Qu Wenruoe7691f42020-03-26 13:35:54 +080025 if (clen < LZO_LEN)
Marek Behún29387542017-09-03 17:00:28 +020026 return -1;
27
Alberto Sánchez Molerod1f43332018-01-20 09:17:57 +020028 tot_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
Qu Wenruo2aebf5b2020-03-26 13:35:56 +080029 tot_in = 0;
Qu Wenruoe7691f42020-03-26 13:35:54 +080030 cbuf += LZO_LEN;
31 clen -= LZO_LEN;
32 tot_len -= LZO_LEN;
Qu Wenruo2aebf5b2020-03-26 13:35:56 +080033 tot_in += LZO_LEN;
Marek Behún29387542017-09-03 17:00:28 +020034
35 if (tot_len == 0 && dlen)
36 return -1;
Qu Wenruoe7691f42020-03-26 13:35:54 +080037 if (tot_len < LZO_LEN)
Marek Behún29387542017-09-03 17:00:28 +020038 return -1;
39
40 res = 0;
41
Qu Wenruoe7691f42020-03-26 13:35:54 +080042 while (tot_len > LZO_LEN) {
Qu Wenruo2aebf5b2020-03-26 13:35:56 +080043 u32 rem_page;
44
Alberto Sánchez Molerod1f43332018-01-20 09:17:57 +020045 in_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
Qu Wenruoe7691f42020-03-26 13:35:54 +080046 cbuf += LZO_LEN;
47 clen -= LZO_LEN;
Marek Behún29387542017-09-03 17:00:28 +020048
Qu Wenruoe7691f42020-03-26 13:35:54 +080049 if (in_len > clen || tot_len < LZO_LEN + in_len)
Marek Behún29387542017-09-03 17:00:28 +020050 return -1;
51
Qu Wenruoe7691f42020-03-26 13:35:54 +080052 tot_len -= (LZO_LEN + in_len);
Qu Wenruo2aebf5b2020-03-26 13:35:56 +080053 tot_in += (LZO_LEN + in_len);
Marek Behún29387542017-09-03 17:00:28 +020054
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;
Qu Wenruo2aebf5b2020-03-26 13:35:56 +080066
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 }
Marek Behún29387542017-09-03 17:00:28 +020078 }
79
80 return res;
81}
82
83/* from zutil.h */
84#define PRESET_DICT 0x20
85
86static 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),
Qu Wenruod90164e2020-06-24 18:03:15 +0200118 current_fs_info->sectorsize);
Marek Behún29387542017-09-03 17:00:28 +0200119
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
Marek Behún0611ae52019-04-29 22:40:45 +0200134#define ZSTD_BTRFS_MAX_WINDOWLOG 17
135#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
136
137static 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
184err_free:
185 free(workspace);
186 return res;
187}
188
Marek Behún29387542017-09-03 17:00:28 +0200189u32 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);
Marek Behún0611ae52019-04-29 22:40:45 +0200207 case BTRFS_COMPRESS_ZSTD:
208 return decompress_zstd(cbuf, clen, dbuf, dlen);
Marek Behún29387542017-09-03 17:00:28 +0200209 default:
210 printf("%s: Unsupported compression in extent: %i\n", __func__,
211 type);
212 return -1;
213 }
214}