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
20 static constexpr int kMaxQByteArraySize = std::numeric_limits<int>::max() - 32;
21
22 ////////////////////////////////////////////////////////////////////////
23 /////////////////////////// KAr ///////////////////////////////////////
24 ////////////////////////////////////////////////////////////////////////
25
26 class Q_DECL_HIDDEN KAr::KArPrivate
27 {
28 public:
KArPrivate()29 KArPrivate()
30 {
31 }
32 };
33
KAr(const QString & filename)34 KAr::KAr(const QString &filename)
35 : KArchive(filename)
36 , d(new KArPrivate)
37 {
38 }
39
KAr(QIODevice * dev)40 KAr::KAr(QIODevice *dev)
41 : KArchive(dev)
42 , d(new KArPrivate)
43 {
44 }
45
~KAr()46 KAr::~KAr()
47 {
48 if (isOpen()) {
49 close();
50 }
51 delete d;
52 }
53
doPrepareWriting(const QString &,const QString &,const QString &,qint64,mode_t,const QDateTime &,const QDateTime &,const QDateTime &)54 bool 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
doFinishWriting(qint64)61 bool KAr::doFinishWriting(qint64)
62 {
63 setErrorString(tr("Cannot write to AR file"));
64 qCWarning(KArchiveLog) << "doFinishWriting not implemented for KAr";
65 return false;
66 }
67
doWriteDir(const QString &,const QString &,const QString &,mode_t,const QDateTime &,const QDateTime &,const QDateTime &)68 bool 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
doWriteSymLink(const QString &,const QString &,const QString &,const QString &,mode_t,const QDateTime &,const QDateTime &,const QDateTime &)75 bool 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
openArchive(QIODevice::OpenMode mode)82 bool KAr::openArchive(QIODevice::OpenMode mode)
83 {
84 // Open archive
85
86 if (mode == QIODevice::WriteOnly) {
87 return true;
88 }
89 if (mode != QIODevice::ReadOnly && mode != QIODevice::ReadWrite) {
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,
170 QString::fromLocal8Bit(name.constData()),
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
closeArchive()186 bool KAr::closeArchive()
187 {
188 // Close the archive
189 return true;
190 }
191
virtual_hook(int id,void * data)192 void KAr::virtual_hook(int id, void *data)
193 {
194 KArchive::virtual_hook(id, data);
195 }
196