1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 /*
3 * Copyright (C), 2008-2020, OPPO Mobile Comm Corp., Ltd.
4 * Created by Huang Jianan <huangjianan@oppo.com>
5 */
6 #include <stdlib.h>
7
8 #include "erofs/decompress.h"
9 #include "erofs/err.h"
10 #include "erofs/print.h"
11
12 #ifdef HAVE_LIBDEFLATE
13 /* if libdeflate is available, use libdeflate instead. */
14 #include <libdeflate.h>
15
z_erofs_decompress_deflate(struct z_erofs_decompress_req * rq)16 static int z_erofs_decompress_deflate(struct z_erofs_decompress_req *rq)
17 {
18 struct erofs_sb_info *sbi = rq->sbi;
19 u8 *dest = (u8 *)rq->out;
20 u8 *src = (u8 *)rq->in;
21 u8 *buff = NULL;
22 size_t actual_out;
23 unsigned int inputmargin = 0;
24 struct libdeflate_decompressor *inf;
25 enum libdeflate_result ret;
26
27 while (!src[inputmargin & (erofs_blksiz(sbi) - 1)])
28 if (!(++inputmargin & (erofs_blksiz(sbi) - 1)))
29 break;
30
31 if (inputmargin >= rq->inputsize)
32 return -EFSCORRUPTED;
33
34 if (rq->decodedskip) {
35 buff = malloc(rq->decodedlength);
36 if (!buff)
37 return -ENOMEM;
38 dest = buff;
39 }
40
41 inf = libdeflate_alloc_decompressor();
42 if (!inf)
43 return -ENOMEM;
44
45 if (rq->partial_decoding) {
46 ret = libdeflate_deflate_decompress(inf, src + inputmargin,
47 rq->inputsize - inputmargin, dest,
48 rq->decodedlength, &actual_out);
49 if (ret && ret != LIBDEFLATE_INSUFFICIENT_SPACE) {
50 ret = -EIO;
51 goto out_inflate_end;
52 }
53
54 if (actual_out != rq->decodedlength) {
55 ret = -EIO;
56 goto out_inflate_end;
57 }
58 } else {
59 ret = libdeflate_deflate_decompress(inf, src + inputmargin,
60 rq->inputsize - inputmargin, dest,
61 rq->decodedlength, NULL);
62 if (ret) {
63 ret = -EIO;
64 goto out_inflate_end;
65 }
66 }
67
68 if (rq->decodedskip)
69 memcpy(rq->out, dest + rq->decodedskip,
70 rq->decodedlength - rq->decodedskip);
71
72 out_inflate_end:
73 libdeflate_free_decompressor(inf);
74 if (buff)
75 free(buff);
76 return ret;
77 }
78 #elif defined(HAVE_ZLIB)
79 #include <zlib.h>
80
81 /* report a zlib or i/o error */
zerr(int ret)82 static int zerr(int ret)
83 {
84 switch (ret) {
85 case Z_STREAM_ERROR:
86 return -EINVAL;
87 case Z_DATA_ERROR:
88 return -EIO;
89 case Z_MEM_ERROR:
90 return -ENOMEM;
91 case Z_ERRNO:
92 case Z_VERSION_ERROR:
93 default:
94 return -EFAULT;
95 }
96 }
97
z_erofs_decompress_deflate(struct z_erofs_decompress_req * rq)98 static int z_erofs_decompress_deflate(struct z_erofs_decompress_req *rq)
99 {
100 struct erofs_sb_info *sbi = rq->sbi;
101 u8 *dest = (u8 *)rq->out;
102 u8 *src = (u8 *)rq->in;
103 u8 *buff = NULL;
104 unsigned int inputmargin = 0;
105 z_stream strm;
106 int ret;
107
108 while (!src[inputmargin & (erofs_blksiz(sbi) - 1)])
109 if (!(++inputmargin & (erofs_blksiz(sbi) - 1)))
110 break;
111
112 if (inputmargin >= rq->inputsize)
113 return -EFSCORRUPTED;
114
115 if (rq->decodedskip) {
116 buff = malloc(rq->decodedlength);
117 if (!buff)
118 return -ENOMEM;
119 dest = buff;
120 }
121
122 /* allocate inflate state */
123 strm.zalloc = Z_NULL;
124 strm.zfree = Z_NULL;
125 strm.opaque = Z_NULL;
126 strm.avail_in = 0;
127 strm.next_in = Z_NULL;
128 ret = inflateInit2(&strm, -15);
129 if (ret != Z_OK) {
130 free(buff);
131 return zerr(ret);
132 }
133
134 strm.next_in = src + inputmargin;
135 strm.avail_in = rq->inputsize - inputmargin;
136 strm.next_out = dest;
137 strm.avail_out = rq->decodedlength;
138
139 ret = inflate(&strm, rq->partial_decoding ? Z_SYNC_FLUSH : Z_FINISH);
140 if (ret != Z_STREAM_END || strm.total_out != rq->decodedlength) {
141 if (ret != Z_OK || !rq->partial_decoding) {
142 ret = zerr(ret);
143 goto out_inflate_end;
144 }
145 }
146
147 if (rq->decodedskip)
148 memcpy(rq->out, dest + rq->decodedskip,
149 rq->decodedlength - rq->decodedskip);
150
151 out_inflate_end:
152 inflateEnd(&strm);
153 if (buff)
154 free(buff);
155 return ret;
156 }
157 #endif
158
159 #ifdef HAVE_LIBLZMA
160 #include <lzma.h>
161
z_erofs_decompress_lzma(struct z_erofs_decompress_req * rq)162 static int z_erofs_decompress_lzma(struct z_erofs_decompress_req *rq)
163 {
164 int ret = 0;
165 struct erofs_sb_info *sbi = rq->sbi;
166 u8 *dest = (u8 *)rq->out;
167 u8 *src = (u8 *)rq->in;
168 u8 *buff = NULL;
169 unsigned int inputmargin = 0;
170 lzma_stream strm;
171 lzma_ret ret2;
172
173 while (!src[inputmargin & (erofs_blksiz(sbi) - 1)])
174 if (!(++inputmargin & (erofs_blksiz(sbi) - 1)))
175 break;
176
177 if (inputmargin >= rq->inputsize)
178 return -EFSCORRUPTED;
179
180 if (rq->decodedskip) {
181 buff = malloc(rq->decodedlength);
182 if (!buff)
183 return -ENOMEM;
184 dest = buff;
185 }
186
187 strm = (lzma_stream)LZMA_STREAM_INIT;
188 strm.next_in = src + inputmargin;
189 strm.avail_in = rq->inputsize - inputmargin;
190 strm.next_out = dest;
191 strm.avail_out = rq->decodedlength;
192
193 ret2 = lzma_microlzma_decoder(&strm, strm.avail_in, rq->decodedlength,
194 !rq->partial_decoding,
195 Z_EROFS_LZMA_MAX_DICT_SIZE);
196 if (ret2 != LZMA_OK) {
197 erofs_err("fail to initialize lzma decoder %u", ret2 | 0U);
198 ret = -EFAULT;
199 goto out;
200 }
201
202 ret2 = lzma_code(&strm, LZMA_FINISH);
203 if (ret2 != LZMA_STREAM_END) {
204 ret = -EFSCORRUPTED;
205 goto out_lzma_end;
206 }
207
208 if (rq->decodedskip)
209 memcpy(rq->out, dest + rq->decodedskip,
210 rq->decodedlength - rq->decodedskip);
211
212 out_lzma_end:
213 lzma_end(&strm);
214 out:
215 if (buff)
216 free(buff);
217 return ret;
218 }
219 #endif
220
221 #ifdef LZ4_ENABLED
222 #include <lz4.h>
223
z_erofs_decompress_lz4(struct z_erofs_decompress_req * rq)224 static int z_erofs_decompress_lz4(struct z_erofs_decompress_req *rq)
225 {
226 int ret = 0;
227 char *dest = rq->out;
228 char *src = rq->in;
229 char *buff = NULL;
230 bool support_0padding = false;
231 unsigned int inputmargin = 0;
232 struct erofs_sb_info *sbi = rq->sbi;
233
234 if (erofs_sb_has_lz4_0padding(sbi)) {
235 support_0padding = true;
236
237 while (!src[inputmargin & (erofs_blksiz(sbi) - 1)])
238 if (!(++inputmargin & (erofs_blksiz(sbi) - 1)))
239 break;
240
241 if (inputmargin >= rq->inputsize)
242 return -EIO;
243 }
244
245 if (rq->decodedskip) {
246 buff = malloc(rq->decodedlength);
247 if (!buff)
248 return -ENOMEM;
249 dest = buff;
250 }
251
252 if (rq->partial_decoding || !support_0padding)
253 ret = LZ4_decompress_safe_partial(src + inputmargin, dest,
254 rq->inputsize - inputmargin,
255 rq->decodedlength, rq->decodedlength);
256 else
257 ret = LZ4_decompress_safe(src + inputmargin, dest,
258 rq->inputsize - inputmargin,
259 rq->decodedlength);
260
261 if (ret != (int)rq->decodedlength) {
262 erofs_err("failed to %s decompress %d in[%u, %u] out[%u]",
263 rq->partial_decoding ? "partial" : "full",
264 ret, rq->inputsize, inputmargin, rq->decodedlength);
265 ret = -EIO;
266 goto out;
267 }
268
269 if (rq->decodedskip)
270 memcpy(rq->out, dest + rq->decodedskip,
271 rq->decodedlength - rq->decodedskip);
272
273 out:
274 if (buff)
275 free(buff);
276
277 return ret;
278 }
279 #endif
280
z_erofs_decompress(struct z_erofs_decompress_req * rq)281 int z_erofs_decompress(struct z_erofs_decompress_req *rq)
282 {
283 struct erofs_sb_info *sbi = rq->sbi;
284
285 if (rq->alg == Z_EROFS_COMPRESSION_INTERLACED) {
286 unsigned int count, rightpart, skip;
287
288 /* XXX: should support inputsize >= erofs_blksiz(sbi) later */
289 if (rq->inputsize > erofs_blksiz(sbi))
290 return -EFSCORRUPTED;
291
292 if (rq->decodedlength > erofs_blksiz(sbi))
293 return -EFSCORRUPTED;
294
295 if (rq->decodedlength < rq->decodedskip)
296 return -EFSCORRUPTED;
297
298 count = rq->decodedlength - rq->decodedskip;
299 skip = erofs_blkoff(sbi, rq->interlaced_offset + rq->decodedskip);
300 rightpart = min(erofs_blksiz(sbi) - skip, count);
301 memcpy(rq->out, rq->in + skip, rightpart);
302 memcpy(rq->out + rightpart, rq->in, count - rightpart);
303 return 0;
304 } else if (rq->alg == Z_EROFS_COMPRESSION_SHIFTED) {
305 if (rq->decodedlength > rq->inputsize)
306 return -EFSCORRUPTED;
307
308 DBG_BUGON(rq->decodedlength < rq->decodedskip);
309 memcpy(rq->out, rq->in + rq->decodedskip,
310 rq->decodedlength - rq->decodedskip);
311 return 0;
312 }
313
314 #ifdef LZ4_ENABLED
315 if (rq->alg == Z_EROFS_COMPRESSION_LZ4)
316 return z_erofs_decompress_lz4(rq);
317 #endif
318 #ifdef HAVE_LIBLZMA
319 if (rq->alg == Z_EROFS_COMPRESSION_LZMA)
320 return z_erofs_decompress_lzma(rq);
321 #endif
322 #if defined(HAVE_ZLIB) || defined(HAVE_LIBDEFLATE)
323 if (rq->alg == Z_EROFS_COMPRESSION_DEFLATE)
324 return z_erofs_decompress_deflate(rq);
325 #endif
326 return -EOPNOTSUPP;
327 }
328