cf12defd28
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
196 lines
6.3 KiB
C++
196 lines
6.3 KiB
C++
/* This file is part of the KDE libraries
|
|
SPDX-FileCopyrightText: 2002 Laurence Anderson <l.d.anderson@warwick.ac.uk>
|
|
|
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
|
*/
|
|
|
|
#include "kar.h"
|
|
#include "karchive_p.h"
|
|
#include "loggingcategory.h"
|
|
|
|
#include <QDebug>
|
|
#include <QFile>
|
|
|
|
#include <limits>
|
|
|
|
#include "kcompressiondevice.h"
|
|
//#include "klimitediodevice_p.h"
|
|
|
|
// As documented in QByteArray
|
|
static constexpr int kMaxQByteArraySize = std::numeric_limits<int>::max() - 32;
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////// KAr ///////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
class Q_DECL_HIDDEN KAr::KArPrivate
|
|
{
|
|
public:
|
|
KArPrivate()
|
|
{
|
|
}
|
|
};
|
|
|
|
KAr::KAr(const QString &filename)
|
|
: KArchive(filename)
|
|
, d(new KArPrivate)
|
|
{
|
|
}
|
|
|
|
KAr::KAr(QIODevice *dev)
|
|
: KArchive(dev)
|
|
, d(new KArPrivate)
|
|
{
|
|
}
|
|
|
|
KAr::~KAr()
|
|
{
|
|
if (isOpen()) {
|
|
close();
|
|
}
|
|
delete d;
|
|
}
|
|
|
|
bool KAr::doPrepareWriting(const QString &, const QString &, const QString &, qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
|
|
{
|
|
setErrorString(tr("Cannot write to AR file"));
|
|
qCWarning(KArchiveLog) << "doPrepareWriting not implemented for KAr";
|
|
return false;
|
|
}
|
|
|
|
bool KAr::doFinishWriting(qint64)
|
|
{
|
|
setErrorString(tr("Cannot write to AR file"));
|
|
qCWarning(KArchiveLog) << "doFinishWriting not implemented for KAr";
|
|
return false;
|
|
}
|
|
|
|
bool KAr::doWriteDir(const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
|
|
{
|
|
setErrorString(tr("Cannot write to AR file"));
|
|
qCWarning(KArchiveLog) << "doWriteDir not implemented for KAr";
|
|
return false;
|
|
}
|
|
|
|
bool KAr::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
|
|
{
|
|
setErrorString(tr("Cannot write to AR file"));
|
|
qCWarning(KArchiveLog) << "doWriteSymLink not implemented for KAr";
|
|
return false;
|
|
}
|
|
|
|
bool KAr::openArchive(QIODevice::OpenMode mode)
|
|
{
|
|
// Open archive
|
|
|
|
if (mode == QIODevice::WriteOnly) {
|
|
return true;
|
|
}
|
|
if (mode != QIODevice::ReadOnly && mode != QIODevice::ReadWrite) {
|
|
setErrorString(tr("Unsupported mode %1").arg(static_cast<int>(mode)));
|
|
return false;
|
|
}
|
|
|
|
QIODevice *dev = device();
|
|
if (!dev) {
|
|
return false;
|
|
}
|
|
|
|
QByteArray magic = dev->read(7);
|
|
if (magic != "!<arch>") {
|
|
setErrorString(tr("Invalid main magic"));
|
|
return false;
|
|
}
|
|
|
|
QByteArray ar_longnames;
|
|
while (!dev->atEnd()) {
|
|
QByteArray ar_header;
|
|
ar_header.resize(60);
|
|
|
|
dev->seek(dev->pos() + (2 - (dev->pos() % 2)) % 2); // Ar headers are padded to byte boundary
|
|
|
|
if (dev->read(ar_header.data(), 60) != 60) { // Read ar header
|
|
qCWarning(KArchiveLog) << "Couldn't read header";
|
|
return true; // Probably EOF / trailing junk
|
|
}
|
|
|
|
if (!ar_header.endsWith("`\n")) { // Check header magic // krazy:exclude=strings
|
|
setErrorString(tr("Invalid magic"));
|
|
return false;
|
|
}
|
|
|
|
QByteArray name = ar_header.mid(0, 16); // Process header
|
|
const int date = ar_header.mid(16, 12).trimmed().toInt();
|
|
// const int uid = ar_header.mid( 28, 6 ).trimmed().toInt();
|
|
// const int gid = ar_header.mid( 34, 6 ).trimmed().toInt();
|
|
const int mode = ar_header.mid(40, 8).trimmed().toInt(nullptr, 8);
|
|
const qint64 size = ar_header.mid(48, 10).trimmed().toInt();
|
|
if (size < 0 || size > kMaxQByteArraySize) {
|
|
setErrorString(tr("Invalid size"));
|
|
return false;
|
|
}
|
|
|
|
bool skip_entry = false; // Deal with special entries
|
|
if (name.mid(0, 1) == "/") {
|
|
if (name.mid(1, 1) == "/") { // Longfilename table entry
|
|
ar_longnames.resize(size);
|
|
// Read the table. Note that the QByteArray will contain NUL characters after each entry.
|
|
dev->read(ar_longnames.data(), size);
|
|
skip_entry = true;
|
|
qCDebug(KArchiveLog) << "Read in longnames entry";
|
|
} else if (name.mid(1, 1) == " ") { // Symbol table entry
|
|
qCDebug(KArchiveLog) << "Skipped symbol entry";
|
|
dev->seek(dev->pos() + size);
|
|
skip_entry = true;
|
|
} else { // Longfilename, look it up in the table
|
|
const int ar_longnamesIndex = name.mid(1, 15).trimmed().toInt();
|
|
qCDebug(KArchiveLog) << "Longfilename #" << ar_longnamesIndex;
|
|
if (ar_longnames.isEmpty()) {
|
|
setErrorString(tr("Invalid longfilename reference"));
|
|
return false;
|
|
}
|
|
if (ar_longnamesIndex < 0 || ar_longnamesIndex >= ar_longnames.size()) {
|
|
setErrorString(tr("Invalid longfilename position reference"));
|
|
return false;
|
|
}
|
|
name = QByteArray(ar_longnames.constData() + ar_longnamesIndex);
|
|
name.truncate(name.indexOf('/'));
|
|
}
|
|
}
|
|
if (skip_entry) {
|
|
continue;
|
|
}
|
|
|
|
// Process filename
|
|
name = name.trimmed();
|
|
name.replace('/', QByteArray());
|
|
qCDebug(KArchiveLog) << "Filename: " << name << " Size: " << size;
|
|
|
|
KArchiveEntry *entry = new KArchiveFile(this,
|
|
QString::fromLocal8Bit(name.constData()),
|
|
mode,
|
|
KArchivePrivate::time_tToDateTime(date),
|
|
rootDir()->user(),
|
|
rootDir()->group(),
|
|
/*symlink*/ QString(),
|
|
dev->pos(),
|
|
size);
|
|
rootDir()->addEntry(entry); // Ar files don't support directories, so everything in root
|
|
|
|
dev->seek(dev->pos() + size); // Skip contents
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool KAr::closeArchive()
|
|
{
|
|
// Close the archive
|
|
return true;
|
|
}
|
|
|
|
void KAr::virtual_hook(int id, void *data)
|
|
{
|
|
KArchive::virtual_hook(id, data);
|
|
}
|