File indexing completed on 2024-04-21 03:52:30
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "k7zip.h" 0008 #include "karchive_p.h" 0009 #include "loggingcategory.h" 0010 0011 #include <QBuffer> 0012 #include <QDebug> 0013 #include <QDir> 0014 #include <QFile> 0015 #include <QTimeZone> 0016 #include <qplatformdefs.h> 0017 0018 #include "kcompressiondevice.h" 0019 #include "klimitediodevice_p.h" 0020 #include <kfilterbase.h> 0021 #include <kxzfilter.h> 0022 0023 #include "zlib.h" 0024 #include <memory> 0025 #include <time.h> // time() 0026 0027 #ifndef QT_STAT_LNK 0028 #define QT_STAT_LNK 0120000 0029 #endif // QT_STAT_LNK 0030 0031 //////////////////////////////////////////////////////////////////////// 0032 /////////////////////////// K7Zip ////////////////////////////////////// 0033 //////////////////////////////////////////////////////////////////////// 0034 0035 #define BUFFER_SIZE 8 * 1024 0036 0037 static const unsigned char k7zip_signature[6] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; 0038 // static const unsigned char XZ_HEADER_MAGIC[6] = { 0xFD, '7', 'z', 'X', 'Z', 0x00 }; 0039 0040 /* clang-format off */ 0041 static QChar GetUi16(const char *p, quint64 offset) 0042 { 0043 return QChar(static_cast<unsigned char>(p[offset + 0]) 0044 | (static_cast<unsigned char>(p[1]) << 8)); 0045 } 0046 0047 static quint32 GetUi32(const char *p, quint64 offset) 0048 { 0049 return (static_cast<unsigned char>(p[offset + 0]) 0050 | (static_cast<unsigned char>(p[offset + 1]) << 8) 0051 | (static_cast<unsigned char>(p[offset + 2]) << 16) 0052 | (static_cast<unsigned char>(p[offset + 3]) << 24)); 0053 } 0054 0055 static quint64 GetUi64(const char *p, quint64 offset) 0056 { 0057 return (GetUi32(p, offset) 0058 | (static_cast<quint64>(GetUi32(p, offset + 4)) << 32)); 0059 } 0060 0061 static quint32 lzma2_dic_size_from_prop(int p) 0062 { 0063 return ((static_cast<quint32>(2) | (p & 1)) << ((p / 2) + 11)); 0064 } 0065 0066 /* clang-format on*/ 0067 0068 #define FILE_ATTRIBUTE_READONLY 1 0069 #define FILE_ATTRIBUTE_HIDDEN 2 0070 #define FILE_ATTRIBUTE_SYSTEM 4 0071 #define FILE_ATTRIBUTE_DIRECTORY 16 0072 #define FILE_ATTRIBUTE_ARCHIVE 32 0073 #define FILE_ATTRIBUTE_DEVICE 64 0074 #define FILE_ATTRIBUTE_NORMAL 128 0075 #define FILE_ATTRIBUTE_TEMPORARY 256 0076 #define FILE_ATTRIBUTE_SPARSE_FILE 512 0077 #define FILE_ATTRIBUTE_REPARSE_POINT 1024 0078 #define FILE_ATTRIBUTE_COMPRESSED 2048 0079 #define FILE_ATTRIBUTE_OFFLINE 0x1000 0080 #define FILE_ATTRIBUTE_ENCRYPTED 0x4000 0081 #define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ 0082 0083 enum HeaderType { 0084 kEnd, 0085 0086 kHeader, 0087 0088 kArchiveProperties, 0089 0090 kAdditionalStreamsInfo, 0091 kMainStreamsInfo, 0092 kFilesInfo, 0093 0094 kPackInfo, 0095 kUnpackInfo, 0096 kSubStreamsInfo, 0097 0098 kSize, 0099 kCRC, 0100 0101 kFolder, 0102 0103 kCodersUnpackSize, 0104 kNumUnpackStream, 0105 0106 kEmptyStream, 0107 kEmptyFile, 0108 kAnti, 0109 0110 kName, 0111 kCTime, 0112 kATime, 0113 kMTime, 0114 kAttributes, 0115 kComment, 0116 0117 kEncodedHeader, 0118 0119 kStartPos, 0120 kDummy, 0121 }; 0122 0123 // Method ID 0124 // static const quint64 k_Copy = 0x00; 0125 // static const quint64 k_Delta = 0x03; 0126 // static const quint64 k_x86 = 0x04; //BCJ 0127 // static const quint64 k_PPC = 0x05; // BIG Endian 0128 // static const quint64 k_IA64 = 0x06; 0129 // static const quint64 k_ARM = 0x07; // little Endian 0130 // static const quint64 k_ARM_Thumb = 0x08; // little Endian 0131 // static const quint64 k_SPARC = 0x09; 0132 static const quint64 k_LZMA2 = 0x21; 0133 // static const quint64 k_Swap2 = 0x020302; 0134 // static const quint64 k_Swap4 = 0x020304; 0135 static const quint64 k_LZMA = 0x030101; 0136 static const quint64 k_BCJ = 0x03030103; 0137 static const quint64 k_BCJ2 = 0x0303011B; 0138 // static const quint64 k_7zPPC = 0x03030205; 0139 // static const quint64 k_Alpha = 0x03030301; 0140 // static const quint64 k_7zIA64 = 0x03030401; 0141 // static const quint64 k_7zARM = 0x03030501; 0142 // static const quint64 k_M68 = 0x03030605; //Big Endian 0143 // static const quint64 k_ARMT = 0x03030701; 0144 // static const quint64 k_7zSPARC = 0x03030805; 0145 static const quint64 k_PPMD = 0x030401; 0146 // static const quint64 k_Experimental = 0x037F01; 0147 // static const quint64 k_Shrink = 0x040101; 0148 // static const quint64 k_Implode = 0x040106; 0149 // static const quint64 k_Deflate = 0x040108; 0150 // static const quint64 k_Deflate64 = 0x040109; 0151 // static const quint64 k_Imploding = 0x040110; 0152 // static const quint64 k_Jpeg = 0x040160; 0153 // static const quint64 k_WavPack = 0x040161; 0154 // static const quint64 k_PPMd = 0x040162; 0155 // static const quint64 k_wzAES = 0x040163; 0156 static const quint64 k_BZip2 = 0x040202; 0157 // static const quint64 k_Rar15 = 0x040301; 0158 // static const quint64 k_Rar20 = 0x040302; 0159 // static const quint64 k_Rar29 = 0x040303; 0160 // static const quint64 k_Arj = 0x040401; //1 2 3 0161 // static const quint64 k_Arj4 = 0x040402; 0162 // static const quint64 k_Z = 0x0405; 0163 // static const quint64 k_Lzh = 0x0406; 0164 // static const quint64 k_Cab = 0x0408; 0165 // static const quint64 k_DeflateNSIS = 0x040901; 0166 // static const quint64 k_Bzip2NSIS = 0x040902; 0167 static const quint64 k_AES = 0x06F10701; 0168 0169 /** 0170 * A K7ZipFileEntry represents a file in a 7zip archive. 0171 */ 0172 class K7ZipFileEntry : public KArchiveFile 0173 { 0174 public: 0175 K7ZipFileEntry(K7Zip *zip, 0176 const QString &name, 0177 int access, 0178 const QDateTime &date, 0179 const QString &user, 0180 const QString &group, 0181 const QString &symlink, 0182 qint64 pos, 0183 qint64 size, 0184 const QByteArray &data); 0185 0186 ~K7ZipFileEntry() override; 0187 0188 /** 0189 * @return the content of this file. 0190 * Call data() with care (only once per file), this data isn't cached. 0191 */ 0192 QByteArray data() const override; 0193 0194 /** 0195 * This method returns QIODevice (internal class: KLimitedIODevice) 0196 * on top of the underlying QIODevice. This is obviously for reading only. 0197 * 0198 * WARNING: Note that the ownership of the device is being transferred to the caller, 0199 * who will have to delete it. 0200 * 0201 * The returned device auto-opens (in readonly mode), no need to open it. 0202 * @return the QIODevice of the file 0203 */ 0204 QIODevice *createDevice() const override; 0205 0206 private: 0207 const QByteArray m_data; 0208 QBuffer *m_buffer; 0209 }; 0210 0211 K7ZipFileEntry::K7ZipFileEntry(K7Zip *zip, 0212 const QString &name, 0213 int access, 0214 const QDateTime &date, 0215 const QString &user, 0216 const QString &group, 0217 const QString &symlink, 0218 qint64 pos, 0219 qint64 size, 0220 const QByteArray &data) 0221 : KArchiveFile(zip, name, access, date, user, group, symlink, pos, size) 0222 , m_data(data) 0223 , m_buffer(new QBuffer) 0224 { 0225 m_buffer->setData(m_data); 0226 m_buffer->open(QIODevice::ReadOnly); 0227 } 0228 0229 K7ZipFileEntry::~K7ZipFileEntry() 0230 { 0231 delete m_buffer; 0232 } 0233 0234 QByteArray K7ZipFileEntry::data() const 0235 { 0236 return m_data.mid(position(), size()); 0237 } 0238 0239 QIODevice *K7ZipFileEntry::createDevice() const 0240 { 0241 return new KLimitedIODevice(m_buffer, position(), size()); 0242 } 0243 0244 class FileInfo 0245 { 0246 public: 0247 FileInfo() 0248 : size(0) 0249 , attributes(0) 0250 , crc(0) 0251 , attribDefined(false) 0252 , crcDefined(false) 0253 , hasStream(false) 0254 , isDir(false) 0255 { 0256 } 0257 0258 QString path; 0259 quint64 size; 0260 quint32 attributes; 0261 quint32 crc; 0262 bool attribDefined; 0263 bool crcDefined; 0264 bool hasStream; 0265 bool isDir; 0266 }; 0267 0268 class Folder 0269 { 0270 public: 0271 class FolderInfo 0272 { 0273 public: 0274 FolderInfo() 0275 : numInStreams(0) 0276 , numOutStreams(0) 0277 , methodID(0) 0278 { 0279 } 0280 0281 bool isSimpleCoder() const 0282 { 0283 return (numInStreams == 1) && (numOutStreams == 1); 0284 } 0285 0286 int numInStreams; 0287 int numOutStreams; 0288 QList<unsigned char> properties; 0289 quint64 methodID; 0290 }; 0291 0292 Folder() 0293 : unpackCRCDefined(false) 0294 , unpackCRC(0) 0295 { 0296 } 0297 0298 ~Folder() 0299 { 0300 qDeleteAll(folderInfos); 0301 } 0302 0303 Q_DISABLE_COPY(Folder) 0304 0305 quint64 getUnpackSize() const 0306 { 0307 if (unpackSizes.isEmpty()) { 0308 return 0; 0309 } 0310 for (int i = unpackSizes.size() - 1; i >= 0; i--) { 0311 if (findBindPairForOutStream(i) < 0) { 0312 return unpackSizes.at(i); 0313 } 0314 } 0315 return 0; 0316 } 0317 0318 int getNumOutStreams() const 0319 { 0320 int result = 0; 0321 for (int i = 0; i < folderInfos.size(); i++) { 0322 result += folderInfos.at(i)->numOutStreams; 0323 } 0324 return result; 0325 } 0326 0327 quint32 getCoderInStreamIndex(quint32 coderIndex) const 0328 { 0329 quint32 streamIndex = 0; 0330 for (quint32 i = 0; i < coderIndex; i++) { 0331 streamIndex += folderInfos.at(i)->numInStreams; 0332 } 0333 return streamIndex; 0334 } 0335 0336 quint32 getCoderOutStreamIndex(quint32 coderIndex) const 0337 { 0338 quint32 streamIndex = 0; 0339 for (quint32 i = 0; i < coderIndex; i++) { 0340 streamIndex += folderInfos.at(i)->numOutStreams; 0341 } 0342 return streamIndex; 0343 } 0344 0345 int findBindPairForInStream(size_t inStreamIndex) const 0346 { 0347 for (int i = 0; i < inIndexes.size(); i++) { 0348 if (inIndexes[i] == inStreamIndex) { 0349 return i; 0350 } 0351 } 0352 return -1; 0353 } 0354 0355 int findBindPairForOutStream(size_t outStreamIndex) const 0356 { 0357 for (int i = 0; i < outIndexes.size(); i++) { 0358 if (outIndexes[i] == outStreamIndex) { 0359 return i; 0360 } 0361 } 0362 return -1; 0363 } 0364 0365 int findPackStreamArrayIndex(size_t inStreamIndex) const 0366 { 0367 for (int i = 0; i < packedStreams.size(); i++) { 0368 if (packedStreams[i] == inStreamIndex) { 0369 return i; 0370 } 0371 } 0372 return -1; 0373 } 0374 0375 void findInStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex) const 0376 { 0377 for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) { 0378 quint32 curSize = folderInfos[coderIndex]->numInStreams; 0379 if (streamIndex < curSize) { 0380 coderStreamIndex = streamIndex; 0381 return; 0382 } 0383 streamIndex -= curSize; 0384 } 0385 } 0386 0387 void findOutStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex) const 0388 { 0389 for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) { 0390 quint32 curSize = folderInfos[coderIndex]->numOutStreams; 0391 if (streamIndex < curSize) { 0392 coderStreamIndex = streamIndex; 0393 return; 0394 } 0395 streamIndex -= curSize; 0396 } 0397 } 0398 0399 bool isEncrypted() const 0400 { 0401 for (int i = folderInfos.size() - 1; i >= 0; i--) { 0402 if (folderInfos.at(i)->methodID == k_AES) { 0403 return true; 0404 } 0405 } 0406 return false; 0407 } 0408 0409 // bool CheckStructure() const; 0410 0411 bool unpackCRCDefined; 0412 quint32 unpackCRC; 0413 QList<FolderInfo *> folderInfos; 0414 QList<quint64> inIndexes; 0415 QList<quint64> outIndexes; 0416 QList<quint64> packedStreams; 0417 QList<quint64> unpackSizes; 0418 }; 0419 0420 class Q_DECL_HIDDEN K7Zip::K7ZipPrivate 0421 { 0422 public: 0423 K7ZipPrivate(K7Zip *parent) 0424 : q(parent) 0425 , packPos(0) 0426 , numPackStreams(0) 0427 , buffer(nullptr) 0428 , pos(0) 0429 , end(0) 0430 , headerSize(0) 0431 , countSize(0) 0432 , m_currentFile(nullptr) 0433 { 0434 } 0435 0436 ~K7ZipPrivate() 0437 { 0438 qDeleteAll(folders); 0439 qDeleteAll(fileInfos); 0440 } 0441 0442 K7Zip *q; 0443 0444 QList<bool> packCRCsDefined; 0445 QList<quint32> packCRCs; 0446 QList<quint64> numUnpackStreamsInFolders; 0447 0448 QList<Folder *> folders; 0449 QList<FileInfo *> fileInfos; 0450 // File information 0451 QList<bool> cTimesDefined; 0452 QList<quint64> cTimes; 0453 QList<bool> aTimesDefined; 0454 QList<quint64> aTimes; 0455 QList<bool> mTimesDefined; 0456 QList<quint64> mTimes; 0457 QList<bool> startPositionsDefined; 0458 QList<quint64> startPositions; 0459 QList<int> fileInfoPopIDs; 0460 0461 quint64 packPos; 0462 quint64 numPackStreams; 0463 QList<quint64> packSizes; 0464 QList<quint64> unpackSizes; 0465 QList<bool> digestsDefined; 0466 QList<quint32> digests; 0467 0468 QList<bool> isAnti; 0469 0470 const char *buffer; 0471 quint64 pos; 0472 quint64 end; 0473 quint64 headerSize; 0474 quint64 countSize; 0475 0476 // Write 0477 QByteArray header; 0478 QByteArray outData; // Store data in this buffer before compress and write in archive. 0479 K7ZipFileEntry *m_currentFile; 0480 QList<KArchiveEntry *> m_entryList; 0481 0482 void clear() 0483 { 0484 packCRCsDefined.clear(); 0485 packCRCs.clear(); 0486 numUnpackStreamsInFolders.clear(); 0487 qDeleteAll(folders); 0488 folders.clear(); 0489 qDeleteAll(fileInfos); 0490 fileInfos.clear(); 0491 cTimesDefined.clear(); 0492 cTimes.clear(); 0493 aTimesDefined.clear(); 0494 aTimes.clear(); 0495 mTimesDefined.clear(); 0496 mTimes.clear(); 0497 startPositionsDefined.clear(); 0498 startPositions.clear(); 0499 fileInfoPopIDs.clear(); 0500 packSizes.clear(); 0501 unpackSizes.clear(); 0502 digestsDefined.clear(); 0503 digests.clear(); 0504 isAnti.clear(); 0505 0506 buffer = nullptr; 0507 pos = 0; 0508 end = 0; 0509 headerSize = 0; 0510 countSize = 0; 0511 } 0512 0513 // Read 0514 int readByte(); 0515 quint32 readUInt32(); 0516 quint64 readUInt64(); 0517 quint64 readNumber(); 0518 QString readString(); 0519 void readHashDigests(int numItems, QList<bool> &digestsDefined, QList<quint32> &digests); 0520 void readBoolVector(int numItems, QList<bool> &v); 0521 void readBoolVector2(int numItems, QList<bool> &v); 0522 void skipData(int size); 0523 bool findAttribute(int attribute); 0524 bool readUInt64DefVector(int numFiles, QList<quint64> &values, QList<bool> &defined); 0525 0526 Folder *folderItem(); 0527 bool readMainStreamsInfo(); 0528 bool readPackInfo(); 0529 bool readUnpackInfo(); 0530 bool readSubStreamsInfo(); 0531 QByteArray readAndDecodePackedStreams(bool readMainStreamInfo = true); 0532 0533 // Write 0534 void createItemsFromEntities(const KArchiveDirectory *, const QString &, QByteArray &); 0535 void writeByte(unsigned char b); 0536 void writeNumber(quint64 value); 0537 void writeBoolVector(const QList<bool> &boolVector); 0538 void writeUInt32(quint32 value); 0539 void writeUInt64(quint64 value); 0540 void writeHashDigests(const QList<bool> &digestsDefined, const QList<quint32> &digests); 0541 void writeAlignedBoolHeader(const QList<bool> &v, int numDefined, int type, unsigned itemSize); 0542 void writeUInt64DefVector(const QList<quint64> &v, const QList<bool> &defined, int type); 0543 void writeFolder(const Folder *folder); 0544 void writePackInfo(quint64 dataOffset, QList<quint64> &packedSizes, QList<bool> &packedCRCsDefined, QList<quint32> &packedCRCs); 0545 void writeUnpackInfo(const QList<Folder *> &folderItems); 0546 void writeSubStreamsInfo(const QList<quint64> &unpackSizes, const QList<bool> &digestsDefined, const QList<quint32> &digests); 0547 void writeHeader(quint64 &headerOffset); 0548 void writeSignature(); 0549 void writeStartHeader(const quint64 nextHeaderSize, const quint32 nextHeaderCRC, const quint64 nextHeaderOffset); 0550 QByteArray encodeStream(QList<quint64> &packSizes, QList<Folder *> &folds); 0551 }; 0552 0553 K7Zip::K7Zip(const QString &fileName) 0554 : KArchive(fileName) 0555 , d(new K7ZipPrivate(this)) 0556 { 0557 } 0558 0559 K7Zip::K7Zip(QIODevice *dev) 0560 : KArchive(dev) 0561 , d(new K7ZipPrivate(this)) 0562 { 0563 Q_ASSERT(dev); 0564 } 0565 0566 K7Zip::~K7Zip() 0567 { 0568 if (isOpen()) { 0569 close(); 0570 } 0571 0572 delete d; 0573 } 0574 0575 int K7Zip::K7ZipPrivate::readByte() 0576 { 0577 if (!buffer || pos + 1 > end) { 0578 return -1; 0579 } 0580 return buffer[pos++]; 0581 } 0582 0583 quint32 K7Zip::K7ZipPrivate::readUInt32() 0584 { 0585 if (!buffer || (quint64)(pos + 4) > end) { 0586 qCDebug(KArchiveLog) << "error size"; 0587 return 0; 0588 } 0589 0590 quint32 res = GetUi32(buffer, pos); 0591 pos += 4; 0592 return res; 0593 } 0594 0595 quint64 K7Zip::K7ZipPrivate::readUInt64() 0596 { 0597 if (!buffer || (quint64)(pos + 8) > end) { 0598 qCDebug(KArchiveLog) << "error size"; 0599 return 0; 0600 } 0601 0602 quint64 res = GetUi64(buffer, pos); 0603 pos += 8; 0604 return res; 0605 } 0606 0607 quint64 K7Zip::K7ZipPrivate::readNumber() 0608 { 0609 if (!buffer || (quint64)(pos + 8) > end) { 0610 return 0; 0611 } 0612 0613 unsigned char firstByte = buffer[pos++]; 0614 unsigned char mask = 0x80; 0615 quint64 value = 0; 0616 for (int i = 0; i < 8; i++) { 0617 if ((firstByte & mask) == 0) { 0618 quint64 highPart = firstByte & (mask - 1); 0619 value += (highPart << (i * 8)); 0620 return value; 0621 } 0622 value |= ((unsigned char)buffer[pos++] << (8 * i)); 0623 mask >>= 1; 0624 } 0625 return value; 0626 } 0627 0628 QString K7Zip::K7ZipPrivate::readString() 0629 { 0630 if (!buffer) { 0631 return QString(); 0632 } 0633 0634 const char *buf = buffer + pos; 0635 size_t rem = (end - pos) / 2 * 2; 0636 { 0637 size_t i; 0638 for (i = 0; i < rem; i += 2) { 0639 if (buf[i] == 0 && buf[i + 1] == 0) { 0640 break; 0641 } 0642 } 0643 if (i == rem) { 0644 qCDebug(KArchiveLog) << "read string error"; 0645 return QString(); 0646 } 0647 rem = i; 0648 } 0649 0650 int len = (int)(rem / 2); 0651 if (len < 0 || (size_t)len * 2 != rem) { 0652 qCDebug(KArchiveLog) << "read string unsupported"; 0653 return QString(); 0654 } 0655 0656 QString p; 0657 for (int i = 0; i < len; i++, buf += 2) { 0658 p += GetUi16(buf, 0); 0659 } 0660 0661 pos += rem + 2; 0662 return p; 0663 } 0664 0665 void K7Zip::K7ZipPrivate::skipData(int size) 0666 { 0667 if (!buffer || pos + size > end) { 0668 return; 0669 } 0670 pos += size; 0671 } 0672 0673 bool K7Zip::K7ZipPrivate::findAttribute(int attribute) 0674 { 0675 if (!buffer) { 0676 return false; 0677 } 0678 0679 for (;;) { 0680 int type = readByte(); 0681 if (type == attribute) { 0682 return true; 0683 } 0684 if (type == kEnd) { 0685 return false; 0686 } 0687 skipData(readNumber()); 0688 } 0689 } 0690 0691 void K7Zip::K7ZipPrivate::readBoolVector(int numItems, QList<bool> &v) 0692 { 0693 if (!buffer) { 0694 return; 0695 } 0696 0697 unsigned char b = 0; 0698 unsigned char mask = 0; 0699 for (int i = 0; i < numItems; i++) { 0700 if (mask == 0) { 0701 b = readByte(); 0702 mask = 0x80; 0703 } 0704 v.append((b & mask) != 0); 0705 mask >>= 1; 0706 } 0707 } 0708 0709 void K7Zip::K7ZipPrivate::readBoolVector2(int numItems, QList<bool> &v) 0710 { 0711 if (!buffer) { 0712 return; 0713 } 0714 0715 int allAreDefined = readByte(); 0716 if (allAreDefined == 0) { 0717 readBoolVector(numItems, v); 0718 return; 0719 } 0720 0721 for (int i = 0; i < numItems; i++) { 0722 v.append(true); 0723 } 0724 } 0725 0726 void K7Zip::K7ZipPrivate::readHashDigests(int numItems, QList<bool> &digestsDefined, QList<quint32> &digests) 0727 { 0728 if (!buffer) { 0729 return; 0730 } 0731 0732 readBoolVector2(numItems, digestsDefined); 0733 for (int i = 0; i < numItems; i++) { 0734 quint32 crc = 0; 0735 if (digestsDefined[i]) { 0736 crc = GetUi32(buffer, pos); 0737 pos += 4; 0738 } 0739 digests.append(crc); 0740 } 0741 } 0742 0743 Folder *K7Zip::K7ZipPrivate::folderItem() 0744 { 0745 if (!buffer) { 0746 return nullptr; 0747 } 0748 0749 Folder *folder = new Folder; 0750 int numCoders = readNumber(); 0751 0752 quint64 numInStreamsTotal = 0; 0753 quint64 numOutStreamsTotal = 0; 0754 for (int i = 0; i < numCoders; ++i) { 0755 // BYTE 0756 // { 0757 // 0:3 CodecIdSize 0758 // 4: Is Complex Coder 0759 // 5: There Are Attributes 0760 // 6: Reserved 0761 // 7: There are more alternative methods. (Not used 0762 // anymore, must be 0). 0763 // } 0764 unsigned char coderInfo = readByte(); 0765 int codecIdSize = (coderInfo & 0xF); 0766 if (codecIdSize > 8) { 0767 qCDebug(KArchiveLog) << "unsupported codec id size"; 0768 delete folder; 0769 return nullptr; 0770 } 0771 Folder::FolderInfo *info = new Folder::FolderInfo(); 0772 std::unique_ptr<unsigned char[]> codecID(new unsigned char[codecIdSize]); 0773 for (int i = 0; i < codecIdSize; ++i) { 0774 codecID[i] = readByte(); 0775 } 0776 0777 int id = 0; 0778 for (int j = 0; j < codecIdSize; j++) { 0779 id |= codecID[codecIdSize - 1 - j] << (8 * j); 0780 } 0781 info->methodID = id; 0782 0783 // if (Is Complex Coder) 0784 if ((coderInfo & 0x10) != 0) { 0785 info->numInStreams = readNumber(); 0786 info->numOutStreams = readNumber(); 0787 } else { 0788 info->numInStreams = 1; 0789 info->numOutStreams = 1; 0790 } 0791 0792 // if (There Are Attributes) 0793 if ((coderInfo & 0x20) != 0) { 0794 int propertiesSize = readNumber(); 0795 for (int i = 0; i < propertiesSize; ++i) { 0796 info->properties.append(readByte()); 0797 } 0798 } 0799 0800 if ((coderInfo & 0x80) != 0) { 0801 qCDebug(KArchiveLog) << "unsupported"; 0802 delete info; 0803 delete folder; 0804 return nullptr; 0805 } 0806 0807 numInStreamsTotal += info->numInStreams; 0808 numOutStreamsTotal += info->numOutStreams; 0809 folder->folderInfos.append(info); 0810 } 0811 0812 int numBindPairs = numOutStreamsTotal - 1; 0813 for (int i = 0; i < numBindPairs; i++) { 0814 folder->inIndexes.append(readNumber()); 0815 folder->outIndexes.append(readNumber()); 0816 } 0817 0818 int numPackedStreams = numInStreamsTotal - numBindPairs; 0819 if (numPackedStreams > 1) { 0820 for (int i = 0; i < numPackedStreams; ++i) { 0821 folder->packedStreams.append(readNumber()); 0822 } 0823 } else { 0824 if (numPackedStreams == 1) { 0825 for (quint64 i = 0; i < numInStreamsTotal; i++) { 0826 if (folder->findBindPairForInStream(i) < 0) { 0827 folder->packedStreams.append(i); 0828 break; 0829 } 0830 } 0831 if (folder->packedStreams.size() != 1) { 0832 delete folder; 0833 return nullptr; 0834 } 0835 } 0836 } 0837 return folder; 0838 } 0839 0840 bool K7Zip::K7ZipPrivate::readUInt64DefVector(int numFiles, QList<quint64> &values, QList<bool> &defined) 0841 { 0842 if (!buffer) { 0843 return false; 0844 } 0845 0846 readBoolVector2(numFiles, defined); 0847 0848 int external = readByte(); 0849 if (external != 0) { 0850 int dataIndex = readNumber(); 0851 if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) { 0852 qCDebug(KArchiveLog) << "wrong data index"; 0853 return false; 0854 } 0855 0856 // TODO : go to the new index 0857 } 0858 0859 for (int i = 0; i < numFiles; i++) { 0860 quint64 t = 0; 0861 if (defined[i]) { 0862 t = readUInt64(); 0863 } 0864 values.append(t); 0865 } 0866 return true; 0867 } 0868 0869 bool K7Zip::K7ZipPrivate::readPackInfo() 0870 { 0871 if (!buffer) { 0872 return false; 0873 } 0874 0875 packPos = readNumber(); 0876 numPackStreams = readNumber(); 0877 packSizes.clear(); 0878 0879 packCRCsDefined.clear(); 0880 packCRCs.clear(); 0881 0882 if (!findAttribute(kSize)) { 0883 qCDebug(KArchiveLog) << "kSize not found"; 0884 return false; 0885 } 0886 0887 for (quint64 i = 0; i < numPackStreams; ++i) { 0888 packSizes.append(readNumber()); 0889 } 0890 0891 for (;;) { 0892 int type = readByte(); 0893 if (type == kEnd) { 0894 break; 0895 } 0896 if (type == kCRC) { 0897 readHashDigests(numPackStreams, packCRCsDefined, packCRCs); 0898 continue; 0899 } 0900 skipData(readNumber()); 0901 } 0902 0903 if (packCRCs.isEmpty()) { 0904 for (quint64 i = 0; i < numPackStreams; ++i) { 0905 packCRCsDefined.append(false); 0906 packCRCs.append(0); 0907 } 0908 } 0909 return true; 0910 } 0911 0912 bool K7Zip::K7ZipPrivate::readUnpackInfo() 0913 { 0914 if (!buffer) { 0915 return false; 0916 } 0917 0918 if (!findAttribute(kFolder)) { 0919 qCDebug(KArchiveLog) << "kFolder not found"; 0920 return false; 0921 } 0922 0923 int numFolders = readNumber(); 0924 qDeleteAll(folders); 0925 folders.clear(); 0926 int external = readByte(); 0927 switch (external) { 0928 case 0: { 0929 for (int i = 0; i < numFolders; ++i) { 0930 folders.append(folderItem()); 0931 } 0932 break; 0933 } 0934 case 1: { 0935 int dataIndex = readNumber(); 0936 if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) { 0937 qCDebug(KArchiveLog) << "wrong data index"; 0938 } 0939 // TODO : go to the new index 0940 break; 0941 } 0942 default: 0943 qCDebug(KArchiveLog) << "external error"; 0944 return false; 0945 } 0946 0947 if (!findAttribute(kCodersUnpackSize)) { 0948 qCDebug(KArchiveLog) << "kCodersUnpackSize not found"; 0949 return false; 0950 } 0951 0952 for (int i = 0; i < numFolders; ++i) { 0953 Folder *folder = folders.at(i); 0954 int numOutStreams = folder->getNumOutStreams(); 0955 for (int j = 0; j < numOutStreams; ++j) { 0956 folder->unpackSizes.append(readNumber()); 0957 } 0958 } 0959 0960 for (;;) { 0961 int type = readByte(); 0962 if (type == kEnd) { 0963 break; 0964 } 0965 if (type == kCRC) { 0966 QList<bool> crcsDefined; 0967 QList<quint32> crcs; 0968 readHashDigests(numFolders, crcsDefined, crcs); 0969 for (int i = 0; i < numFolders; i++) { 0970 Folder *folder = folders.at(i); 0971 folder->unpackCRCDefined = crcsDefined[i]; 0972 folder->unpackCRC = crcs[i]; 0973 } 0974 continue; 0975 } 0976 skipData(readNumber()); 0977 } 0978 return true; 0979 } 0980 0981 bool K7Zip::K7ZipPrivate::readSubStreamsInfo() 0982 { 0983 if (!buffer) { 0984 return false; 0985 } 0986 0987 numUnpackStreamsInFolders.clear(); 0988 0989 int type; 0990 for (;;) { 0991 type = readByte(); 0992 if (type == kNumUnpackStream) { 0993 for (int i = 0; i < folders.size(); i++) { 0994 numUnpackStreamsInFolders.append(readNumber()); 0995 } 0996 continue; 0997 } 0998 if (type == kCRC || type == kSize) { 0999 break; 1000 } 1001 if (type == kEnd) { 1002 break; 1003 } 1004 skipData(readNumber()); 1005 } 1006 1007 if (numUnpackStreamsInFolders.isEmpty()) { 1008 for (int i = 0; i < folders.size(); i++) { 1009 numUnpackStreamsInFolders.append(1); 1010 } 1011 } 1012 1013 for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) { 1014 quint64 numSubstreams = numUnpackStreamsInFolders.at(i); 1015 if (numSubstreams == 0) { 1016 continue; 1017 } 1018 quint64 sum = 0; 1019 for (quint64 j = 1; j < numSubstreams; j++) { 1020 if (type == kSize) { 1021 int size = readNumber(); 1022 unpackSizes.append(size); 1023 sum += size; 1024 } 1025 } 1026 unpackSizes.append(folders.at(i)->getUnpackSize() - sum); 1027 } 1028 1029 if (type == kSize) { 1030 type = readByte(); 1031 } 1032 1033 int numDigests = 0; 1034 int numDigestsTotal = 0; 1035 for (int i = 0; i < folders.size(); i++) { 1036 quint64 numSubstreams = numUnpackStreamsInFolders.at(i); 1037 if (numSubstreams != 1 || !folders.at(i)->unpackCRCDefined) { 1038 numDigests += numSubstreams; 1039 } 1040 numDigestsTotal += numSubstreams; 1041 } 1042 1043 for (;;) { 1044 if (type == kCRC) { 1045 QList<bool> digestsDefined2; 1046 QList<quint32> digests2; 1047 readHashDigests(numDigests, digestsDefined2, digests2); 1048 int digestIndex = 0; 1049 for (int i = 0; i < folders.size(); i++) { 1050 quint64 numSubstreams = numUnpackStreamsInFolders.at(i); 1051 const Folder *folder = folders.at(i); 1052 if (numSubstreams == 1 && folder->unpackCRCDefined) { 1053 digestsDefined.append(true); 1054 digests.append(folder->unpackCRC); 1055 } else { 1056 for (quint64 j = 0; j < numSubstreams; j++, digestIndex++) { 1057 digestsDefined.append(digestsDefined2[digestIndex]); 1058 digests.append(digests2[digestIndex]); 1059 } 1060 } 1061 } 1062 } else if (type == kEnd) { 1063 if (digestsDefined.isEmpty()) { 1064 for (int i = 0; i < numDigestsTotal; i++) { 1065 digestsDefined.append(false); 1066 digests.append(0); 1067 } 1068 } 1069 1070 break; 1071 } else { 1072 skipData(readNumber()); 1073 } 1074 1075 type = readByte(); 1076 } 1077 return true; 1078 } 1079 1080 #define TICKSPERSEC 10000000 1081 #define TICKSPERMSEC 10000 1082 #define SECSPERDAY 86400 1083 #define SECSPERHOUR 3600 1084 #define SECSPERMIN 60 1085 #define EPOCHWEEKDAY 1 /* Jan 1, 1601 was Monday */ 1086 #define DAYSPERWEEK 7 1087 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97) 1088 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1) 1089 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC) 1090 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (unsigned long long)SECSPERDAY) 1091 1092 static uint toTimeT(const long long liTime) 1093 { 1094 long long time = liTime / TICKSPERSEC; 1095 1096 /* The native version of RtlTimeToTimeFields does not take leap seconds 1097 * into account */ 1098 1099 /* Split the time into days and seconds within the day */ 1100 long int days = time / SECSPERDAY; 1101 int secondsInDay = time % SECSPERDAY; 1102 1103 /* compute time of day */ 1104 short hour = (short)(secondsInDay / SECSPERHOUR); 1105 secondsInDay = secondsInDay % SECSPERHOUR; 1106 short minute = (short)(secondsInDay / SECSPERMIN); 1107 short second = (short)(secondsInDay % SECSPERMIN); 1108 1109 /* compute year, month and day of month. */ 1110 long int cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4; 1111 days += 28188 + cleaps; 1112 long int years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM); 1113 long int yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4; 1114 long int months = (64 * yearday) / 1959; 1115 /* the result is based on a year starting on March. 1116 * To convert take 12 from January and February and 1117 * increase the year by one. */ 1118 1119 short month; 1120 short year; 1121 if (months < 14) { 1122 month = (short)(months - 1); 1123 year = (short)(years + 1524); 1124 } else { 1125 month = (short)(months - 13); 1126 year = (short)(years + 1525); 1127 } 1128 /* calculation of day of month is based on the wonderful 1129 * sequence of INT( n * 30.6): it reproduces the· 1130 * 31-30-31-30-31-31 month lengths exactly for small n's */ 1131 short day = (short)(yearday - (1959 * months) / 64); 1132 1133 QDateTime t(QDate(year, month, day), QTime(hour, minute, second)); 1134 t.setTimeZone(QTimeZone::utc()); 1135 return t.toSecsSinceEpoch(); 1136 } 1137 1138 long long rtlSecondsSince1970ToSpecTime(quint32 seconds) 1139 { 1140 long long secs = seconds * (long long)TICKSPERSEC + TICKS_1601_TO_1970; 1141 return secs; 1142 } 1143 1144 bool K7Zip::K7ZipPrivate::readMainStreamsInfo() 1145 { 1146 if (!buffer) { 1147 return false; 1148 } 1149 1150 quint32 type; 1151 for (;;) { 1152 type = readByte(); 1153 if (type > ((quint32)1 << 30)) { 1154 qCDebug(KArchiveLog) << "type error"; 1155 return false; 1156 } 1157 switch (type) { 1158 case kEnd: 1159 return true; 1160 case kPackInfo: { 1161 if (!readPackInfo()) { 1162 qCDebug(KArchiveLog) << "error during read pack information"; 1163 return false; 1164 } 1165 break; 1166 } 1167 case kUnpackInfo: { 1168 if (!readUnpackInfo()) { 1169 qCDebug(KArchiveLog) << "error during read pack information"; 1170 return false; 1171 } 1172 break; 1173 } 1174 case kSubStreamsInfo: { 1175 if (!readSubStreamsInfo()) { 1176 qCDebug(KArchiveLog) << "error during read substreams information"; 1177 return false; 1178 } 1179 break; 1180 } 1181 default: 1182 qCDebug(KArchiveLog) << "Wrong type"; 1183 return false; 1184 } 1185 } 1186 1187 qCDebug(KArchiveLog) << "should not reach"; 1188 return false; 1189 } 1190 1191 static bool getInStream(const Folder *folder, quint32 streamIndex, int &seqInStream, quint32 &coderIndex) 1192 { 1193 for (int i = 0; i < folder->packedStreams.size(); i++) { 1194 if (folder->packedStreams[i] == streamIndex) { 1195 seqInStream = i; 1196 return true; 1197 } 1198 } 1199 1200 int binderIndex = folder->findBindPairForInStream(streamIndex); 1201 if (binderIndex < 0) { 1202 return false; 1203 } 1204 1205 quint32 coderStreamIndex; 1206 folder->findOutStream(folder->outIndexes[binderIndex], coderIndex, coderStreamIndex); 1207 1208 quint32 startIndex = folder->getCoderInStreamIndex(coderIndex); 1209 1210 if (folder->folderInfos[coderIndex]->numInStreams > 1) { 1211 return false; 1212 } 1213 1214 for (int i = 0; i < (int)folder->folderInfos[coderIndex]->numInStreams; i++) { 1215 getInStream(folder, startIndex + i, seqInStream, coderIndex); 1216 } 1217 1218 return true; 1219 } 1220 1221 static bool getOutStream(const Folder *folder, quint32 streamIndex, int &seqOutStream) 1222 { 1223 QList<quint32> outStreams; 1224 quint32 outStreamIndex = 0; 1225 for (int i = 0; i < folder->folderInfos.size(); i++) { 1226 const Folder::FolderInfo *coderInfo = folder->folderInfos.at(i); 1227 1228 for (int j = 0; j < coderInfo->numOutStreams; j++, outStreamIndex++) { 1229 if (folder->findBindPairForOutStream(outStreamIndex) < 0) { 1230 outStreams.append(outStreamIndex); 1231 } 1232 } 1233 } 1234 1235 for (int i = 0; i < outStreams.size(); i++) { 1236 if (outStreams[i] == streamIndex) { 1237 seqOutStream = i; 1238 return true; 1239 } 1240 } 1241 1242 int binderIndex = folder->findBindPairForOutStream(streamIndex); 1243 if (binderIndex < 0) { 1244 return false; 1245 } 1246 1247 quint32 coderIndex; 1248 quint32 coderStreamIndex; 1249 folder->findInStream(folder->inIndexes[binderIndex], coderIndex, coderStreamIndex); 1250 1251 quint32 startIndex = folder->getCoderOutStreamIndex(coderIndex); 1252 1253 if (folder->folderInfos[coderIndex]->numOutStreams > 1) { 1254 return false; 1255 } 1256 1257 for (int i = 0; i < (int)folder->folderInfos[coderIndex]->numOutStreams; i++) { 1258 getOutStream(folder, startIndex + i, seqOutStream); 1259 } 1260 1261 return true; 1262 } 1263 1264 const int kNumTopBits = 24; 1265 const quint32 kTopValue = (1 << kNumTopBits); 1266 1267 class RangeDecoder 1268 { 1269 int pos; 1270 1271 public: 1272 QByteArray stream; 1273 quint32 range; 1274 quint32 code; 1275 1276 RangeDecoder(const QByteArray &s) 1277 : pos(0) 1278 , stream(s) 1279 , range(0xFFFFFFFF) 1280 , code(0) 1281 { 1282 for (int i = 0; i < 5; i++) { 1283 code = (code << 8) | readByte(); 1284 } 1285 } 1286 1287 unsigned char readByte() 1288 { 1289 return stream[pos++]; 1290 } 1291 1292 void normalize() 1293 { 1294 while (range < kTopValue) { 1295 code = (code << 8) | readByte(); 1296 range <<= 8; 1297 } 1298 } 1299 1300 quint32 getThreshold(quint32 total) 1301 { 1302 return (code) / (range /= total); 1303 } 1304 1305 void decode(quint32 start, quint32 size) 1306 { 1307 code -= start * range; 1308 range *= size; 1309 normalize(); 1310 } 1311 1312 quint32 decodeDirectBits(int numTotalBits) 1313 { 1314 quint32 r = range; 1315 quint32 c = code; 1316 quint32 result = 0; 1317 for (int i = numTotalBits; i != 0; i--) { 1318 r >>= 1; 1319 quint32 t = (c - r) >> 31; 1320 c -= r & (t - 1); 1321 result = (result << 1) | (1 - t); 1322 1323 if (r < kTopValue) { 1324 c = (c << 8) | readByte(); 1325 r <<= 8; 1326 } 1327 } 1328 range = r; 1329 code = c; 1330 return result; 1331 } 1332 1333 quint32 DecodeBit(quint32 size0, quint32 numTotalBits) 1334 { 1335 quint32 newBound = (range >> numTotalBits) * size0; 1336 quint32 symbol; 1337 if (code < newBound) { 1338 symbol = 0; 1339 range = newBound; 1340 } else { 1341 symbol = 1; 1342 code -= newBound; 1343 range -= newBound; 1344 } 1345 normalize(); 1346 return symbol; 1347 } 1348 }; 1349 1350 const int kNumBitModelTotalBits = 11; 1351 const quint32 kBitModelTotal = (1 << kNumBitModelTotalBits); 1352 1353 template<int numMoveBits> 1354 class CBitModel 1355 { 1356 public: 1357 quint32 prob; 1358 void updateModel(quint32 symbol) 1359 { 1360 if (symbol == 0) { 1361 prob += (kBitModelTotal - prob) >> numMoveBits; 1362 } else { 1363 prob -= (prob) >> numMoveBits; 1364 } 1365 } 1366 1367 void init() 1368 { 1369 prob = kBitModelTotal / 2; 1370 } 1371 }; 1372 1373 template<int numMoveBits> 1374 class CBitDecoder : public CBitModel<numMoveBits> 1375 { 1376 public: 1377 quint32 decode(RangeDecoder *decoder) 1378 { 1379 quint32 newBound = (decoder->range >> kNumBitModelTotalBits) * this->prob; 1380 if (decoder->code < newBound) { 1381 decoder->range = newBound; 1382 this->prob += (kBitModelTotal - this->prob) >> numMoveBits; 1383 if (decoder->range < kTopValue) { 1384 decoder->code = (decoder->code << 8) | decoder->readByte(); 1385 decoder->range <<= 8; 1386 } 1387 return 0; 1388 } else { 1389 decoder->range -= newBound; 1390 decoder->code -= newBound; 1391 this->prob -= (this->prob) >> numMoveBits; 1392 if (decoder->range < kTopValue) { 1393 decoder->code = (decoder->code << 8) | decoder->readByte(); 1394 decoder->range <<= 8; 1395 } 1396 return 1; 1397 } 1398 } 1399 }; 1400 1401 inline bool isJcc(unsigned char b0, unsigned char b1) 1402 { 1403 return (b0 == 0x0F && (b1 & 0xF0) == 0x80); 1404 } 1405 inline bool isJ(unsigned char b0, unsigned char b1) 1406 { 1407 return ((b1 & 0xFE) == 0xE8 || isJcc(b0, b1)); 1408 } 1409 inline unsigned getIndex(unsigned char b0, unsigned char b1) 1410 { 1411 return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257)); 1412 } 1413 1414 const int kNumMoveBits = 5; 1415 1416 static QByteArray decodeBCJ2(const QByteArray &mainStream, const QByteArray &callStream, const QByteArray &jumpStream, const QByteArray &rangeBuffer) 1417 { 1418 unsigned char prevByte = 0; 1419 QByteArray outStream; 1420 int mainStreamPos = 0; 1421 int callStreamPos = 0; 1422 int jumpStreamPos = 0; 1423 1424 RangeDecoder rangeDecoder(rangeBuffer); 1425 1426 QList<CBitDecoder<kNumMoveBits>> statusDecoder(256 + 2); 1427 1428 for (int i = 0; i < 256 + 2; i++) { 1429 statusDecoder[i].init(); 1430 } 1431 1432 for (;;) { 1433 quint32 i; 1434 unsigned char b = 0; 1435 const quint32 kBurstSize = (1 << 18); 1436 for (i = 0; i < kBurstSize; i++) { 1437 if (mainStreamPos == mainStream.size()) { 1438 return outStream; 1439 } 1440 1441 b = mainStream[mainStreamPos++]; 1442 outStream.append(b); 1443 1444 if (isJ(prevByte, b)) { 1445 break; 1446 } 1447 prevByte = b; 1448 } 1449 1450 if (i == kBurstSize) { 1451 continue; 1452 } 1453 1454 unsigned index = getIndex(prevByte, b); 1455 if (statusDecoder[index].decode(&rangeDecoder) == 1) { 1456 if (b == 0xE8) { 1457 if (callStreamPos + 4 > callStream.size()) { 1458 return QByteArray(); 1459 } 1460 } else { 1461 if (jumpStreamPos + 4 > jumpStream.size()) { 1462 return QByteArray(); 1463 } 1464 } 1465 quint32 src = 0; 1466 for (int i = 0; i < 4; i++) { 1467 unsigned char b0; 1468 if (b == 0xE8) { 1469 b0 = callStream[callStreamPos++]; 1470 } else { 1471 b0 = jumpStream[jumpStreamPos++]; 1472 } 1473 src <<= 8; 1474 src |= ((quint32)b0); 1475 } 1476 1477 quint32 dest = src - (quint32(outStream.size()) + 4); 1478 outStream.append((unsigned char)(dest)); 1479 outStream.append((unsigned char)(dest >> 8)); 1480 outStream.append((unsigned char)(dest >> 16)); 1481 outStream.append((unsigned char)(dest >> 24)); 1482 prevByte = (unsigned char)(dest >> 24); 1483 } else { 1484 prevByte = b; 1485 } 1486 } 1487 } 1488 1489 QByteArray K7Zip::K7ZipPrivate::readAndDecodePackedStreams(bool readMainStreamInfo) 1490 { 1491 if (!buffer) { 1492 return QByteArray(); 1493 } 1494 1495 if (readMainStreamInfo) { 1496 readMainStreamsInfo(); 1497 } 1498 1499 QByteArray inflatedData; 1500 1501 quint64 startPos = 32 + packPos; 1502 for (int i = 0; i < folders.size(); i++) { 1503 const Folder *folder = folders.at(i); 1504 quint64 unpackSize64 = folder->getUnpackSize(); 1505 size_t unpackSize = (size_t)unpackSize64; 1506 if (unpackSize != unpackSize64) { 1507 qCDebug(KArchiveLog) << "unsupported"; 1508 return inflatedData; 1509 } 1510 1511 // Find main coder 1512 quint32 mainCoderIndex = 0; 1513 QList<int> outStreamIndexed; 1514 int outStreamIndex = 0; 1515 for (int j = 0; j < folder->folderInfos.size(); j++) { 1516 const Folder::FolderInfo *info = folder->folderInfos[j]; 1517 for (int k = 0; k < info->numOutStreams; k++, outStreamIndex++) { 1518 if (folder->findBindPairForOutStream(outStreamIndex) < 0) { 1519 outStreamIndexed.append(outStreamIndex); 1520 break; 1521 } 1522 } 1523 } 1524 1525 quint32 temp = 0; 1526 if (!outStreamIndexed.isEmpty()) { 1527 folder->findOutStream(outStreamIndexed[0], mainCoderIndex, temp); 1528 } 1529 1530 quint32 startInIndex = folder->getCoderInStreamIndex(mainCoderIndex); 1531 quint32 startOutIndex = folder->getCoderOutStreamIndex(mainCoderIndex); 1532 1533 Folder::FolderInfo *mainCoder = folder->folderInfos[mainCoderIndex]; 1534 1535 QList<int> seqInStreams; 1536 QList<quint32> coderIndexes; 1537 seqInStreams.reserve(mainCoder->numInStreams); 1538 coderIndexes.reserve(mainCoder->numInStreams); 1539 for (int j = 0; j < (int)mainCoder->numInStreams; j++) { 1540 int seqInStream; 1541 quint32 coderIndex; 1542 getInStream(folder, startInIndex + j, seqInStream, coderIndex); 1543 seqInStreams.append(seqInStream); 1544 coderIndexes.append(coderIndex); 1545 } 1546 1547 QList<int> seqOutStreams; 1548 seqOutStreams.reserve(mainCoder->numOutStreams); 1549 for (int j = 0; j < (int)mainCoder->numOutStreams; j++) { 1550 int seqOutStream; 1551 getOutStream(folder, startOutIndex + j, seqOutStream); 1552 seqOutStreams.append(seqOutStream); 1553 } 1554 1555 QList<QByteArray> datas; 1556 for (int j = 0; j < (int)mainCoder->numInStreams; j++) { 1557 int size = packSizes[j + i]; 1558 std::unique_ptr<char[]> encodedBuffer(new char[size]); 1559 QIODevice *dev = q->device(); 1560 dev->seek(startPos); 1561 quint64 n = dev->read(encodedBuffer.get(), size); 1562 if (n != (quint64)size) { 1563 qCDebug(KArchiveLog) << "Failed read next size, should read " << size << ", read " << n; 1564 return inflatedData; 1565 } 1566 QByteArray deflatedData(encodedBuffer.get(), size); 1567 datas.append(deflatedData); 1568 startPos += size; 1569 pos += size; 1570 headerSize += size; 1571 } 1572 1573 QList<QByteArray> inflatedDatas; 1574 QByteArray deflatedData; 1575 for (int j = 0; j < seqInStreams.size(); ++j) { 1576 Folder::FolderInfo *coder = nullptr; 1577 if ((quint32)j != mainCoderIndex) { 1578 coder = folder->folderInfos[coderIndexes[j]]; 1579 } else { 1580 coder = folder->folderInfos[mainCoderIndex]; 1581 } 1582 1583 deflatedData = datas[seqInStreams[j]]; 1584 1585 KFilterBase *filter = nullptr; 1586 1587 switch (coder->methodID) { 1588 case k_LZMA: 1589 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz); 1590 if (!filter) { 1591 qCDebug(KArchiveLog) << "filter not found"; 1592 return QByteArray(); 1593 } 1594 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::LZMA, coder->properties); 1595 break; 1596 case k_LZMA2: 1597 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz); 1598 if (!filter) { 1599 qCDebug(KArchiveLog) << "filter not found"; 1600 return QByteArray(); 1601 } 1602 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::LZMA2, coder->properties); 1603 break; 1604 case k_PPMD: { 1605 /*if (coder->properties.size() == 5) { 1606 //Byte order = *(const Byte *)coder.Props; 1607 qint32 dicSize = ((unsigned char)coder->properties[1] | 1608 (((unsigned char)coder->properties[2]) << 8) | 1609 (((unsigned char)coder->properties[3]) << 16) | 1610 (((unsigned char)coder->properties[4]) << 24)); 1611 }*/ 1612 break; 1613 } 1614 case k_AES: 1615 if (coder->properties.size() >= 1) { 1616 // const Byte *data = (const Byte *)coder.Props; 1617 // Byte firstByte = *data++; 1618 // UInt32 numCyclesPower = firstByte & 0x3F; 1619 } 1620 break; 1621 case k_BCJ: 1622 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz); 1623 if (!filter) { 1624 qCDebug(KArchiveLog) << "filter not found"; 1625 return QByteArray(); 1626 } 1627 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::BCJ, coder->properties); 1628 break; 1629 case k_BCJ2: { 1630 QByteArray bcj2 = decodeBCJ2(inflatedDatas[0], inflatedDatas[1], inflatedDatas[2], deflatedData); 1631 inflatedDatas.clear(); 1632 inflatedDatas.append(bcj2); 1633 break; 1634 } 1635 case k_BZip2: 1636 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::BZip2); 1637 if (!filter) { 1638 qCDebug(KArchiveLog) << "filter not found"; 1639 return QByteArray(); 1640 } 1641 filter->init(QIODevice::ReadOnly); 1642 break; 1643 } 1644 1645 if (coder->methodID == k_BCJ2) { 1646 continue; 1647 } 1648 1649 if (!filter) { 1650 return QByteArray(); 1651 } 1652 1653 filter->setInBuffer(deflatedData.data(), deflatedData.size()); 1654 1655 QByteArray outBuffer; 1656 // reserve memory 1657 outBuffer.resize(unpackSize); 1658 1659 KFilterBase::Result result = KFilterBase::Ok; 1660 QByteArray inflatedDataTmp; 1661 while (result != KFilterBase::End && result != KFilterBase::Error && !filter->inBufferEmpty()) { 1662 filter->setOutBuffer(outBuffer.data(), outBuffer.size()); 1663 result = filter->uncompress(); 1664 if (result == KFilterBase::Error) { 1665 qCDebug(KArchiveLog) << " decode error"; 1666 filter->terminate(); 1667 delete filter; 1668 return QByteArray(); 1669 } 1670 int uncompressedBytes = outBuffer.size() - filter->outBufferAvailable(); 1671 1672 // append the uncompressed data to inflate buffer 1673 inflatedDataTmp.append(outBuffer.data(), uncompressedBytes); 1674 1675 if (result == KFilterBase::End) { 1676 // qCDebug(KArchiveLog) << "Finished unpacking"; 1677 break; // Finished. 1678 } 1679 } 1680 1681 if (result != KFilterBase::End && !filter->inBufferEmpty()) { 1682 qCDebug(KArchiveLog) << "decode failed result" << result; 1683 filter->terminate(); 1684 delete filter; 1685 return QByteArray(); 1686 } 1687 1688 filter->terminate(); 1689 delete filter; 1690 1691 inflatedDatas.append(inflatedDataTmp); 1692 } 1693 1694 QByteArray inflated; 1695 for (const QByteArray &data : std::as_const(inflatedDatas)) { 1696 inflated.append(data); 1697 } 1698 1699 inflatedDatas.clear(); 1700 1701 if (folder->unpackCRCDefined) { 1702 if ((size_t)inflated.size() < unpackSize) { 1703 qCDebug(KArchiveLog) << "wrong crc size data"; 1704 return QByteArray(); 1705 } 1706 quint32 crc = crc32(0, (Bytef *)(inflated.data()), unpackSize); 1707 if (crc != folder->unpackCRC) { 1708 qCDebug(KArchiveLog) << "wrong crc"; 1709 return QByteArray(); 1710 } 1711 } 1712 1713 inflatedData.append(inflated); 1714 } 1715 1716 return inflatedData; 1717 } 1718 1719 ///////////////// Write //////////////////// 1720 1721 void K7Zip::K7ZipPrivate::createItemsFromEntities(const KArchiveDirectory *dir, const QString &path, QByteArray &data) 1722 { 1723 const QStringList l = dir->entries(); 1724 QStringList::ConstIterator it = l.begin(); 1725 for (; it != l.end(); ++it) { 1726 const KArchiveEntry *entry = dir->entry((*it)); 1727 1728 FileInfo *fileInfo = new FileInfo; 1729 fileInfo->attribDefined = true; 1730 1731 fileInfo->path = path + entry->name(); 1732 mTimesDefined.append(true); 1733 mTimes.append(rtlSecondsSince1970ToSpecTime(entry->date().toSecsSinceEpoch())); 1734 1735 if (entry->isFile()) { 1736 const K7ZipFileEntry *fileEntry = static_cast<const K7ZipFileEntry *>(entry); 1737 1738 fileInfo->attributes = FILE_ATTRIBUTE_ARCHIVE; 1739 fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->permissions() & 0xFFFF) << 16); 1740 fileInfo->size = fileEntry->size(); 1741 QString symLink = fileEntry->symLinkTarget(); 1742 if (fileInfo->size > 0) { 1743 fileInfo->hasStream = true; 1744 data.append(outData.mid(fileEntry->position(), fileEntry->size())); 1745 unpackSizes.append(fileInfo->size); 1746 } else if (!symLink.isEmpty()) { 1747 fileInfo->hasStream = true; 1748 data.append(symLink.toUtf8()); 1749 unpackSizes.append(symLink.size()); 1750 } 1751 fileInfos.append(fileInfo); 1752 } else if (entry->isDirectory()) { 1753 fileInfo->attributes = FILE_ATTRIBUTE_DIRECTORY; 1754 fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->permissions() & 0xFFFF) << 16); 1755 fileInfo->isDir = true; 1756 fileInfos.append(fileInfo); 1757 createItemsFromEntities((KArchiveDirectory *)entry, path + (*it) + QLatin1Char('/'), data); 1758 } 1759 } 1760 } 1761 1762 void K7Zip::K7ZipPrivate::writeByte(unsigned char b) 1763 { 1764 header.append(b); 1765 countSize++; 1766 } 1767 1768 void K7Zip::K7ZipPrivate::writeNumber(quint64 value) 1769 { 1770 int firstByte = 0; 1771 short mask = 0x80; 1772 int i; 1773 for (i = 0; i < 8; i++) { 1774 if (value < ((quint64(1) << (7 * (i + 1))))) { 1775 firstByte |= (int)(value >> (8 * i)); 1776 break; 1777 } 1778 firstByte |= mask; 1779 mask >>= 1; 1780 } 1781 writeByte(firstByte); 1782 for (; i > 0; i--) { 1783 writeByte((int)value); 1784 value >>= 8; 1785 } 1786 } 1787 1788 void K7Zip::K7ZipPrivate::writeBoolVector(const QList<bool> &boolVector) 1789 { 1790 int b = 0; 1791 short mask = 0x80; 1792 for (int i = 0; i < boolVector.size(); i++) { 1793 if (boolVector[i]) { 1794 b |= mask; 1795 } 1796 mask >>= 1; 1797 if (mask == 0) { 1798 writeByte(b); 1799 mask = 0x80; 1800 b = 0; 1801 } 1802 } 1803 if (mask != 0x80) { 1804 writeByte(b); 1805 } 1806 } 1807 1808 void K7Zip::K7ZipPrivate::writeUInt32(quint32 value) 1809 { 1810 for (int i = 0; i < 4; i++) { 1811 writeByte((unsigned char)value); 1812 value >>= 8; 1813 } 1814 } 1815 1816 void K7Zip::K7ZipPrivate::writeUInt64(quint64 value) 1817 { 1818 for (int i = 0; i < 8; i++) { 1819 writeByte((unsigned char)value); 1820 value >>= 8; 1821 } 1822 } 1823 1824 void K7Zip::K7ZipPrivate::writeAlignedBoolHeader(const QList<bool> &v, int numDefined, int type, unsigned itemSize) 1825 { 1826 const unsigned bvSize = (numDefined == v.size()) ? 0 : ((unsigned)v.size() + 7) / 8; 1827 const quint64 dataSize = (quint64)numDefined * itemSize + bvSize + 2; 1828 // SkipAlign(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSize); 1829 1830 writeByte(type); 1831 writeNumber(dataSize); 1832 if (numDefined == v.size()) { 1833 writeByte(1); 1834 } else { 1835 writeByte(0); 1836 writeBoolVector(v); 1837 } 1838 writeByte(0); 1839 } 1840 1841 void K7Zip::K7ZipPrivate::writeUInt64DefVector(const QList<quint64> &v, const QList<bool> &defined, int type) 1842 { 1843 int numDefined = 0; 1844 1845 for (int i = 0; i < defined.size(); i++) { 1846 if (defined[i]) { 1847 numDefined++; 1848 } 1849 } 1850 1851 if (numDefined == 0) { 1852 return; 1853 } 1854 1855 writeAlignedBoolHeader(defined, numDefined, type, 8); 1856 1857 for (int i = 0; i < defined.size(); i++) { 1858 if (defined[i]) { 1859 writeUInt64(v[i]); 1860 } 1861 } 1862 } 1863 1864 void K7Zip::K7ZipPrivate::writeHashDigests(const QList<bool> &digestsDefined, const QList<quint32> &digests) 1865 { 1866 int numDefined = 0; 1867 int i; 1868 for (i = 0; i < digestsDefined.size(); i++) { 1869 if (digestsDefined[i]) { 1870 numDefined++; 1871 } 1872 } 1873 1874 if (numDefined == 0) { 1875 return; 1876 } 1877 1878 writeByte(kCRC); 1879 if (numDefined == digestsDefined.size()) { 1880 writeByte(1); 1881 } else { 1882 writeByte(0); 1883 writeBoolVector(digestsDefined); 1884 } 1885 1886 for (i = 0; i < digests.size(); i++) { 1887 if (digestsDefined[i]) { 1888 writeUInt32(digests[i]); 1889 } 1890 } 1891 } 1892 1893 void K7Zip::K7ZipPrivate::writePackInfo(quint64 dataOffset, QList<quint64> &packedSizes, QList<bool> &packedCRCsDefined, QList<quint32> &packedCRCs) 1894 { 1895 if (packedSizes.isEmpty()) { 1896 return; 1897 } 1898 writeByte(kPackInfo); 1899 writeNumber(dataOffset); 1900 writeNumber(packedSizes.size()); 1901 writeByte(kSize); 1902 1903 for (int i = 0; i < packedSizes.size(); i++) { 1904 writeNumber(packedSizes[i]); 1905 } 1906 1907 writeHashDigests(packedCRCsDefined, packedCRCs); 1908 1909 writeByte(kEnd); 1910 } 1911 1912 void K7Zip::K7ZipPrivate::writeFolder(const Folder *folder) 1913 { 1914 writeNumber(folder->folderInfos.size()); 1915 for (int i = 0; i < folder->folderInfos.size(); i++) { 1916 const Folder::FolderInfo *info = folder->folderInfos.at(i); 1917 { 1918 size_t propsSize = info->properties.size(); 1919 1920 quint64 id = info->methodID; 1921 size_t idSize; 1922 for (idSize = 1; idSize < sizeof(id); idSize++) { 1923 if ((id >> (8 * idSize)) == 0) { 1924 break; 1925 } 1926 } 1927 1928 int longID[15]; 1929 for (int t = idSize - 1; t >= 0; t--, id >>= 8) { 1930 longID[t] = (int)(id & 0xFF); 1931 } 1932 1933 int b; 1934 b = (int)(idSize & 0xF); 1935 bool isComplex = !info->isSimpleCoder(); 1936 b |= (isComplex ? 0x10 : 0); 1937 b |= ((propsSize != 0) ? 0x20 : 0); 1938 1939 writeByte(b); 1940 for (size_t j = 0; j < idSize; ++j) { 1941 writeByte(longID[j]); 1942 } 1943 1944 if (isComplex) { 1945 writeNumber(info->numInStreams); 1946 writeNumber(info->numOutStreams); 1947 } 1948 1949 if (propsSize == 0) { 1950 continue; 1951 } 1952 1953 writeNumber(propsSize); 1954 for (size_t j = 0; j < propsSize; ++j) { 1955 writeByte(info->properties[j]); 1956 } 1957 } 1958 } 1959 1960 for (int i = 0; i < folder->inIndexes.size(); i++) { 1961 writeNumber(folder->inIndexes[i]); 1962 writeNumber(folder->outIndexes[i]); 1963 } 1964 1965 if (folder->packedStreams.size() > 1) { 1966 for (int i = 0; i < folder->packedStreams.size(); i++) { 1967 writeNumber(folder->packedStreams[i]); 1968 } 1969 } 1970 } 1971 1972 void K7Zip::K7ZipPrivate::writeUnpackInfo(const QList<Folder *> &folderItems) 1973 { 1974 if (folderItems.isEmpty()) { 1975 return; 1976 } 1977 1978 writeByte(kUnpackInfo); 1979 1980 writeByte(kFolder); 1981 writeNumber(folderItems.size()); 1982 { 1983 writeByte(0); 1984 for (int i = 0; i < folderItems.size(); i++) { 1985 writeFolder(folderItems[i]); 1986 } 1987 } 1988 1989 writeByte(kCodersUnpackSize); 1990 int i; 1991 for (i = 0; i < folderItems.size(); i++) { 1992 const Folder *folder = folderItems[i]; 1993 for (int j = 0; j < folder->unpackSizes.size(); j++) { 1994 writeNumber(folder->unpackSizes.at(j)); 1995 } 1996 } 1997 1998 QList<bool> unpackCRCsDefined; 1999 QList<quint32> unpackCRCs; 2000 unpackCRCsDefined.reserve(folderItems.size()); 2001 unpackCRCs.reserve(folderItems.size()); 2002 for (i = 0; i < folderItems.size(); i++) { 2003 const Folder *folder = folderItems[i]; 2004 unpackCRCsDefined.append(folder->unpackCRCDefined); 2005 unpackCRCs.append(folder->unpackCRC); 2006 } 2007 writeHashDigests(unpackCRCsDefined, unpackCRCs); 2008 2009 writeByte(kEnd); 2010 } 2011 2012 void K7Zip::K7ZipPrivate::writeSubStreamsInfo(const QList<quint64> &unpackSizes, const QList<bool> &digestsDefined, const QList<quint32> &digests) 2013 { 2014 writeByte(kSubStreamsInfo); 2015 2016 for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) { 2017 if (numUnpackStreamsInFolders.at(i) != 1) { 2018 writeByte(kNumUnpackStream); 2019 for (int j = 0; j < numUnpackStreamsInFolders.size(); j++) { 2020 writeNumber(numUnpackStreamsInFolders.at(j)); 2021 } 2022 break; 2023 } 2024 } 2025 2026 bool needFlag = true; 2027 int index = 0; 2028 for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) { 2029 for (quint32 j = 0; j < numUnpackStreamsInFolders.at(i); j++) { 2030 if (j + 1 != numUnpackStreamsInFolders.at(i)) { 2031 if (needFlag) { 2032 writeByte(kSize); 2033 } 2034 needFlag = false; 2035 writeNumber(unpackSizes[index]); 2036 } 2037 index++; 2038 } 2039 } 2040 2041 QList<bool> digestsDefined2; 2042 QList<quint32> digests2; 2043 2044 int digestIndex = 0; 2045 for (int i = 0; i < folders.size(); i++) { 2046 int numSubStreams = (int)numUnpackStreamsInFolders.at(i); 2047 if (numSubStreams == 1 && folders.at(i)->unpackCRCDefined) { 2048 digestIndex++; 2049 } else { 2050 for (int j = 0; j < numSubStreams; j++, digestIndex++) { 2051 digestsDefined2.append(digestsDefined[digestIndex]); 2052 digests2.append(digests[digestIndex]); 2053 } 2054 } 2055 } 2056 writeHashDigests(digestsDefined2, digests2); 2057 writeByte(kEnd); 2058 } 2059 2060 QByteArray K7Zip::K7ZipPrivate::encodeStream(QList<quint64> &packSizes, QList<Folder *> &folds) 2061 { 2062 Folder *folder = new Folder; 2063 folder->unpackCRCDefined = true; 2064 folder->unpackCRC = crc32(0, (Bytef *)(header.data()), header.size()); 2065 folder->unpackSizes.append(header.size()); 2066 2067 Folder::FolderInfo *info = new Folder::FolderInfo(); 2068 info->numInStreams = 1; 2069 info->numOutStreams = 1; 2070 info->methodID = k_LZMA2; 2071 2072 quint32 dictSize = header.size(); 2073 const quint32 kMinReduceSize = (1 << 16); 2074 if (dictSize < kMinReduceSize) { 2075 dictSize = kMinReduceSize; 2076 } 2077 2078 int dict; 2079 for (dict = 0; dict < 40; dict++) { 2080 if (dictSize <= lzma2_dic_size_from_prop(dict)) { 2081 break; 2082 } 2083 } 2084 2085 info->properties.append(dict); 2086 folder->folderInfos.append(info); 2087 2088 folds.append(folder); 2089 2090 // compress data 2091 QByteArray encodedData; 2092 if (!header.isEmpty()) { 2093 QByteArray enc; 2094 QBuffer inBuffer(&enc); 2095 2096 KCompressionDevice flt(&inBuffer, false, KCompressionDevice::Xz); 2097 flt.open(QIODevice::WriteOnly); 2098 2099 KFilterBase *filter = flt.filterBase(); 2100 2101 static_cast<KXzFilter *>(filter)->init(QIODevice::WriteOnly, KXzFilter::LZMA2, info->properties); 2102 2103 const int ret = flt.write(header); 2104 if (ret != header.size()) { 2105 qCDebug(KArchiveLog) << "write error write " << ret << "expected" << header.size(); 2106 return encodedData; 2107 } 2108 2109 flt.close(); 2110 encodedData = inBuffer.data(); 2111 } 2112 2113 packSizes.append(encodedData.size()); 2114 return encodedData; 2115 } 2116 2117 void K7Zip::K7ZipPrivate::writeHeader(quint64 &headerOffset) 2118 { 2119 quint64 packedSize = 0; 2120 for (int i = 0; i < packSizes.size(); ++i) { 2121 packedSize += packSizes[i]; 2122 } 2123 2124 headerOffset = packedSize; 2125 2126 writeByte(kHeader); 2127 2128 // Archive Properties 2129 2130 if (!folders.isEmpty()) { 2131 writeByte(kMainStreamsInfo); 2132 writePackInfo(0, packSizes, packCRCsDefined, packCRCs); 2133 2134 writeUnpackInfo(folders); 2135 2136 QList<quint64> unpackFileSizes; 2137 QList<bool> digestsDefined; 2138 QList<quint32> digests; 2139 for (int i = 0; i < fileInfos.size(); i++) { 2140 const FileInfo *file = fileInfos.at(i); 2141 if (!file->hasStream) { 2142 continue; 2143 } 2144 unpackFileSizes.append(file->size); 2145 digestsDefined.append(file->crcDefined); 2146 digests.append(file->crc); 2147 } 2148 2149 writeSubStreamsInfo(unpackSizes, digestsDefined, digests); 2150 writeByte(kEnd); 2151 } 2152 2153 if (fileInfos.isEmpty()) { 2154 writeByte(kEnd); 2155 return; 2156 } 2157 2158 writeByte(kFilesInfo); 2159 writeNumber(fileInfos.size()); 2160 2161 { 2162 /* ---------- Empty Streams ---------- */ 2163 QList<bool> emptyStreamVector; 2164 int numEmptyStreams = 0; 2165 for (int i = 0; i < fileInfos.size(); i++) { 2166 if (fileInfos.at(i)->hasStream) { 2167 emptyStreamVector.append(false); 2168 } else { 2169 emptyStreamVector.append(true); 2170 numEmptyStreams++; 2171 } 2172 } 2173 2174 if (numEmptyStreams > 0) { 2175 writeByte(kEmptyStream); 2176 writeNumber(((unsigned)emptyStreamVector.size() + 7) / 8); 2177 writeBoolVector(emptyStreamVector); 2178 2179 QList<bool> emptyFileVector; 2180 QList<bool> antiVector; 2181 int numEmptyFiles = 0; 2182 int numAntiItems = 0; 2183 for (int i = 0; i < fileInfos.size(); i++) { 2184 const FileInfo *file = fileInfos.at(i); 2185 if (!file->hasStream) { 2186 emptyFileVector.append(!file->isDir); 2187 if (!file->isDir) { 2188 numEmptyFiles++; 2189 bool isAnti = (i < this->isAnti.size() && this->isAnti[i]); 2190 antiVector.append(isAnti); 2191 if (isAnti) { 2192 numAntiItems++; 2193 } 2194 } 2195 } 2196 } 2197 2198 if (numEmptyFiles > 0) { 2199 writeByte(kEmptyFile); 2200 writeNumber(((unsigned)emptyFileVector.size() + 7) / 8); 2201 writeBoolVector(emptyFileVector); 2202 } 2203 2204 if (numAntiItems > 0) { 2205 writeByte(kAnti); 2206 writeNumber(((unsigned)antiVector.size() + 7) / 8); 2207 writeBoolVector(antiVector); 2208 } 2209 } 2210 } 2211 2212 { 2213 /* ---------- Names ---------- */ 2214 2215 int numDefined = 0; 2216 size_t namesDataSize = 0; 2217 for (int i = 0; i < fileInfos.size(); i++) { 2218 const QString &name = fileInfos.at(i)->path; 2219 if (!name.isEmpty()) { 2220 numDefined++; 2221 namesDataSize += (name.length() + 1) * 2; 2222 } 2223 } 2224 2225 if (numDefined > 0) { 2226 namesDataSize++; 2227 // SkipAlign(2 + GetBigNumberSize(namesDataSize), 2); 2228 2229 writeByte(kName); 2230 writeNumber(namesDataSize); 2231 writeByte(0); 2232 for (int i = 0; i < fileInfos.size(); i++) { 2233 const QString &name = fileInfos.at(i)->path; 2234 for (int t = 0; t < name.length(); t++) { 2235 wchar_t c = name[t].toLatin1(); 2236 writeByte((unsigned char)c); 2237 writeByte((unsigned char)(c >> 8)); 2238 } 2239 // End of string 2240 writeByte(0); 2241 writeByte(0); 2242 } 2243 } 2244 } 2245 2246 writeUInt64DefVector(mTimes, mTimesDefined, kMTime); 2247 2248 writeUInt64DefVector(startPositions, startPositionsDefined, kStartPos); 2249 2250 { 2251 /* ---------- Write Attrib ---------- */ 2252 QList<bool> boolVector; 2253 int numDefined = 0; 2254 boolVector.reserve(fileInfos.size()); 2255 for (int i = 0; i < fileInfos.size(); i++) { 2256 bool defined = fileInfos.at(i)->attribDefined; 2257 boolVector.append(defined); 2258 if (defined) { 2259 numDefined++; 2260 } 2261 } 2262 2263 if (numDefined > 0) { 2264 writeAlignedBoolHeader(boolVector, numDefined, kAttributes, 4); 2265 for (int i = 0; i < fileInfos.size(); i++) { 2266 const FileInfo *file = fileInfos.at(i); 2267 if (file->attribDefined) { 2268 writeUInt32(file->attributes); 2269 } 2270 } 2271 } 2272 } 2273 2274 writeByte(kEnd); // for files 2275 writeByte(kEnd); // for headers*/ 2276 } 2277 2278 static void setUInt32(unsigned char *p, quint32 d) 2279 { 2280 for (int i = 0; i < 4; i++, d >>= 8) { 2281 p[i] = (unsigned)d; 2282 } 2283 } 2284 2285 static void setUInt64(unsigned char *p, quint64 d) 2286 { 2287 for (int i = 0; i < 8; i++, d >>= 8) { 2288 p[i] = (unsigned char)d; 2289 } 2290 } 2291 2292 void K7Zip::K7ZipPrivate::writeStartHeader(const quint64 nextHeaderSize, const quint32 nextHeaderCRC, const quint64 nextHeaderOffset) 2293 { 2294 unsigned char buf[24]; 2295 setUInt64(buf + 4, nextHeaderOffset); 2296 setUInt64(buf + 12, nextHeaderSize); 2297 setUInt32(buf + 20, nextHeaderCRC); 2298 setUInt32(buf, crc32(0, (Bytef *)(buf + 4), 20)); 2299 q->device()->write((char *)buf, 24); 2300 } 2301 2302 void K7Zip::K7ZipPrivate::writeSignature() 2303 { 2304 unsigned char buf[8]; 2305 memcpy(buf, k7zip_signature, 6); 2306 buf[6] = 0 /*kMajorVersion*/; 2307 buf[7] = 3; 2308 q->device()->write((char *)buf, 8); 2309 } 2310 2311 bool K7Zip::openArchive(QIODevice::OpenMode mode) 2312 { 2313 if (!(mode & QIODevice::ReadOnly)) { 2314 return true; 2315 } 2316 2317 QIODevice *dev = device(); 2318 2319 if (!dev) { 2320 setErrorString(tr("Could not get underlying device")); 2321 return false; 2322 } 2323 2324 char header[32]; 2325 // check signature 2326 qint64 n = dev->read(header, 32); 2327 if (n != 32) { 2328 setErrorString(tr("Read header failed")); 2329 return false; 2330 } 2331 2332 for (int i = 0; i < 6; ++i) { 2333 if ((unsigned char)header[i] != k7zip_signature[i]) { 2334 setErrorString(tr("Check signature failed")); 2335 return false; 2336 } 2337 } 2338 2339 // get Archive Version 2340 int major = header[6]; 2341 int minor = header[7]; 2342 2343 /*if (major > 0 || minor > 2) { 2344 qCDebug(KArchiveLog) << "wrong archive version"; 2345 return false; 2346 }*/ 2347 2348 // get Start Header CRC 2349 quint32 startHeaderCRC = GetUi32(header, 8); 2350 quint64 nextHeaderOffset = GetUi64(header, 12); 2351 quint64 nextHeaderSize = GetUi64(header, 20); 2352 quint32 nextHeaderCRC = GetUi32(header, 28); 2353 2354 quint32 crc = crc32(0, (Bytef *)(header + 0xC), 20); 2355 2356 if (crc != startHeaderCRC) { 2357 setErrorString(tr("Bad CRC")); 2358 return false; 2359 } 2360 2361 if (nextHeaderSize == 0) { 2362 return true; 2363 } 2364 2365 if (nextHeaderSize > (quint64)0xFFFFFFFF) { 2366 setErrorString(tr("Next header size is too big")); 2367 return false; 2368 } 2369 2370 if ((qint64)nextHeaderOffset < 0) { 2371 setErrorString(tr("Next header size is less than zero")); 2372 return false; 2373 } 2374 2375 dev->seek(nextHeaderOffset + 32); 2376 2377 QByteArray inBuffer; 2378 inBuffer.resize(nextHeaderSize); 2379 2380 n = dev->read(inBuffer.data(), inBuffer.size()); 2381 if (n != (qint64)nextHeaderSize) { 2382 setErrorString(tr("Failed read next header size; should read %1, read %2").arg(nextHeaderSize).arg(n)); 2383 return false; 2384 } 2385 d->buffer = inBuffer.data(); 2386 d->end = nextHeaderSize; 2387 2388 d->headerSize = 32 + nextHeaderSize; 2389 // int physSize = 32 + nextHeaderSize + nextHeaderOffset; 2390 2391 crc = crc32(0, (Bytef *)(d->buffer), (quint32)nextHeaderSize); 2392 2393 if (crc != nextHeaderCRC) { 2394 setErrorString(tr("Bad next header CRC")); 2395 return false; 2396 } 2397 2398 int type = d->readByte(); 2399 QByteArray decodedData; 2400 if (type != kHeader) { 2401 if (type != kEncodedHeader) { 2402 setErrorString(tr("Error in header")); 2403 return false; 2404 } 2405 2406 decodedData = d->readAndDecodePackedStreams(); 2407 2408 int external = d->readByte(); 2409 if (external != 0) { 2410 int dataIndex = (int)d->readNumber(); 2411 if (dataIndex < 0) { 2412 // qCDebug(KArchiveLog) << "dataIndex error"; 2413 } 2414 d->buffer = decodedData.constData(); 2415 d->pos = 0; 2416 d->end = decodedData.size(); 2417 } 2418 2419 type = d->readByte(); 2420 if (type != kHeader) { 2421 setErrorString(tr("Wrong header type")); 2422 return false; 2423 } 2424 } 2425 // read header 2426 2427 type = d->readByte(); 2428 2429 if (type == kArchiveProperties) { 2430 // TODO : implement this part 2431 setErrorString(tr("Not implemented")); 2432 return false; 2433 } 2434 2435 if (type == kAdditionalStreamsInfo) { 2436 // TODO : implement this part 2437 setErrorString(tr("Not implemented")); 2438 return false; 2439 } 2440 2441 if (type == kMainStreamsInfo) { 2442 if (!d->readMainStreamsInfo()) { 2443 setErrorString(tr("Error while reading main streams information")); 2444 return false; 2445 } 2446 type = d->readByte(); 2447 } else { 2448 for (int i = 0; i < d->folders.size(); ++i) { 2449 Folder *folder = d->folders.at(i); 2450 d->unpackSizes.append(folder->getUnpackSize()); 2451 d->digestsDefined.append(folder->unpackCRCDefined); 2452 d->digests.append(folder->unpackCRC); 2453 } 2454 } 2455 2456 if (type == kEnd) { 2457 return true; 2458 } 2459 2460 if (type != kFilesInfo) { 2461 setErrorString(tr("Error while reading header")); 2462 return false; 2463 } 2464 2465 // read files info 2466 int numFiles = d->readNumber(); 2467 for (int i = 0; i < numFiles; ++i) { 2468 d->fileInfos.append(new FileInfo); 2469 } 2470 2471 QList<bool> emptyStreamVector; 2472 QList<bool> emptyFileVector; 2473 QList<bool> antiFileVector; 2474 int numEmptyStreams = 0; 2475 2476 for (;;) { 2477 quint64 type = d->readByte(); 2478 if (type == kEnd) { 2479 break; 2480 } 2481 2482 quint64 size = d->readNumber(); 2483 2484 size_t ppp = d->pos; 2485 2486 bool addPropIdToList = true; 2487 bool isKnownType = true; 2488 2489 if (type > ((quint32)1 << 30)) { 2490 isKnownType = false; 2491 } else { 2492 switch (type) { 2493 case kEmptyStream: { 2494 d->readBoolVector(numFiles, emptyStreamVector); 2495 for (int i = 0; i < emptyStreamVector.size(); ++i) { 2496 if (emptyStreamVector[i]) { 2497 numEmptyStreams++; 2498 } 2499 } 2500 2501 break; 2502 } 2503 case kEmptyFile: 2504 d->readBoolVector(numEmptyStreams, emptyFileVector); 2505 break; 2506 case kAnti: 2507 d->readBoolVector(numEmptyStreams, antiFileVector); 2508 break; 2509 case kCTime: 2510 if (!d->readUInt64DefVector(numFiles, d->cTimes, d->cTimesDefined)) { 2511 return false; 2512 } 2513 break; 2514 case kATime: 2515 if (!d->readUInt64DefVector(numFiles, d->aTimes, d->aTimesDefined)) { 2516 return false; 2517 } 2518 break; 2519 case kMTime: 2520 if (!d->readUInt64DefVector(numFiles, d->mTimes, d->mTimesDefined)) { 2521 setErrorString(tr("Error reading modification time")); 2522 return false; 2523 } 2524 break; 2525 case kName: { 2526 int external = d->readByte(); 2527 if (external != 0) { 2528 int dataIndex = d->readNumber(); 2529 if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) { 2530 qCDebug(KArchiveLog) << "wrong data index"; 2531 } 2532 2533 // TODO : go to the new index 2534 } 2535 2536 QString name; 2537 for (int i = 0; i < numFiles; i++) { 2538 name = d->readString(); 2539 d->fileInfos.at(i)->path = name; 2540 } 2541 break; 2542 } 2543 case kAttributes: { 2544 QList<bool> attributesAreDefined; 2545 d->readBoolVector2(numFiles, attributesAreDefined); 2546 int external = d->readByte(); 2547 if (external != 0) { 2548 int dataIndex = d->readNumber(); 2549 if (dataIndex < 0) { 2550 qCDebug(KArchiveLog) << "wrong data index"; 2551 } 2552 2553 // TODO : go to the new index 2554 } 2555 2556 for (int i = 0; i < numFiles; i++) { 2557 FileInfo *fileInfo = d->fileInfos.at(i); 2558 fileInfo->attribDefined = attributesAreDefined[i]; 2559 if (fileInfo->attribDefined) { 2560 fileInfo->attributes = d->readUInt32(); 2561 } 2562 } 2563 break; 2564 } 2565 case kStartPos: 2566 if (!d->readUInt64DefVector(numFiles, d->startPositions, d->startPositionsDefined)) { 2567 setErrorString(tr("Error reading MTime")); 2568 return false; 2569 } 2570 break; 2571 case kDummy: { 2572 for (quint64 i = 0; i < size; i++) { 2573 if (d->readByte() != 0) { 2574 setErrorString(tr("Invalid")); 2575 return false; 2576 } 2577 } 2578 addPropIdToList = false; 2579 break; 2580 } 2581 default: 2582 addPropIdToList = isKnownType = false; 2583 } 2584 } 2585 2586 if (isKnownType) { 2587 if (addPropIdToList) { 2588 d->fileInfoPopIDs.append(type); 2589 } 2590 } else { 2591 d->skipData(d->readNumber()); 2592 } 2593 2594 bool checkRecordsSize = (major > 0 || minor > 2); 2595 if (checkRecordsSize && d->pos - ppp != size) { 2596 setErrorString(tr("Read size failed " 2597 "(checkRecordsSize: %1, d->pos - ppp: %2, size: %3)") 2598 .arg(checkRecordsSize) 2599 .arg(d->pos - ppp) 2600 .arg(size)); 2601 return false; 2602 } 2603 } 2604 2605 int emptyFileIndex = 0; 2606 int sizeIndex = 0; 2607 2608 int numAntiItems = 0; 2609 2610 if (emptyStreamVector.isEmpty()) { 2611 emptyStreamVector.fill(false, numFiles); 2612 } 2613 2614 if (antiFileVector.isEmpty()) { 2615 antiFileVector.fill(false, numEmptyStreams); 2616 } 2617 if (emptyFileVector.isEmpty()) { 2618 emptyFileVector.fill(false, numEmptyStreams); 2619 } 2620 2621 for (int i = 0; i < numEmptyStreams; i++) { 2622 if (antiFileVector[i]) { 2623 numAntiItems++; 2624 } 2625 } 2626 2627 d->outData = d->readAndDecodePackedStreams(false); 2628 2629 int oldPos = 0; 2630 for (int i = 0; i < numFiles; i++) { 2631 FileInfo *fileInfo = d->fileInfos.at(i); 2632 bool isAnti; 2633 fileInfo->hasStream = !emptyStreamVector[i]; 2634 if (fileInfo->hasStream) { 2635 fileInfo->isDir = false; 2636 isAnti = false; 2637 fileInfo->size = d->unpackSizes[sizeIndex]; 2638 fileInfo->crc = d->digests[sizeIndex]; 2639 fileInfo->crcDefined = d->digestsDefined[sizeIndex]; 2640 sizeIndex++; 2641 } else { 2642 fileInfo->isDir = !emptyFileVector[emptyFileIndex]; 2643 isAnti = antiFileVector[emptyFileIndex]; 2644 emptyFileIndex++; 2645 fileInfo->size = 0; 2646 fileInfo->crcDefined = false; 2647 } 2648 if (numAntiItems != 0) { 2649 d->isAnti.append(isAnti); 2650 } 2651 2652 int access; 2653 bool symlink = false; 2654 if (fileInfo->attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) { 2655 access = fileInfo->attributes >> 16; 2656 if ((access & QT_STAT_MASK) == QT_STAT_LNK) { 2657 symlink = true; 2658 } 2659 } else { 2660 if (fileInfo->isDir) { 2661 access = S_IFDIR | 0755; 2662 } else { 2663 access = 0100644; 2664 } 2665 } 2666 2667 qint64 pos = 0; 2668 if (!fileInfo->isDir) { 2669 pos = oldPos; 2670 oldPos += fileInfo->size; 2671 } 2672 2673 KArchiveEntry *e; 2674 QString entryName; 2675 int index = fileInfo->path.lastIndexOf(QLatin1Char('/')); 2676 if (index == -1) { 2677 entryName = fileInfo->path; 2678 } else { 2679 entryName = fileInfo->path.mid(index + 1); 2680 } 2681 Q_ASSERT(!entryName.isEmpty()); 2682 2683 QDateTime mTime; 2684 if (d->mTimesDefined[i]) { 2685 mTime = KArchivePrivate::time_tToDateTime(toTimeT(d->mTimes[i])); 2686 } else { 2687 mTime = KArchivePrivate::time_tToDateTime(time(nullptr)); 2688 } 2689 2690 if (fileInfo->isDir) { 2691 QString path = QDir::cleanPath(fileInfo->path); 2692 const KArchiveEntry *ent = rootDir()->entry(path); 2693 if (ent && ent->isDirectory()) { 2694 e = nullptr; 2695 } else { 2696 e = new KArchiveDirectory(this, entryName, access, mTime, rootDir()->user(), rootDir()->group(), QString() /*symlink*/); 2697 } 2698 } else { 2699 if (!symlink) { 2700 e = new K7ZipFileEntry(this, 2701 entryName, 2702 access, 2703 mTime, 2704 rootDir()->user(), 2705 rootDir()->group(), 2706 QString() /*symlink*/, 2707 pos, 2708 fileInfo->size, 2709 d->outData); 2710 } else { 2711 QString target = QFile::decodeName(d->outData.mid(pos, fileInfo->size)); 2712 e = new K7ZipFileEntry(this, entryName, access, mTime, rootDir()->user(), rootDir()->group(), target, 0, 0, nullptr); 2713 } 2714 } 2715 2716 if (e) { 2717 if (index == -1) { 2718 rootDir()->addEntry(e); 2719 } else { 2720 QString path = QDir::cleanPath(fileInfo->path.left(index)); 2721 KArchiveDirectory *d = findOrCreate(path); 2722 d->addEntry(e); 2723 } 2724 } 2725 } 2726 2727 return true; 2728 } 2729 2730 bool K7Zip::closeArchive() 2731 { 2732 // Unnecessary check (already checked by KArchive::close()) 2733 if (!isOpen()) { 2734 // qCWarning(KArchiveLog) << "You must open the file before close it\n"; 2735 return false; 2736 } 2737 2738 if ((mode() == QIODevice::ReadOnly)) { 2739 return true; 2740 } 2741 2742 d->clear(); 2743 2744 Folder *folder = new Folder(); 2745 2746 folder->unpackSizes.clear(); 2747 folder->unpackSizes.append(d->outData.size()); 2748 2749 Folder::FolderInfo *info = new Folder::FolderInfo(); 2750 2751 info->numInStreams = 1; 2752 info->numOutStreams = 1; 2753 info->methodID = k_LZMA2; 2754 2755 quint32 dictSize = d->outData.size(); 2756 2757 const quint32 kMinReduceSize = (1 << 16); 2758 if (dictSize < kMinReduceSize) { 2759 dictSize = kMinReduceSize; 2760 } 2761 2762 // k_LZMA2 method 2763 int dict; 2764 for (dict = 0; dict < 40; dict++) { 2765 if (dictSize <= lzma2_dic_size_from_prop(dict)) { 2766 break; 2767 } 2768 } 2769 info->properties.append(dict); 2770 2771 folder->folderInfos.append(info); 2772 d->folders.append(folder); 2773 2774 const KArchiveDirectory *dir = directory(); 2775 QByteArray data; 2776 d->createItemsFromEntities(dir, QString(), data); 2777 d->outData = data; 2778 2779 folder->unpackCRCDefined = true; 2780 folder->unpackCRC = crc32(0, (Bytef *)(d->outData.data()), d->outData.size()); 2781 2782 // compress data 2783 QByteArray encodedData; 2784 if (!d->outData.isEmpty()) { 2785 QByteArray enc; 2786 QBuffer inBuffer(&enc); 2787 2788 KCompressionDevice flt(&inBuffer, false, KCompressionDevice::Xz); 2789 flt.open(QIODevice::WriteOnly); 2790 2791 KFilterBase *filter = flt.filterBase(); 2792 2793 static_cast<KXzFilter *>(filter)->init(QIODevice::WriteOnly, KXzFilter::LZMA2, info->properties); 2794 2795 const int ret = flt.write(d->outData); 2796 if (ret != d->outData.size()) { 2797 setErrorString(tr("Write error")); 2798 return false; 2799 } 2800 2801 flt.close(); 2802 encodedData = inBuffer.data(); 2803 } 2804 2805 d->packSizes.append(encodedData.size()); 2806 2807 int numUnpackStream = 0; 2808 for (int i = 0; i < d->fileInfos.size(); ++i) { 2809 if (d->fileInfos.at(i)->hasStream) { 2810 numUnpackStream++; 2811 } 2812 } 2813 d->numUnpackStreamsInFolders.append(numUnpackStream); 2814 2815 quint64 headerOffset; 2816 d->writeHeader(headerOffset); 2817 2818 // Encode Header 2819 QByteArray encodedStream; 2820 { 2821 QList<quint64> packSizes; 2822 QList<Folder *> folders; 2823 encodedStream = d->encodeStream(packSizes, folders); 2824 2825 if (folders.isEmpty()) { 2826 // FIXME Not sure why this is an error. Come up with a better message 2827 setErrorString(tr("Failed while encoding header")); 2828 return false; 2829 } 2830 2831 d->header.clear(); 2832 2833 d->writeByte(kEncodedHeader); 2834 QList<bool> emptyDefined; 2835 QList<quint32> emptyCrcs; 2836 d->writePackInfo(headerOffset, packSizes, emptyDefined, emptyCrcs); 2837 d->writeUnpackInfo(folders); 2838 d->writeByte(kEnd); 2839 for (int i = 0; i < packSizes.size(); i++) { 2840 headerOffset += packSizes.at(i); 2841 } 2842 qDeleteAll(folders); 2843 } 2844 // end encode header 2845 2846 quint64 nextHeaderSize = d->header.size(); 2847 quint32 nextHeaderCRC = crc32(0, (Bytef *)(d->header.data()), d->header.size()); 2848 quint64 nextHeaderOffset = headerOffset; 2849 2850 device()->seek(0); 2851 d->writeSignature(); 2852 d->writeStartHeader(nextHeaderSize, nextHeaderCRC, nextHeaderOffset); 2853 device()->write(encodedData.data(), encodedData.size()); 2854 device()->write(encodedStream.data(), encodedStream.size()); 2855 device()->write(d->header.data(), d->header.size()); 2856 2857 return true; 2858 } 2859 2860 bool K7Zip::doFinishWriting(qint64 size) 2861 { 2862 d->m_currentFile->setSize(size); 2863 d->m_currentFile = nullptr; 2864 2865 return true; 2866 } 2867 2868 bool K7Zip::doWriteData(const char *data, qint64 size) 2869 { 2870 if (!d->m_currentFile) { 2871 setErrorString(tr("No file currently selected")); 2872 return false; 2873 } 2874 2875 if (d->m_currentFile->position() == d->outData.size()) { 2876 d->outData.append(data, size); 2877 } else { 2878 d->outData.remove(d->m_currentFile->position(), d->m_currentFile->size()); 2879 d->outData.insert(d->m_currentFile->position(), data, size); 2880 } 2881 2882 return true; 2883 } 2884 2885 bool K7Zip::doPrepareWriting(const QString &name, 2886 const QString &user, 2887 const QString &group, 2888 qint64 /*size*/, 2889 mode_t perm, 2890 const QDateTime & /*atime*/, 2891 const QDateTime &mtime, 2892 const QDateTime & /*ctime*/) 2893 { 2894 if (!isOpen()) { 2895 setErrorString(tr("Application error: 7-Zip file must be open before being written into")); 2896 qCWarning(KArchiveLog) << "doPrepareWriting failed: !isOpen()"; 2897 return false; 2898 } 2899 2900 if (!(mode() & QIODevice::WriteOnly)) { 2901 setErrorString(tr("Application error: attempted to write into non-writable 7-Zip file")); 2902 qCWarning(KArchiveLog) << "doPrepareWriting failed: !(mode() & QIODevice::WriteOnly)"; 2903 return false; 2904 } 2905 2906 // Find or create parent dir 2907 KArchiveDirectory *parentDir = rootDir(); 2908 // QString fileName( name ); 2909 // In some files we can find dir/./file => call cleanPath 2910 QString fileName(QDir::cleanPath(name)); 2911 int i = name.lastIndexOf(QLatin1Char('/')); 2912 if (i != -1) { 2913 QString dir = name.left(i); 2914 fileName = name.mid(i + 1); 2915 parentDir = findOrCreate(dir); 2916 } 2917 2918 // test if the entry already exist 2919 const KArchiveEntry *entry = parentDir->entry(fileName); 2920 if (!entry) { 2921 K7ZipFileEntry *e = 2922 new K7ZipFileEntry(this, fileName, perm, mtime, user, group, QString() /*symlink*/, d->outData.size(), 0 /*unknown yet*/, d->outData); 2923 if (!parentDir->addEntryV2(e)) { 2924 return false; 2925 } 2926 d->m_entryList << e; 2927 d->m_currentFile = e; 2928 } else { 2929 // TODO : find and replace in m_entryList 2930 // d->m_currentFile = static_cast<K7ZipFileEntry*>(entry); 2931 } 2932 2933 return true; 2934 } 2935 2936 bool K7Zip::doWriteDir(const QString &name, 2937 const QString &user, 2938 const QString &group, 2939 mode_t perm, 2940 const QDateTime & /*atime*/, 2941 const QDateTime &mtime, 2942 const QDateTime & /*ctime*/) 2943 { 2944 if (!isOpen()) { 2945 setErrorString(tr("Application error: 7-Zip file must be open before being written into")); 2946 qCWarning(KArchiveLog) << "doWriteDir failed: !isOpen()"; 2947 return false; 2948 } 2949 2950 if (!(mode() & QIODevice::WriteOnly)) { 2951 // qCWarning(KArchiveLog) << "You must open the tar file for writing\n"; 2952 return false; 2953 } 2954 2955 // In some tar files we can find dir/./ => call cleanPath 2956 QString dirName(QDir::cleanPath(name)); 2957 2958 // Remove trailing '/' 2959 if (dirName.endsWith(QLatin1Char('/'))) { 2960 dirName.remove(dirName.size() - 1, 1); 2961 } 2962 2963 KArchiveDirectory *parentDir = rootDir(); 2964 int i = dirName.lastIndexOf(QLatin1Char('/')); 2965 if (i != -1) { 2966 QString dir = name.left(i); 2967 dirName = name.mid(i + 1); 2968 parentDir = findOrCreate(dir); 2969 } 2970 2971 KArchiveDirectory *e = new KArchiveDirectory(this, dirName, perm, mtime, user, group, QString() /*symlink*/); 2972 parentDir->addEntry(e); 2973 2974 return true; 2975 } 2976 2977 bool K7Zip::doWriteSymLink(const QString &name, 2978 const QString &target, 2979 const QString &user, 2980 const QString &group, 2981 mode_t perm, 2982 const QDateTime & /*atime*/, 2983 const QDateTime &mtime, 2984 const QDateTime & /*ctime*/) 2985 { 2986 if (!isOpen()) { 2987 setErrorString(tr("Application error: 7-Zip file must be open before being written into")); 2988 qCWarning(KArchiveLog) << "doWriteSymLink failed: !isOpen()"; 2989 return false; 2990 } 2991 2992 if (!(mode() & QIODevice::WriteOnly)) { 2993 setErrorString(tr("Application error: attempted to write into non-writable 7-Zip file")); 2994 qCWarning(KArchiveLog) << "doWriteSymLink failed: !(mode() & QIODevice::WriteOnly)"; 2995 return false; 2996 } 2997 2998 // Find or create parent dir 2999 KArchiveDirectory *parentDir = rootDir(); 3000 // In some files we can find dir/./file => call cleanPath 3001 QString fileName(QDir::cleanPath(name)); 3002 int i = name.lastIndexOf(QLatin1Char('/')); 3003 if (i != -1) { 3004 QString dir = name.left(i); 3005 fileName = name.mid(i + 1); 3006 parentDir = findOrCreate(dir); 3007 } 3008 QByteArray encodedTarget = QFile::encodeName(target); 3009 3010 K7ZipFileEntry *e = new K7ZipFileEntry(this, fileName, perm, mtime, user, group, target, 0, 0, nullptr); 3011 d->outData.append(encodedTarget); 3012 3013 if (!parentDir->addEntryV2(e)) { 3014 return false; 3015 } 3016 3017 d->m_entryList << e; 3018 3019 return true; 3020 } 3021 3022 void K7Zip::virtual_hook(int id, void *data) 3023 { 3024 KArchive::virtual_hook(id, data); 3025 }