File indexing completed on 2025-02-16 13:00:37
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 2007-2008 Per Øyvind Karlsen <peroyvind@mandriva.org> 0003 0004 Based on kbzip2filter: 0005 SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "kxzfilter.h" 0011 #include "loggingcategory.h" 0012 0013 #if HAVE_XZ_SUPPORT 0014 extern "C" { 0015 #include <lzma.h> 0016 } 0017 0018 #include <QDebug> 0019 0020 #include <QIODevice> 0021 0022 class Q_DECL_HIDDEN KXzFilter::Private 0023 { 0024 public: 0025 Private() 0026 : isInitialized(false) 0027 { 0028 memset(&zStream, 0, sizeof(zStream)); 0029 mode = 0; 0030 } 0031 0032 lzma_stream zStream; 0033 int mode; 0034 bool isInitialized; 0035 KXzFilter::Flag flag; 0036 }; 0037 0038 KXzFilter::KXzFilter() 0039 : d(new Private) 0040 { 0041 } 0042 0043 KXzFilter::~KXzFilter() 0044 { 0045 delete d; 0046 } 0047 0048 bool KXzFilter::init(int mode) 0049 { 0050 QVector<unsigned char> props; 0051 return init(mode, AUTO, props); 0052 } 0053 0054 static void freeFilters(lzma_filter filters[]) 0055 { 0056 for (int i = 0; filters[i].id != LZMA_VLI_UNKNOWN; i++) { 0057 free(filters[i].options); 0058 } 0059 } 0060 0061 bool KXzFilter::init(int mode, Flag flag, const QVector<unsigned char> &properties) 0062 { 0063 if (d->isInitialized) { 0064 terminate(); 0065 } 0066 0067 d->flag = flag; 0068 lzma_ret result; 0069 d->zStream.next_in = nullptr; 0070 d->zStream.avail_in = 0; 0071 if (mode == QIODevice::ReadOnly) { 0072 // TODO when we can depend on Qt 5.12 Use a QScopeGuard to call freeFilters 0073 lzma_filter filters[5]; 0074 filters[0].id = LZMA_VLI_UNKNOWN; 0075 0076 switch (flag) { 0077 case AUTO: 0078 /* We set the memlimit for decompression to 100MiB which should be 0079 * more than enough to be sufficient for level 9 which requires 65 MiB. 0080 */ 0081 result = lzma_auto_decoder(&d->zStream, 100 << 20, 0); 0082 if (result != LZMA_OK) { 0083 qCWarning(KArchiveLog) << "lzma_auto_decoder returned" << result; 0084 return false; 0085 } 0086 break; 0087 case LZMA: { 0088 filters[0].id = LZMA_FILTER_LZMA1; 0089 filters[0].options = nullptr; 0090 filters[1].id = LZMA_VLI_UNKNOWN; 0091 filters[1].options = nullptr; 0092 0093 Q_ASSERT(properties.size() == 5); 0094 unsigned char props[5]; 0095 for (int i = 0; i < properties.size(); ++i) { 0096 props[i] = properties[i]; 0097 } 0098 0099 result = lzma_properties_decode(&filters[0], nullptr, props, sizeof(props)); 0100 if (result != LZMA_OK) { 0101 qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result; 0102 freeFilters(filters); 0103 return false; 0104 } 0105 break; 0106 } 0107 case LZMA2: { 0108 filters[0].id = LZMA_FILTER_LZMA2; 0109 filters[0].options = nullptr; 0110 filters[1].id = LZMA_VLI_UNKNOWN; 0111 filters[1].options = nullptr; 0112 0113 Q_ASSERT(properties.size() == 1); 0114 unsigned char props[1]; 0115 props[0] = properties[0]; 0116 0117 result = lzma_properties_decode(&filters[0], nullptr, props, sizeof(props)); 0118 if (result != LZMA_OK) { 0119 qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result; 0120 freeFilters(filters); 0121 return false; 0122 } 0123 break; 0124 } 0125 case BCJ: { 0126 filters[0].id = LZMA_FILTER_X86; 0127 filters[0].options = nullptr; 0128 0129 unsigned char props[5] = {0x5d, 0x00, 0x00, 0x08, 0x00}; 0130 filters[1].id = LZMA_FILTER_LZMA1; 0131 filters[1].options = nullptr; 0132 result = lzma_properties_decode(&filters[1], nullptr, props, sizeof(props)); 0133 if (result != LZMA_OK) { 0134 qCWarning(KArchiveLog) << "lzma_properties_decode1 returned" << result; 0135 freeFilters(filters); 0136 return false; 0137 } 0138 0139 filters[2].id = LZMA_VLI_UNKNOWN; 0140 filters[2].options = nullptr; 0141 0142 break; 0143 } 0144 case POWERPC: 0145 case IA64: 0146 case ARM: 0147 case ARMTHUMB: 0148 case SPARC: 0149 // qCDebug(KArchiveLog) << "flag" << flag << "props size" << properties.size(); 0150 break; 0151 } 0152 0153 if (flag != AUTO) { 0154 result = lzma_raw_decoder(&d->zStream, filters); 0155 if (result != LZMA_OK) { 0156 qCWarning(KArchiveLog) << "lzma_raw_decoder returned" << result; 0157 freeFilters(filters); 0158 return false; 0159 } 0160 } 0161 freeFilters(filters); 0162 0163 } else if (mode == QIODevice::WriteOnly) { 0164 if (flag == AUTO) { 0165 result = lzma_easy_encoder(&d->zStream, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC32); 0166 } else { 0167 lzma_filter filters[5]; 0168 if (flag == LZMA2) { 0169 lzma_options_lzma lzma_opt; 0170 lzma_lzma_preset(&lzma_opt, LZMA_PRESET_DEFAULT); 0171 0172 filters[0].id = LZMA_FILTER_LZMA2; 0173 filters[0].options = &lzma_opt; 0174 filters[1].id = LZMA_VLI_UNKNOWN; 0175 filters[1].options = nullptr; 0176 } 0177 result = lzma_raw_encoder(&d->zStream, filters); 0178 } 0179 if (result != LZMA_OK) { 0180 qCWarning(KArchiveLog) << "lzma_easy_encoder returned" << result; 0181 return false; 0182 } 0183 } else { 0184 // qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported"; 0185 return false; 0186 } 0187 d->mode = mode; 0188 d->isInitialized = true; 0189 return true; 0190 } 0191 0192 int KXzFilter::mode() const 0193 { 0194 return d->mode; 0195 } 0196 0197 bool KXzFilter::terminate() 0198 { 0199 if (d->mode == QIODevice::ReadOnly || d->mode == QIODevice::WriteOnly) { 0200 lzma_end(&d->zStream); 0201 } else { 0202 // qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported"; 0203 return false; 0204 } 0205 d->isInitialized = false; 0206 return true; 0207 } 0208 0209 void KXzFilter::reset() 0210 { 0211 // qCDebug(KArchiveLog) << "KXzFilter::reset"; 0212 // liblzma doesn't have a reset call... 0213 terminate(); 0214 init(d->mode); 0215 } 0216 0217 void KXzFilter::setOutBuffer(char *data, uint maxlen) 0218 { 0219 d->zStream.avail_out = maxlen; 0220 d->zStream.next_out = (uint8_t *)data; 0221 } 0222 0223 void KXzFilter::setInBuffer(const char *data, unsigned int size) 0224 { 0225 d->zStream.avail_in = size; 0226 d->zStream.next_in = (uint8_t *)const_cast<char *>(data); 0227 } 0228 0229 int KXzFilter::inBufferAvailable() const 0230 { 0231 return d->zStream.avail_in; 0232 } 0233 0234 int KXzFilter::outBufferAvailable() const 0235 { 0236 return d->zStream.avail_out; 0237 } 0238 0239 KXzFilter::Result KXzFilter::uncompress() 0240 { 0241 // qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out =" << outBufferAvailable(); 0242 lzma_ret result; 0243 result = lzma_code(&d->zStream, LZMA_RUN); 0244 0245 /*if (result != LZMA_OK) { 0246 qCDebug(KArchiveLog) << "lzma_code returned " << result; 0247 //qCDebug(KArchiveLog) << "KXzFilter::uncompress " << ( result == LZMA_STREAM_END ? KFilterBase::End : KFilterBase::Error ); 0248 }*/ 0249 0250 switch (result) { 0251 case LZMA_OK: 0252 return KFilterBase::Ok; 0253 case LZMA_STREAM_END: 0254 return KFilterBase::End; 0255 default: 0256 return KFilterBase::Error; 0257 } 0258 } 0259 0260 KXzFilter::Result KXzFilter::compress(bool finish) 0261 { 0262 // qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable(); 0263 lzma_ret result = lzma_code(&d->zStream, finish ? LZMA_FINISH : LZMA_RUN); 0264 switch (result) { 0265 case LZMA_OK: 0266 return KFilterBase::Ok; 0267 break; 0268 case LZMA_STREAM_END: 0269 // qCDebug(KArchiveLog) << " lzma_code returned " << result; 0270 return KFilterBase::End; 0271 break; 0272 default: 0273 // qCDebug(KArchiveLog) << " lzma_code returned " << result; 0274 return KFilterBase::Error; 0275 break; 0276 } 0277 } 0278 0279 #endif /* HAVE_XZ_SUPPORT */