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