KArchive

kar.cpp
1/* This file is part of the KDE libraries
2 SPDX-FileCopyrightText: 2002 Laurence Anderson <l.d.anderson@warwick.ac.uk>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "kar.h"
8#include "karchive_p.h"
9#include "loggingcategory.h"
10
11#include <QDebug>
12#include <QFile>
13
14#include <limits>
15
16#include "kcompressiondevice.h"
17//#include "klimitediodevice_p.h"
18
19// As documented in QByteArray
20static constexpr int kMaxQByteArraySize = std::numeric_limits<int>::max() - 32;
21
22////////////////////////////////////////////////////////////////////////
23/////////////////////////// KAr ///////////////////////////////////////
24////////////////////////////////////////////////////////////////////////
25
26class Q_DECL_HIDDEN KAr::KArPrivate
27{
28public:
29 KArPrivate()
30 {
31 }
32};
33
34KAr::KAr(const QString &filename)
35 : KArchive(filename)
36 , d(new KArPrivate)
37{
38}
39
41 : KArchive(dev)
42 , d(new KArPrivate)
43{
44}
45
47{
48 if (isOpen()) {
49 close();
50 }
51 delete d;
52}
53
54bool KAr::doPrepareWriting(const QString &, const QString &, const QString &, qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
55{
56 setErrorString(tr("Cannot write to AR file"));
57 qCWarning(KArchiveLog) << "doPrepareWriting not implemented for KAr";
58 return false;
59}
60
62{
63 setErrorString(tr("Cannot write to AR file"));
64 qCWarning(KArchiveLog) << "doFinishWriting not implemented for KAr";
65 return false;
66}
67
68bool KAr::doWriteDir(const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
69{
70 setErrorString(tr("Cannot write to AR file"));
71 qCWarning(KArchiveLog) << "doWriteDir not implemented for KAr";
72 return false;
73}
74
75bool KAr::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
76{
77 setErrorString(tr("Cannot write to AR file"));
78 qCWarning(KArchiveLog) << "doWriteSymLink not implemented for KAr";
79 return false;
80}
81
83{
84 // Open archive
85
87 return true;
88 }
90 setErrorString(tr("Unsupported mode %1").arg(mode));
91 return false;
92 }
93
94 QIODevice *dev = device();
95 if (!dev) {
96 return false;
97 }
98
99 QByteArray magic = dev->read(7);
100 if (magic != "!<arch>") {
101 setErrorString(tr("Invalid main magic"));
102 return false;
103 }
104
105 QByteArray ar_longnames;
106 while (!dev->atEnd()) {
107 QByteArray ar_header;
108 ar_header.resize(60);
109
110 dev->seek(dev->pos() + (2 - (dev->pos() % 2)) % 2); // Ar headers are padded to byte boundary
111
112 if (dev->read(ar_header.data(), 60) != 60) { // Read ar header
113 qCWarning(KArchiveLog) << "Couldn't read header";
114 return true; // Probably EOF / trailing junk
115 }
116
117 if (!ar_header.endsWith("`\n")) { // Check header magic // krazy:exclude=strings
118 setErrorString(tr("Invalid magic"));
119 return false;
120 }
121
122 QByteArray name = ar_header.mid(0, 16); // Process header
123 const int date = ar_header.mid(16, 12).trimmed().toInt();
124 // const int uid = ar_header.mid( 28, 6 ).trimmed().toInt();
125 // const int gid = ar_header.mid( 34, 6 ).trimmed().toInt();
126 const int mode = ar_header.mid(40, 8).trimmed().toInt(nullptr, 8);
127 const qint64 size = ar_header.mid(48, 10).trimmed().toInt();
128 if (size < 0 || size > kMaxQByteArraySize) {
129 setErrorString(tr("Invalid size"));
130 return false;
131 }
132
133 bool skip_entry = false; // Deal with special entries
134 if (name.mid(0, 1) == "/") {
135 if (name.mid(1, 1) == "/") { // Longfilename table entry
136 ar_longnames.resize(size);
137 // Read the table. Note that the QByteArray will contain NUL characters after each entry.
138 dev->read(ar_longnames.data(), size);
139 skip_entry = true;
140 qCDebug(KArchiveLog) << "Read in longnames entry";
141 } else if (name.mid(1, 1) == " ") { // Symbol table entry
142 qCDebug(KArchiveLog) << "Skipped symbol entry";
143 dev->seek(dev->pos() + size);
144 skip_entry = true;
145 } else { // Longfilename, look it up in the table
146 const int ar_longnamesIndex = name.mid(1, 15).trimmed().toInt();
147 qCDebug(KArchiveLog) << "Longfilename #" << ar_longnamesIndex;
148 if (ar_longnames.isEmpty()) {
149 setErrorString(tr("Invalid longfilename reference"));
150 return false;
151 }
152 if (ar_longnamesIndex < 0 || ar_longnamesIndex >= ar_longnames.size()) {
153 setErrorString(tr("Invalid longfilename position reference"));
154 return false;
155 }
156 name = QByteArray(ar_longnames.constData() + ar_longnamesIndex);
157 name.truncate(name.indexOf('/'));
158 }
159 }
160 if (skip_entry) {
161 continue;
162 }
163
164 // Process filename
165 name = name.trimmed();
166 name.replace('/', QByteArray());
167 qCDebug(KArchiveLog) << "Filename: " << name << " Size: " << size;
168
169 KArchiveEntry *entry = new KArchiveFile(this,
171 mode,
172 KArchivePrivate::time_tToDateTime(date),
173 rootDir()->user(),
174 rootDir()->group(),
175 /*symlink*/ QString(),
176 dev->pos(),
177 size);
178 rootDir()->addEntry(entry); // Ar files don't support directories, so everything in root
179
180 dev->seek(dev->pos() + size); // Skip contents
181 }
182
183 return true;
184}
185
187{
188 // Close the archive
189 return true;
190}
191
192void KAr::virtual_hook(int id, void *data)
193{
194 KArchive::virtual_hook(id, data);
195}
KAr is a class for reading archives in ar format.
Definition kar.h:21
bool doWriteSymLink(const QString &name, const QString &target, const QString &user, const QString &group, mode_t perm, const QDateTime &atime, const QDateTime &mtime, const QDateTime &ctime) override
Writes a symbolic link to the archive.
Definition kar.cpp:75
KAr(const QString &filename)
Creates an instance that operates on the given filename.
Definition kar.cpp:34
bool openArchive(QIODevice::OpenMode mode) override
Opens the archive for reading.
Definition kar.cpp:82
~KAr() override
If the ar file is still opened, then it will be closed automatically by the destructor.
Definition kar.cpp:46
bool closeArchive() override
Closes the archive.
Definition kar.cpp:186
bool doPrepareWriting(const QString &name, const QString &user, const QString &group, qint64 size, mode_t perm, const QDateTime &atime, const QDateTime &mtime, const QDateTime &ctime) override
This virtual method must be implemented by subclasses.
Definition kar.cpp:54
bool doWriteDir(const QString &name, const QString &user, const QString &group, mode_t perm, const QDateTime &atime, const QDateTime &mtime, const QDateTime &ctime) override
Write a directory to the archive.
Definition kar.cpp:68
bool doFinishWriting(qint64 size) override
Called after writing the data.
Definition kar.cpp:61
void addEntry(KArchiveEntry *)
Definition karchive.cpp:922
A base class for entries in an KArchive.
Represents a file entry in a KArchive.
KArchive is a base class for reading and writing archives.
Definition karchive.h:41
QIODevice * device() const
The underlying device.
Definition karchive.cpp:628
virtual bool close()
Closes the archive.
Definition karchive.cpp:214
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
Definition karchive.cpp:519
QIODevice::OpenMode mode() const
Returns the mode in which the archive was opened.
Definition karchive.cpp:623
bool isOpen() const
Checks whether the archive is open.
Definition karchive.cpp:633
void setErrorString(const QString &errorStr)
Sets error description.
Definition karchive.cpp:484
const char * constData() const const
char * data()
bool endsWith(QByteArrayView bv) const const
bool isEmpty() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
void resize(qsizetype newSize, char c)
qsizetype size() const const
int toInt(bool *ok, int base) const const
QByteArray trimmed() const const
virtual bool atEnd() const const
virtual qint64 pos() const const
QByteArray read(qint64 maxSize)
virtual bool seek(qint64 pos)
typedef OpenMode
const QChar * constData() const const
QString fromLocal8Bit(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString mid(qsizetype position, qsizetype n) const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
int toInt(bool *ok, int base) const const
QString trimmed() const const
void truncate(qsizetype position)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:15:57 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.