1 /*****************************************************************************
2 * Copyright (C) 2000 David Faure <faure@kde.org> *
3 * Copyright (C) 2002 Szombathelyi György <gyurco@users.sourceforge.net> *
4 * Copyright (C) 2003 Leo Savernik <l.savernik@aon.at> *
5 * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] *
6 * *
7 * This file is heavily based on ktar from kdelibs *
8 * *
9 * This file is part of Krusader [https://krusader.org]. *
10 * *
11 * Krusader is free software: you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation, either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * Krusader is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with Krusader. If not, see [http://www.gnu.org/licenses/]. *
23 *****************************************************************************/
24
25 #include "kiso.h"
26
27 // QtCore
28 #include <QDebug>
29 #include <QDir>
30 #include <QFile>
31 #include <QMimeDatabase>
32 #include <QMimeType>
33 #include <qplatformdefs.h>
34
35 #include <KConfigCore/KConfig>
36 #include <KConfigCore/KConfigGroup>
37 #include <KArchive/KFilterBase>
38 #include <KArchive/KFilterDev>
39
40 #include "libisofs/isofs.h"
41 #include "qfilehack.h"
42
43 #ifdef Q_OS_LINUX
44 #undef __STRICT_ANSI__
45 #include <linux/cdrom.h>
46 #define __STRICT_ANSI__
47 #include <sys/ioctl.h>
48 #include <fcntl.h>
49 #endif
50
51 ////////////////////////////////////////////////////////////////////////
52 /////////////////////////// KIso ///////////////////////////////////
53 ////////////////////////////////////////////////////////////////////////
54
55 /**
56 * puts the track layout of the device 'fname' into 'tracks'
57 * tracks structure: start sector, track number, ...
58 * tracks should be 100*2 entry long (this is the maximum in the CD-ROM standard)
59 * currently it's linux only, porters are welcome
60 */
61
getTracks(const char * fname,int * tracks)62 static int getTracks(const char *fname, int *tracks)
63 {
64 KRFUNC;
65 int ret = 0;
66 memset(tracks, 0, 200*sizeof(int));
67
68 #ifdef Q_OS_LINUX
69 int fd, i;
70 struct cdrom_tochdr tochead;
71 struct cdrom_tocentry tocentry;
72
73 //qDebug() << "getTracks open:" << fname << endl;
74 fd = QT_OPEN(fname, O_RDONLY | O_NONBLOCK);
75 if (fd > 0) {
76 if (ioctl(fd, CDROMREADTOCHDR, &tochead) != -1) {
77 // qDebug() << "getTracks first track:" << tochead.cdth_trk0
78 // << " last track " << tochead.cdth_trk1 << endl;
79 for (i = tochead.cdth_trk0;i <= tochead.cdth_trk1;++i) {
80 if (ret > 99) break;
81 memset(&tocentry, 0, sizeof(struct cdrom_tocentry));
82 tocentry.cdte_track = i;
83 tocentry.cdte_format = CDROM_LBA;
84 if (ioctl(fd, CDROMREADTOCENTRY, &tocentry) < 0) break;
85 // qDebug() << "getTracks got track " << i << " starting at: " <<
86 // tocentry.cdte_addr.lba << endl;
87 if ((tocentry.cdte_ctrl & 0x4) == 0x4) {
88 tracks[ret<<1] = tocentry.cdte_addr.lba;
89 tracks[(ret<<1)+1] = i;
90 ret++;
91 }
92 }
93 }
94 close(fd);
95 }
96
97 #endif
98
99 return ret;
100 }
101
102 class KIso::KIsoPrivate
103 {
104 public:
KIsoPrivate()105 KIsoPrivate() {}
106 QStringList dirList;
107 };
108
KIso(const QString & filename,const QString & _mimetype)109 KIso::KIso(const QString& filename, const QString & _mimetype)
110 : KArchive(0L)
111 {
112 KRFUNC;
113 KRDEBUG("Starting KIso: " << filename << " - type: " << _mimetype);
114
115 m_startsec = -1;
116 m_filename = filename;
117 d = new KIsoPrivate;
118 QString mimetype(_mimetype);
119 bool forced = true;
120 if (mimetype.isEmpty()) {
121 QMimeDatabase db;
122 QMimeType mt = db.mimeTypeForFile(filename, QMimeDatabase::MatchContent);
123 if (mt.isValid())
124 mimetype = mt.name();
125
126 //qDebug() << "KIso::KIso mimetype=" << mimetype << endl;
127
128 // Don't move to prepareDevice - the other constructor theoretically allows ANY filter
129 if (mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around
130 mimetype == "application/x-webarchive")
131 // that's a gzipped tar file, so ask for gzip filter
132 mimetype = "application/x-gzip";
133 else if (mimetype == "application/x-tbz") // that's a bzipped2 tar file, so ask for bz2 filter
134 mimetype = "application/x-bzip2";
135 else {
136 // Something else. Check if it's not really gzip though (e.g. for KOffice docs)
137 QFile file(filename);
138 if (file.open(QIODevice::ReadOnly)) {
139 char firstByte;
140 char secondByte;
141 char thirdByte;
142 file.getChar(&firstByte);
143 file.getChar(&secondByte);
144 file.getChar(&thirdByte);
145 if (firstByte == 0037 && secondByte == (char)0213)
146 mimetype = "application/x-gzip";
147 else if (firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h')
148 mimetype = "application/x-bzip2";
149 else if (firstByte == 'P' && secondByte == 'K' && thirdByte == 3) {
150 char fourthByte;
151 file.getChar(&fourthByte);
152 if (fourthByte == 4)
153 mimetype = "application/x-zip";
154 }
155 }
156 }
157 forced = false;
158 }
159
160 prepareDevice(filename, mimetype, forced);
161 }
162
prepareDevice(const QString & filename,const QString & mimetype,bool forced)163 void KIso::prepareDevice(const QString & filename,
164 const QString & mimetype, bool forced)
165 {
166 KRFUNC;
167 KRDEBUG("Preparing: " << filename << " - type: " << mimetype << " - using the force: " << forced);
168 /* 'hack' for Qt's false assumption that only S_ISREG is seekable */
169 if ("inode/blockdevice" == mimetype)
170 setDevice(new QFileHack(filename));
171 else {
172 if ("application/x-gzip" == mimetype
173 || "application/x-bzip2" == mimetype)
174 forced = true;
175
176
177 KCompressionDevice *device;
178 if(mimetype.isEmpty()) {
179 device = new KFilterDev(filename);
180 } else {
181 device = new KCompressionDevice(filename, KFilterDev::compressionTypeForMimeType(mimetype));
182 }
183 if (device->compressionType() == KCompressionDevice::None && forced) {
184 delete device;
185 } else {
186 setDevice(device);
187 }
188 }
189
190 }
191
KIso(QIODevice * dev)192 KIso::KIso(QIODevice * dev)
193 : KArchive(dev)
194 {
195 d = new KIsoPrivate;
196 }
197
~KIso()198 KIso::~KIso()
199 {
200 // mjarrett: Closes to prevent ~KArchive from aborting w/o device
201 if (isOpen())
202 close();
203 if (!m_filename.isEmpty())
204 delete device(); // we created it ourselves
205 delete d;
206 }
207
208 /* callback function for libisofs */
readf(char * buf,unsigned int start,unsigned int len,void * udata)209 static int readf(char *buf, unsigned int start, unsigned int len, void *udata)
210 {
211 KRFUNC;
212
213 QIODevice* dev = (static_cast<KIso*>(udata))->device();
214
215 // seek(0) ensures integrity with the QIODevice's built-in buffer
216 // see bug #372023 for details
217 dev->seek(0);
218
219 if (dev->seek((qint64)start << (qint64)11)) {
220 if ((dev->read(buf, len << 11u)) != -1) return (len);
221 }
222 //qDebug() << "KIso::ReadRequest failed start: " << start << " len: " << len << endl;
223
224 return -1;
225 }
226
227 /* callback function for libisofs */
mycallb(struct iso_directory_record * idr,void * udata)228 static int mycallb(struct iso_directory_record *idr, void *udata)
229 {
230 KRFUNC;
231
232 KIso *iso = static_cast<KIso*>(udata);
233 QString path, user, group, symlink;
234 int i;
235 int access;
236 int time, cdate, adate;
237 rr_entry rr;
238 bool special = false;
239 KArchiveEntry *entry = NULL, *oldentry = NULL;
240 char z_algo[2], z_params[2];
241 long long z_size = 0;
242
243 if ((idr->flags[0] & 1) && !iso->showhidden) return 0;
244 if (iso->level) {
245 if (isonum_711(idr->name_len) == 1) {
246 switch (idr->name[0]) {
247 case 0:
248 path += (".");
249 special = true;
250 break;
251 case 1:
252 path += ("..");
253 special = true;
254 break;
255 }
256 }
257 if (iso->showrr && ParseRR(idr, &rr) > 0) {
258 if (!special) path = rr.name;
259 symlink = rr.sl;
260 access = rr.mode;
261 time = rr.t_mtime;
262 adate = rr.t_atime;
263 cdate = rr.t_ctime;
264 user.setNum(rr.uid);
265 group.setNum(rr.gid);
266 z_algo[0] = rr.z_algo[0];z_algo[1] = rr.z_algo[1];
267 z_params[0] = rr.z_params[0];z_params[1] = rr.z_params[1];
268 z_size = rr.z_size;
269 } else {
270 access = iso->dirent->permissions() & ~S_IFMT;
271 adate = cdate = time = isodate_915(idr->date, 0);
272 user = iso->dirent->user();
273 group = iso->dirent->group();
274 if (idr->flags[0] & 2) access |= S_IFDIR; else access |= S_IFREG;
275 if (!special) {
276 if (iso->joliet) {
277 for (i = 0;i < (isonum_711(idr->name_len) - 1);i += 2) {
278 void *p = &(idr->name[i]);
279 QChar ch(be2me_16(*(ushort *)p));
280 if (ch == ';') break;
281 path += ch;
282 }
283 } else {
284 for (i = 0;i < isonum_711(idr->name_len);++i) {
285 if (idr->name[i] == ';') break;
286 if (idr->name[i]) path += (idr->name[i]);
287 }
288 }
289 if (path.endsWith('.')) path.resize(path.length() - 1);
290 }
291 }
292 if (iso->showrr) FreeRR(&rr);
293 if (idr->flags[0] & 2) {
294 entry = new KIsoDirectory(iso, path, access | S_IFDIR, time, adate, cdate,
295 user, group, symlink);
296 } else {
297 entry = new KIsoFile(iso, path, access, time, adate, cdate,
298 user, group, symlink, (long long)(isonum_733(idr->extent)) << (long long)11, isonum_733(idr->size));
299 if (z_size)(static_cast <KIsoFile*>(entry))->setZF(z_algo, z_params, z_size);
300
301 }
302 iso->dirent->addEntry(entry);
303 }
304 if ((idr->flags[0] & 2) && (iso->level == 0 || !special)) {
305 if (iso->level) {
306 oldentry = iso->dirent;
307 iso->dirent = static_cast<KIsoDirectory*>(entry);
308 }
309 iso->level++;
310 ProcessDir(&readf, isonum_733(idr->extent), isonum_733(idr->size), &mycallb, udata);
311 iso->level--;
312 if (iso->level) iso->dirent = static_cast<KIsoDirectory*>(oldentry);
313 }
314 return 0;
315 }
316
addBoot(struct el_torito_boot_descriptor * bootdesc)317 void KIso::addBoot(struct el_torito_boot_descriptor* bootdesc)
318 {
319 KRFUNC;
320
321 int i;
322 long long size;
323 boot_head boot;
324 boot_entry *be;
325 QString path;
326 KIsoFile *entry;
327
328 path = "Catalog";
329 entry = new KIsoFile(this, path, dirent->permissions() & ~S_IFDIR,
330 dirent->date(), dirent->adate(), dirent->cdate(),
331 dirent->user(), dirent->group(), QString(),
332 (long long)isonum_731(bootdesc->boot_catalog) << (long long)11, (long long)2048);
333 dirent->addEntry(entry);
334 if (!ReadBootTable(&readf, isonum_731(bootdesc->boot_catalog), &boot, this)) {
335 i = 1;
336 be = boot.defentry;
337 while (be) {
338 size = BootImageSize(isonum_711(be->data.d_e.media),
339 isonum_721(be->data.d_e.seccount));
340 path = "Default Image";
341 if (i > 1) path += " (" + QString::number(i) + ')';
342 entry = new KIsoFile(this, path, dirent->permissions() & ~S_IFDIR,
343 dirent->date(), dirent->adate(), dirent->cdate(),
344 dirent->user(), dirent->group(), QString(),
345 (long long)isonum_731(be->data.d_e.start) << (long long)11, size << (long long)9);
346 dirent->addEntry(entry);
347 be = be->next;
348 i++;
349 }
350
351 FreeBootTable(&boot);
352 }
353 }
354
readParams()355 void KIso::readParams()
356 {
357 KRFUNC;
358 KConfig *config;
359
360 config = new KConfig("kio_isorc");
361
362 KConfigGroup group(config, QString());
363 showhidden = group.readEntry("showhidden", false);
364 showrr = group.readEntry("showrr", true);
365 delete config;
366 }
367
openArchive(QIODevice::OpenMode mode)368 bool KIso::openArchive(QIODevice::OpenMode mode)
369 {
370 KRFUNC;
371 iso_vol_desc *desc;
372 QString path, uid, gid;
373 QT_STATBUF buf;
374 int tracks[2*100], trackno = 0, i, access, c_b, c_i, c_j;
375 KArchiveDirectory *root;
376 struct iso_directory_record* idr;
377 struct el_torito_boot_descriptor* bootdesc;
378
379 if (mode == QIODevice::WriteOnly)
380 return false;
381
382 readParams();
383 d->dirList.clear();
384
385 tracks[0] = 0;
386 if (m_startsec > 0) tracks[0] = m_startsec;
387 //qDebug() << " m_startsec: " << m_startsec << endl;
388 /* We'll use the permission and user/group of the 'host' file except
389 * in Rock Ridge, where the permissions are stored on the file system
390 */
391 if (QT_STAT(m_filename.toLocal8Bit(), &buf) < 0) {
392 /* defaults, if stat fails */
393 memset(&buf, 0, sizeof(struct stat));
394 buf.st_mode = 0777;
395 } else {
396 /* If it's a block device, try to query the track layout (for multisession) */
397 if (m_startsec == -1 && S_ISBLK(buf.st_mode))
398 trackno = getTracks(m_filename.toLatin1(), (int*) & tracks);
399 }
400 uid.setNum(buf.st_uid);
401 gid.setNum(buf.st_gid);
402 access = buf.st_mode & ~S_IFMT;
403
404 //qDebug() << "KIso::openArchive number of tracks: " << trackno << endl;
405
406 if (trackno == 0) trackno = 1;
407 for (i = 0;i < trackno;++i) {
408
409 c_b = 1;c_i = 1;c_j = 1;
410 root = rootDir();
411 if (trackno > 1) {
412 path.clear();
413 QTextStream(&path) << "Track " << tracks[(i<<1)+1];
414 root = new KIsoDirectory(this, path, access | S_IFDIR,
415 buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString());
416 rootDir()->addEntry(root);
417 }
418
419 desc = ReadISO9660(&readf, tracks[i<<1], this);
420 if (!desc) {
421 //qDebug() << "KIso::openArchive no volume descriptors" << endl;
422 continue;
423 }
424
425 while (desc) {
426 switch (isonum_711(desc->data.type)) {
427 case ISO_VD_BOOT:
428
429 bootdesc = (struct el_torito_boot_descriptor*) & (desc->data);
430 if (!memcmp(EL_TORITO_ID, bootdesc->system_id, ISODCL(8, 39))) {
431 path = "El Torito Boot";
432 if (c_b > 1) path += " (" + QString::number(c_b) + ')';
433
434 dirent = new KIsoDirectory(this, path, access | S_IFDIR,
435 buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString());
436 root->addEntry(dirent);
437
438 addBoot(bootdesc);
439 c_b++;
440 }
441 break;
442
443 case ISO_VD_PRIMARY:
444 case ISO_VD_SUPPLEMENTARY:
445 idr = (struct iso_directory_record*) & (((struct iso_primary_descriptor*) & desc->data)->root_directory_record);
446 joliet = JolietLevel(&desc->data);
447 if (joliet) {
448 QTextStream(&path) << "Joliet level " << joliet;
449 if (c_j > 1) path += " (" + QString::number(c_j) + ')';
450 } else {
451 path = "ISO9660";
452 if (c_i > 1) path += " (" + QString::number(c_i) + ')';
453 }
454 dirent = new KIsoDirectory(this, path, access | S_IFDIR,
455 buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString());
456 root->addEntry(dirent);
457 level = 0;
458 mycallb(idr, this);
459 if (joliet) c_j++; else c_i++;
460 break;
461 }
462 desc = desc->next;
463 }
464 free(desc);
465 }
466 device()->close();
467 return true;
468 }
469
closeArchive()470 bool KIso::closeArchive()
471 {
472 KRFUNC;
473 d->dirList.clear();
474 return true;
475 }
476
writeDir(const QString &,const QString &,const QString &,mode_t,time_t,time_t,time_t)477 bool KIso::writeDir(const QString&, const QString&, const QString&, mode_t, time_t, time_t, time_t)
478 {
479 return false;
480 }
481
prepareWriting(const QString &,const QString &,const QString &,qint64,mode_t,time_t,time_t,time_t)482 bool KIso::prepareWriting(const QString&, const QString&, const QString&, qint64, mode_t, time_t, time_t, time_t)
483 {
484 return false;
485 }
486
finishWriting(qint64)487 bool KIso::finishWriting(qint64)
488 {
489 return false;
490 }
491
writeSymLink(const QString &,const QString &,const QString &,const QString &,mode_t,time_t,time_t,time_t)492 bool KIso::writeSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, time_t, time_t, time_t)
493 {
494 return false;
495 }
496
doWriteDir(const QString &,const QString &,const QString &,mode_t,const QDateTime &,const QDateTime &,const QDateTime &)497 bool KIso::doWriteDir(const QString&, const QString&, const QString&, mode_t, const QDateTime&, const QDateTime &, const QDateTime &)
498 {
499 return false;
500 }
501
doWriteSymLink(const QString &,const QString &,const QString &,const QString &,mode_t,const QDateTime &,const QDateTime &,const QDateTime &)502 bool KIso::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime&, const QDateTime&, const QDateTime&)
503 {
504 return false;
505 }
506
doPrepareWriting(const QString &,const QString &,const QString &,qint64,mode_t,const QDateTime &,const QDateTime &,const QDateTime &)507 bool KIso::doPrepareWriting(const QString& , const QString& , const QString& , qint64, mode_t, const QDateTime&, const QDateTime&, const QDateTime&)
508 {
509 return false;
510 }
511
doFinishWriting(qint64)512 bool KIso::doFinishWriting(qint64)
513 {
514 return false;
515 }
516
virtual_hook(int id,void * data)517 void KIso::virtual_hook(int id, void* data)
518 {
519 KArchive::virtual_hook(id, data);
520 }
521
522