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_LIBLZMA
13 #include <lzma.h>
14
z_erofs_decompress_lzma(struct z_erofs_decompress_req * rq)15 static int z_erofs_decompress_lzma(struct z_erofs_decompress_req *rq)
16 {
17 int ret = 0;
18 u8 *dest = (u8 *)rq->out;
19 u8 *src = (u8 *)rq->in;
20 u8 *buff = NULL;
21 unsigned int inputmargin = 0;
22 lzma_stream strm;
23 lzma_ret ret2;
24
25 while (!src[inputmargin & ~PAGE_MASK])
26 if (!(++inputmargin & ~PAGE_MASK))
27 break;
28
29 if (inputmargin >= rq->inputsize)
30 return -EFSCORRUPTED;
31
32 if (rq->decodedskip) {
33 buff = malloc(rq->decodedlength);
34 if (!buff)
35 return -ENOMEM;
36 dest = buff;
37 }
38
39 strm = (lzma_stream)LZMA_STREAM_INIT;
40 strm.next_in = src + inputmargin;
41 strm.avail_in = rq->inputsize - inputmargin;
42 strm.next_out = dest;
43 strm.avail_out = rq->decodedlength;
44
45 ret2 = lzma_microlzma_decoder(&strm, strm.avail_in, rq->decodedlength,
46 !rq->partial_decoding,
47 Z_EROFS_LZMA_MAX_DICT_SIZE);
48 if (ret2 != LZMA_OK) {
49 erofs_err("fail to initialize lzma decoder %u", ret2 | 0U);
50 ret = -EFAULT;
51 goto out;
52 }
53
54 ret2 = lzma_code(&strm, LZMA_FINISH);
55 if (ret2 != LZMA_STREAM_END) {
56 ret = -EFSCORRUPTED;
57 goto out_lzma_end;
58 }
59
60 if (rq->decodedskip)
61 memcpy(rq->out, dest + rq->decodedskip,
62 rq->decodedlength - rq->decodedskip);
63
64 out_lzma_end:
65 lzma_end(&strm);
66 out:
67 if (buff)
68 free(buff);
69 return ret;
70 }
71 #endif
72
73 #ifdef LZ4_ENABLED
74 #include <lz4.h>
75
z_erofs_decompress_lz4(struct z_erofs_decompress_req * rq)76 static int z_erofs_decompress_lz4(struct z_erofs_decompress_req *rq)
77 {
78 int ret = 0;
79 char *dest = rq->out;
80 char *src = rq->in;
81 char *buff = NULL;
82 bool support_0padding = false;
83 unsigned int inputmargin = 0;
84
85 if (erofs_sb_has_lz4_0padding()) {
86 support_0padding = true;
87
88 while (!src[inputmargin & ~PAGE_MASK])
89 if (!(++inputmargin & ~PAGE_MASK))
90 break;
91
92 if (inputmargin >= rq->inputsize)
93 return -EIO;
94 }
95
96 if (rq->decodedskip) {
97 buff = malloc(rq->decodedlength);
98 if (!buff)
99 return -ENOMEM;
100 dest = buff;
101 }
102
103 if (rq->partial_decoding || !support_0padding)
104 ret = LZ4_decompress_safe_partial(src + inputmargin, dest,
105 rq->inputsize - inputmargin,
106 rq->decodedlength, rq->decodedlength);
107 else
108 ret = LZ4_decompress_safe(src + inputmargin, dest,
109 rq->inputsize - inputmargin,
110 rq->decodedlength);
111
112 if (ret != (int)rq->decodedlength) {
113 erofs_err("failed to %s decompress %d in[%u, %u] out[%u]",
114 rq->partial_decoding ? "partial" : "full",
115 ret, rq->inputsize, inputmargin, rq->decodedlength);
116 ret = -EIO;
117 goto out;
118 }
119
120 if (rq->decodedskip)
121 memcpy(rq->out, dest + rq->decodedskip,
122 rq->decodedlength - rq->decodedskip);
123
124 out:
125 if (buff)
126 free(buff);
127
128 return ret;
129 }
130 #endif
131
z_erofs_decompress(struct z_erofs_decompress_req * rq)132 int z_erofs_decompress(struct z_erofs_decompress_req *rq)
133 {
134 if (rq->alg == Z_EROFS_COMPRESSION_SHIFTED) {
135 if (rq->inputsize > EROFS_BLKSIZ)
136 return -EFSCORRUPTED;
137
138 DBG_BUGON(rq->decodedlength > EROFS_BLKSIZ);
139 DBG_BUGON(rq->decodedlength < rq->decodedskip);
140
141 memcpy(rq->out, rq->in + rq->decodedskip,
142 rq->decodedlength - rq->decodedskip);
143 return 0;
144 }
145
146 #ifdef LZ4_ENABLED
147 if (rq->alg == Z_EROFS_COMPRESSION_LZ4)
148 return z_erofs_decompress_lz4(rq);
149 #endif
150 #ifdef HAVE_LIBLZMA
151 if (rq->alg == Z_EROFS_COMPRESSION_LZMA)
152 return z_erofs_decompress_lzma(rq);
153 #endif
154 return -EOPNOTSUPP;
155 }
156