1/* This file is part of the KDE libraries
2 SPDX-FileCopyrightText: 2007-2008 Per Øyvind Karlsen <peroyvind@mandriva.org>
3
4 Based on kbzip2filter:
5 SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "kxzfilter.h"
11#include "loggingcategory.h"
12
13#if HAVE_XZ_SUPPORT
14extern "C" {
15#include <lzma.h>
16}
17
18#include <QDebug>
19
20#include <QIODevice>
21
22class Q_DECL_HIDDEN KXzFilter::Private
23{
24public:
25 Private()
26 : isInitialized(false)
27 {
28 memset(s: &zStream, c: 0, n: sizeof(zStream));
29 mode = 0;
30 }
31
32 lzma_stream zStream;
33 int mode;
34 bool isInitialized;
35 KXzFilter::Flag flag;
36};
37
38KXzFilter::KXzFilter()
39 : d(new Private)
40{
41}
42
43KXzFilter::~KXzFilter()
44{
45 delete d;
46}
47
48bool KXzFilter::init(int mode)
49{
50 QList<unsigned char> props;
51 return init(mode, flag: AUTO, props);
52}
53
54static void freeFilters(lzma_filter filters[])
55{
56 for (int i = 0; filters[i].id != LZMA_VLI_UNKNOWN; i++) {
57 free(ptr: filters[i].options);
58 }
59}
60
61bool KXzFilter::init(int mode, Flag flag, const QList<unsigned char> &properties)
62{
63 if (d->isInitialized) {
64 terminate();
65 }
66
67 d->flag = flag;
68 lzma_ret result;
69 d->zStream.next_in = nullptr;
70 d->zStream.avail_in = 0;
71 if (mode == QIODevice::ReadOnly) {
72 lzma_filter filters[5];
73 const auto filtersCleanupGuard = qScopeGuard(f: [&filters] {
74 freeFilters(filters);
75 });
76
77 filters[0].id = LZMA_VLI_UNKNOWN;
78
79 switch (flag) {
80 case AUTO:
81 /* We set the memlimit for decompression to 100MiB which should be
82 * more than enough to be sufficient for level 9 which requires 65 MiB.
83 */
84 result = lzma_auto_decoder(strm: &d->zStream, memlimit: 100 << 20, flags: 0);
85 if (result != LZMA_OK) {
86 qCWarning(KArchiveLog) << "lzma_auto_decoder returned" << result;
87 return false;
88 }
89 break;
90 case LZMA: {
91 filters[0].id = LZMA_FILTER_LZMA1;
92 filters[0].options = nullptr;
93 filters[1].id = LZMA_VLI_UNKNOWN;
94 filters[1].options = nullptr;
95
96 if (properties.size() != 5) {
97 qCWarning(KArchiveLog) << "KXzFilter::init: LZMA unexpected number of properties" << properties.size();
98 return false;
99 }
100 unsigned char props[5];
101 for (int i = 0; i < properties.size(); ++i) {
102 props[i] = properties[i];
103 }
104
105 result = lzma_properties_decode(filter: &filters[0], allocator: nullptr, props, props_size: sizeof(props));
106 if (result != LZMA_OK) {
107 qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result;
108 return false;
109 }
110 break;
111 }
112 case LZMA2: {
113 filters[0].id = LZMA_FILTER_LZMA2;
114 filters[0].options = nullptr;
115 filters[1].id = LZMA_VLI_UNKNOWN;
116 filters[1].options = nullptr;
117
118 if (properties.size() != 1) {
119 qCWarning(KArchiveLog) << "KXzFilter::init: LZMA2 unexpected number of properties" << properties.size();
120 return false;
121 }
122 unsigned char props[1];
123 props[0] = properties[0];
124
125 result = lzma_properties_decode(filter: &filters[0], allocator: nullptr, props, props_size: sizeof(props));
126 if (result != LZMA_OK) {
127 qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result;
128 return false;
129 }
130 break;
131 }
132 case BCJ: {
133 filters[0].id = LZMA_FILTER_X86;
134 filters[0].options = nullptr;
135
136 unsigned char props[5] = {0x5d, 0x00, 0x00, 0x08, 0x00};
137 filters[1].id = LZMA_FILTER_LZMA1;
138 filters[1].options = nullptr;
139 result = lzma_properties_decode(filter: &filters[1], allocator: nullptr, props, props_size: sizeof(props));
140 if (result != LZMA_OK) {
141 qCWarning(KArchiveLog) << "lzma_properties_decode1 returned" << result;
142 return false;
143 }
144
145 filters[2].id = LZMA_VLI_UNKNOWN;
146 filters[2].options = nullptr;
147
148 break;
149 }
150 case POWERPC:
151 case IA64:
152 case ARM:
153 case ARMTHUMB:
154 case SPARC:
155 // qCDebug(KArchiveLog) << "flag" << flag << "props size" << properties.size();
156 break;
157 }
158
159 if (flag != AUTO) {
160 result = lzma_raw_decoder(strm: &d->zStream, filters);
161 if (result != LZMA_OK) {
162 qCWarning(KArchiveLog) << "lzma_raw_decoder returned" << result;
163 return false;
164 }
165 }
166
167 } else if (mode == QIODevice::WriteOnly) {
168 if (flag == AUTO) {
169 result = lzma_easy_encoder(strm: &d->zStream, LZMA_PRESET_DEFAULT, check: LZMA_CHECK_CRC32);
170 } else {
171 lzma_filter filters[5];
172 if (flag == LZMA2) {
173 lzma_options_lzma lzma_opt;
174 lzma_lzma_preset(options: &lzma_opt, LZMA_PRESET_DEFAULT);
175
176 filters[0].id = LZMA_FILTER_LZMA2;
177 filters[0].options = &lzma_opt;
178 filters[1].id = LZMA_VLI_UNKNOWN;
179 filters[1].options = nullptr;
180 }
181 result = lzma_raw_encoder(strm: &d->zStream, filters);
182 }
183 if (result != LZMA_OK) {
184 qCWarning(KArchiveLog) << "lzma_easy_encoder returned" << result;
185 return false;
186 }
187 } else {
188 // qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
189 return false;
190 }
191 d->mode = mode;
192 d->isInitialized = true;
193 return true;
194}
195
196int KXzFilter::mode() const
197{
198 return d->mode;
199}
200
201bool KXzFilter::terminate()
202{
203 if (d->mode == QIODevice::ReadOnly || d->mode == QIODevice::WriteOnly) {
204 lzma_end(strm: &d->zStream);
205 } else {
206 // qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
207 return false;
208 }
209 d->isInitialized = false;
210 return true;
211}
212
213void KXzFilter::reset()
214{
215 // qCDebug(KArchiveLog) << "KXzFilter::reset";
216 // liblzma doesn't have a reset call...
217 terminate();
218 init(mode: d->mode);
219}
220
221void KXzFilter::setOutBuffer(char *data, uint maxlen)
222{
223 d->zStream.avail_out = maxlen;
224 d->zStream.next_out = (uint8_t *)data;
225}
226
227void KXzFilter::setInBuffer(const char *data, unsigned int size)
228{
229 d->zStream.avail_in = size;
230 d->zStream.next_in = (uint8_t *)const_cast<char *>(data);
231}
232
233int KXzFilter::inBufferAvailable() const
234{
235 return d->zStream.avail_in;
236}
237
238int KXzFilter::outBufferAvailable() const
239{
240 return d->zStream.avail_out;
241}
242
243KXzFilter::Result KXzFilter::uncompress()
244{
245 // qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out =" << outBufferAvailable();
246 lzma_ret result;
247 result = lzma_code(strm: &d->zStream, action: LZMA_RUN);
248
249 /*if (result != LZMA_OK) {
250 qCDebug(KArchiveLog) << "lzma_code returned " << result;
251 //qCDebug(KArchiveLog) << "KXzFilter::uncompress " << ( result == LZMA_STREAM_END ? KFilterBase::End : KFilterBase::Error );
252 }*/
253
254 switch (result) {
255 case LZMA_OK:
256 return KFilterBase::Ok;
257 case LZMA_STREAM_END:
258 return KFilterBase::End;
259 default:
260 return KFilterBase::Error;
261 }
262}
263
264KXzFilter::Result KXzFilter::compress(bool finish)
265{
266 // qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
267 lzma_ret result = lzma_code(strm: &d->zStream, action: finish ? LZMA_FINISH : LZMA_RUN);
268 switch (result) {
269 case LZMA_OK:
270 return KFilterBase::Ok;
271 break;
272 case LZMA_STREAM_END:
273 // qCDebug(KArchiveLog) << " lzma_code returned " << result;
274 return KFilterBase::End;
275 break;
276 default:
277 // qCDebug(KArchiveLog) << " lzma_code returned " << result;
278 return KFilterBase::Error;
279 break;
280 }
281}
282
283#endif /* HAVE_XZ_SUPPORT */
284

source code of karchive/src/kxzfilter.cpp