File indexing completed on 2025-02-16 13:00:35
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 2002 Laurence Anderson <l.d.anderson@warwick.ac.uk> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "kar.h" 0008 #include "karchive_p.h" 0009 #include "loggingcategory.h" 0010 0011 #include <QDebug> 0012 #include <QFile> 0013 0014 #include <limits> 0015 0016 #include "kcompressiondevice.h" 0017 //#include "klimitediodevice_p.h" 0018 0019 // As documented in QByteArray 0020 static constexpr int kMaxQByteArraySize = std::numeric_limits<int>::max() - 32; 0021 0022 //////////////////////////////////////////////////////////////////////// 0023 /////////////////////////// KAr /////////////////////////////////////// 0024 //////////////////////////////////////////////////////////////////////// 0025 0026 class Q_DECL_HIDDEN KAr::KArPrivate 0027 { 0028 public: 0029 KArPrivate() 0030 { 0031 } 0032 }; 0033 0034 KAr::KAr(const QString &filename) 0035 : KArchive(filename) 0036 , d(new KArPrivate) 0037 { 0038 } 0039 0040 KAr::KAr(QIODevice *dev) 0041 : KArchive(dev) 0042 , d(new KArPrivate) 0043 { 0044 } 0045 0046 KAr::~KAr() 0047 { 0048 if (isOpen()) { 0049 close(); 0050 } 0051 delete d; 0052 } 0053 0054 bool KAr::doPrepareWriting(const QString &, const QString &, const QString &, qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 0055 { 0056 setErrorString(tr("Cannot write to AR file")); 0057 qCWarning(KArchiveLog) << "doPrepareWriting not implemented for KAr"; 0058 return false; 0059 } 0060 0061 bool KAr::doFinishWriting(qint64) 0062 { 0063 setErrorString(tr("Cannot write to AR file")); 0064 qCWarning(KArchiveLog) << "doFinishWriting not implemented for KAr"; 0065 return false; 0066 } 0067 0068 bool KAr::doWriteDir(const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 0069 { 0070 setErrorString(tr("Cannot write to AR file")); 0071 qCWarning(KArchiveLog) << "doWriteDir not implemented for KAr"; 0072 return false; 0073 } 0074 0075 bool KAr::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 0076 { 0077 setErrorString(tr("Cannot write to AR file")); 0078 qCWarning(KArchiveLog) << "doWriteSymLink not implemented for KAr"; 0079 return false; 0080 } 0081 0082 bool KAr::openArchive(QIODevice::OpenMode mode) 0083 { 0084 // Open archive 0085 0086 if (mode == QIODevice::WriteOnly) { 0087 return true; 0088 } 0089 if (mode != QIODevice::ReadOnly && mode != QIODevice::ReadWrite) { 0090 setErrorString(tr("Unsupported mode %1").arg(mode)); 0091 return false; 0092 } 0093 0094 QIODevice *dev = device(); 0095 if (!dev) { 0096 return false; 0097 } 0098 0099 QByteArray magic = dev->read(7); 0100 if (magic != "!<arch>") { 0101 setErrorString(tr("Invalid main magic")); 0102 return false; 0103 } 0104 0105 QByteArray ar_longnames; 0106 while (!dev->atEnd()) { 0107 QByteArray ar_header; 0108 ar_header.resize(60); 0109 0110 dev->seek(dev->pos() + (2 - (dev->pos() % 2)) % 2); // Ar headers are padded to byte boundary 0111 0112 if (dev->read(ar_header.data(), 60) != 60) { // Read ar header 0113 qCWarning(KArchiveLog) << "Couldn't read header"; 0114 return true; // Probably EOF / trailing junk 0115 } 0116 0117 if (!ar_header.endsWith("`\n")) { // Check header magic // krazy:exclude=strings 0118 setErrorString(tr("Invalid magic")); 0119 return false; 0120 } 0121 0122 QByteArray name = ar_header.mid(0, 16); // Process header 0123 const int date = ar_header.mid(16, 12).trimmed().toInt(); 0124 // const int uid = ar_header.mid( 28, 6 ).trimmed().toInt(); 0125 // const int gid = ar_header.mid( 34, 6 ).trimmed().toInt(); 0126 const int mode = ar_header.mid(40, 8).trimmed().toInt(nullptr, 8); 0127 const qint64 size = ar_header.mid(48, 10).trimmed().toInt(); 0128 if (size < 0 || size > kMaxQByteArraySize) { 0129 setErrorString(tr("Invalid size")); 0130 return false; 0131 } 0132 0133 bool skip_entry = false; // Deal with special entries 0134 if (name.mid(0, 1) == "/") { 0135 if (name.mid(1, 1) == "/") { // Longfilename table entry 0136 ar_longnames.resize(size); 0137 // Read the table. Note that the QByteArray will contain NUL characters after each entry. 0138 dev->read(ar_longnames.data(), size); 0139 skip_entry = true; 0140 qCDebug(KArchiveLog) << "Read in longnames entry"; 0141 } else if (name.mid(1, 1) == " ") { // Symbol table entry 0142 qCDebug(KArchiveLog) << "Skipped symbol entry"; 0143 dev->seek(dev->pos() + size); 0144 skip_entry = true; 0145 } else { // Longfilename, look it up in the table 0146 const int ar_longnamesIndex = name.mid(1, 15).trimmed().toInt(); 0147 qCDebug(KArchiveLog) << "Longfilename #" << ar_longnamesIndex; 0148 if (ar_longnames.isEmpty()) { 0149 setErrorString(tr("Invalid longfilename reference")); 0150 return false; 0151 } 0152 if (ar_longnamesIndex < 0 || ar_longnamesIndex >= ar_longnames.size()) { 0153 setErrorString(tr("Invalid longfilename position reference")); 0154 return false; 0155 } 0156 name = QByteArray(ar_longnames.constData() + ar_longnamesIndex); 0157 name.truncate(name.indexOf('/')); 0158 } 0159 } 0160 if (skip_entry) { 0161 continue; 0162 } 0163 0164 // Process filename 0165 name = name.trimmed(); 0166 name.replace('/', QByteArray()); 0167 qCDebug(KArchiveLog) << "Filename: " << name << " Size: " << size; 0168 0169 KArchiveEntry *entry = new KArchiveFile(this, 0170 QString::fromLocal8Bit(name.constData()), 0171 mode, 0172 KArchivePrivate::time_tToDateTime(date), 0173 rootDir()->user(), 0174 rootDir()->group(), 0175 /*symlink*/ QString(), 0176 dev->pos(), 0177 size); 0178 rootDir()->addEntry(entry); // Ar files don't support directories, so everything in root 0179 0180 dev->seek(dev->pos() + size); // Skip contents 0181 } 0182 0183 return true; 0184 } 0185 0186 bool KAr::closeArchive() 0187 { 0188 // Close the archive 0189 return true; 0190 } 0191 0192 void KAr::virtual_hook(int id, void *data) 0193 { 0194 KArchive::virtual_hook(id, data); 0195 }