File indexing completed on 2024-04-21 03:52:33

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     QList<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 QList<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 */