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 // TODO when we can depend on Qt 5.12 Use a QScopeGuard to call freeFilters
73 lzma_filter filters[5];
74 filters[0].id = LZMA_VLI_UNKNOWN;
75
76 switch (flag) {
77 case AUTO:
78 /* We set the memlimit for decompression to 100MiB which should be
79 * more than enough to be sufficient for level 9 which requires 65 MiB.
80 */
81 result = lzma_auto_decoder(strm: &d->zStream, memlimit: 100 << 20, flags: 0);
82 if (result != LZMA_OK) {
83 qCWarning(KArchiveLog) << "lzma_auto_decoder returned" << result;
84 return false;
85 }
86 break;
87 case LZMA: {
88 filters[0].id = LZMA_FILTER_LZMA1;
89 filters[0].options = nullptr;
90 filters[1].id = LZMA_VLI_UNKNOWN;
91 filters[1].options = nullptr;
92
93 Q_ASSERT(properties.size() == 5);
94 unsigned char props[5];
95 for (int i = 0; i < properties.size(); ++i) {
96 props[i] = properties[i];
97 }
98
99 result = lzma_properties_decode(filter: &filters[0], allocator: nullptr, props, props_size: sizeof(props));
100 if (result != LZMA_OK) {
101 qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result;
102 freeFilters(filters);
103 return false;
104 }
105 break;
106 }
107 case LZMA2: {
108 filters[0].id = LZMA_FILTER_LZMA2;
109 filters[0].options = nullptr;
110 filters[1].id = LZMA_VLI_UNKNOWN;
111 filters[1].options = nullptr;
112
113 Q_ASSERT(properties.size() == 1);
114 unsigned char props[1];
115 props[0] = properties[0];
116
117 result = lzma_properties_decode(filter: &filters[0], allocator: nullptr, props, props_size: sizeof(props));
118 if (result != LZMA_OK) {
119 qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result;
120 freeFilters(filters);
121 return false;
122 }
123 break;
124 }
125 case BCJ: {
126 filters[0].id = LZMA_FILTER_X86;
127 filters[0].options = nullptr;
128
129 unsigned char props[5] = {0x5d, 0x00, 0x00, 0x08, 0x00};
130 filters[1].id = LZMA_FILTER_LZMA1;
131 filters[1].options = nullptr;
132 result = lzma_properties_decode(filter: &filters[1], allocator: nullptr, props, props_size: sizeof(props));
133 if (result != LZMA_OK) {
134 qCWarning(KArchiveLog) << "lzma_properties_decode1 returned" << result;
135 freeFilters(filters);
136 return false;
137 }
138
139 filters[2].id = LZMA_VLI_UNKNOWN;
140 filters[2].options = nullptr;
141
142 break;
143 }
144 case POWERPC:
145 case IA64:
146 case ARM:
147 case ARMTHUMB:
148 case SPARC:
149 // qCDebug(KArchiveLog) << "flag" << flag << "props size" << properties.size();
150 break;
151 }
152
153 if (flag != AUTO) {
154 result = lzma_raw_decoder(strm: &d->zStream, filters);
155 if (result != LZMA_OK) {
156 qCWarning(KArchiveLog) << "lzma_raw_decoder returned" << result;
157 freeFilters(filters);
158 return false;
159 }
160 }
161 freeFilters(filters);
162
163 } else if (mode == QIODevice::WriteOnly) {
164 if (flag == AUTO) {
165 result = lzma_easy_encoder(strm: &d->zStream, LZMA_PRESET_DEFAULT, check: LZMA_CHECK_CRC32);
166 } else {
167 lzma_filter filters[5];
168 if (flag == LZMA2) {
169 lzma_options_lzma lzma_opt;
170 lzma_lzma_preset(options: &lzma_opt, LZMA_PRESET_DEFAULT);
171
172 filters[0].id = LZMA_FILTER_LZMA2;
173 filters[0].options = &lzma_opt;
174 filters[1].id = LZMA_VLI_UNKNOWN;
175 filters[1].options = nullptr;
176 }
177 result = lzma_raw_encoder(strm: &d->zStream, filters);
178 }
179 if (result != LZMA_OK) {
180 qCWarning(KArchiveLog) << "lzma_easy_encoder returned" << result;
181 return false;
182 }
183 } else {
184 // qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
185 return false;
186 }
187 d->mode = mode;
188 d->isInitialized = true;
189 return true;
190}
191
192int KXzFilter::mode() const
193{
194 return d->mode;
195}
196
197bool KXzFilter::terminate()
198{
199 if (d->mode == QIODevice::ReadOnly || d->mode == QIODevice::WriteOnly) {
200 lzma_end(strm: &d->zStream);
201 } else {
202 // qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
203 return false;
204 }
205 d->isInitialized = false;
206 return true;
207}
208
209void KXzFilter::reset()
210{
211 // qCDebug(KArchiveLog) << "KXzFilter::reset";
212 // liblzma doesn't have a reset call...
213 terminate();
214 init(mode: d->mode);
215}
216
217void KXzFilter::setOutBuffer(char *data, uint maxlen)
218{
219 d->zStream.avail_out = maxlen;
220 d->zStream.next_out = (uint8_t *)data;
221}
222
223void KXzFilter::setInBuffer(const char *data, unsigned int size)
224{
225 d->zStream.avail_in = size;
226 d->zStream.next_in = (uint8_t *)const_cast<char *>(data);
227}
228
229int KXzFilter::inBufferAvailable() const
230{
231 return d->zStream.avail_in;
232}
233
234int KXzFilter::outBufferAvailable() const
235{
236 return d->zStream.avail_out;
237}
238
239KXzFilter::Result KXzFilter::uncompress()
240{
241 // qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out =" << outBufferAvailable();
242 lzma_ret result;
243 result = lzma_code(strm: &d->zStream, action: LZMA_RUN);
244
245 /*if (result != LZMA_OK) {
246 qCDebug(KArchiveLog) << "lzma_code returned " << result;
247 //qCDebug(KArchiveLog) << "KXzFilter::uncompress " << ( result == LZMA_STREAM_END ? KFilterBase::End : KFilterBase::Error );
248 }*/
249
250 switch (result) {
251 case LZMA_OK:
252 return KFilterBase::Ok;
253 case LZMA_STREAM_END:
254 return KFilterBase::End;
255 default:
256 return KFilterBase::Error;
257 }
258}
259
260KXzFilter::Result KXzFilter::compress(bool finish)
261{
262 // qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
263 lzma_ret result = lzma_code(strm: &d->zStream, action: finish ? LZMA_FINISH : LZMA_RUN);
264 switch (result) {
265 case LZMA_OK:
266 return KFilterBase::Ok;
267 break;
268 case LZMA_STREAM_END:
269 // qCDebug(KArchiveLog) << " lzma_code returned " << result;
270 return KFilterBase::End;
271 break;
272 default:
273 // qCDebug(KArchiveLog) << " lzma_code returned " << result;
274 return KFilterBase::Error;
275 break;
276 }
277}
278
279#endif /* HAVE_XZ_SUPPORT */
280

source code of karchive/src/kxzfilter.cpp