File indexing completed on 2024-12-01 05:20:02
0001 /* 0002 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org> 0003 SPDX-FileCopyrightText: 2002 Szombathelyi György <gyurco@users.sourceforge.net> 0004 SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at> 0005 SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org> 0006 0007 This file is heavily based on ktar from kdelibs 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "kiso.h" 0013 0014 // QtCore 0015 #include <QDebug> 0016 #include <QDir> 0017 #include <QFile> 0018 #include <QMimeDatabase> 0019 #include <QMimeType> 0020 #include <qplatformdefs.h> 0021 0022 #include <KArchive/KFilterBase> 0023 #include <KArchive/KFilterDev> 0024 #include <KConfigCore/KConfig> 0025 #include <KConfigCore/KConfigGroup> 0026 0027 #include "libisofs/isofs.h" 0028 #include "qfilehack.h" 0029 0030 #ifdef Q_OS_LINUX 0031 #undef __STRICT_ANSI__ 0032 #include <linux/cdrom.h> 0033 #define __STRICT_ANSI__ 0034 #include <fcntl.h> 0035 #include <sys/ioctl.h> 0036 #endif 0037 0038 //////////////////////////////////////////////////////////////////////// 0039 /////////////////////////// KIso /////////////////////////////////// 0040 //////////////////////////////////////////////////////////////////////// 0041 0042 /** 0043 * puts the track layout of the device 'fname' into 'tracks' 0044 * tracks structure: start sector, track number, ... 0045 * tracks should be 100*2 entry long (this is the maximum in the CD-ROM standard) 0046 * currently it's linux only, porters are welcome 0047 */ 0048 0049 static int getTracks(const char *fname, int *tracks) 0050 { 0051 KRFUNC; 0052 int ret = 0; 0053 memset(tracks, 0, 200 * sizeof(int)); 0054 0055 #ifdef Q_OS_LINUX 0056 int fd, i; 0057 struct cdrom_tochdr tochead; 0058 struct cdrom_tocentry tocentry; 0059 0060 // qDebug() << "getTracks open:" << fname << endl; 0061 fd = QT_OPEN(fname, O_RDONLY | O_NONBLOCK); 0062 if (fd > 0) { 0063 if (ioctl(fd, CDROMREADTOCHDR, &tochead) != -1) { 0064 // qDebug() << "getTracks first track:" << tochead.cdth_trk0 0065 // << " last track " << tochead.cdth_trk1 << endl; 0066 for (i = tochead.cdth_trk0; i <= tochead.cdth_trk1; ++i) { 0067 if (ret > 99) 0068 break; 0069 memset(&tocentry, 0, sizeof(struct cdrom_tocentry)); 0070 tocentry.cdte_track = static_cast<__u8>(i); 0071 tocentry.cdte_format = CDROM_LBA; 0072 if (ioctl(fd, CDROMREADTOCENTRY, &tocentry) < 0) 0073 break; 0074 // qDebug() << "getTracks got track " << i << " starting at: " << 0075 // tocentry.cdte_addr.lba << endl; 0076 if ((tocentry.cdte_ctrl & 0x4) == 0x4) { 0077 tracks[ret << 1] = tocentry.cdte_addr.lba; 0078 tracks[(ret << 1) + 1] = i; 0079 ret++; 0080 } 0081 } 0082 } 0083 close(fd); 0084 } 0085 0086 #endif 0087 0088 return ret; 0089 } 0090 0091 class KIso::KIsoPrivate 0092 { 0093 public: 0094 KIsoPrivate() = default; 0095 QStringList dirList; 0096 }; 0097 0098 KIso::KIso(const QString &filename, const QString &_mimetype) 0099 : KArchive(nullptr) 0100 { 0101 KRFUNC; 0102 KRDEBUG("Starting KIso: " << filename << " - type: " << _mimetype); 0103 0104 m_startsec = -1; 0105 m_filename = filename; 0106 d = new KIsoPrivate; 0107 QString mimetype(_mimetype); 0108 bool forced = true; 0109 if (mimetype.isEmpty()) { 0110 QMimeDatabase db; 0111 QMimeType mt = db.mimeTypeForFile(filename, QMimeDatabase::MatchContent); 0112 if (mt.isValid()) 0113 mimetype = mt.name(); 0114 0115 // qDebug() << "KIso::KIso mimetype=" << mimetype << endl; 0116 0117 // Don't move to prepareDevice - the other constructor theoretically allows ANY filter 0118 if (mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around 0119 mimetype == "application/x-webarchive") 0120 // that's a gzipped tar file, so ask for gzip filter 0121 mimetype = "application/x-gzip"; 0122 else if (mimetype == "application/x-tbz") // that's a bzipped2 tar file, so ask for bz2 filter 0123 mimetype = "application/x-bzip2"; 0124 else { 0125 // Something else. Check if it's not really gzip though (e.g. for KOffice docs) 0126 QFile file(filename); 0127 if (file.open(QIODevice::ReadOnly)) { 0128 char firstByte; 0129 char secondByte; 0130 char thirdByte; 0131 file.getChar(&firstByte); 0132 file.getChar(&secondByte); 0133 file.getChar(&thirdByte); 0134 if (firstByte == 0037 && secondByte == (char)0213) 0135 mimetype = "application/x-gzip"; 0136 else if (firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h') 0137 mimetype = "application/x-bzip2"; 0138 else if (firstByte == 'P' && secondByte == 'K' && thirdByte == 3) { 0139 char fourthByte; 0140 file.getChar(&fourthByte); 0141 if (fourthByte == 4) 0142 mimetype = "application/x-zip"; 0143 } 0144 } 0145 } 0146 forced = false; 0147 } 0148 0149 prepareDevice(filename, mimetype, forced); 0150 } 0151 0152 void KIso::prepareDevice(const QString &filename, const QString &mimetype, bool forced) 0153 { 0154 KRFUNC; 0155 KRDEBUG("Preparing: " << filename << " - type: " << mimetype << " - using the force: " << forced); 0156 /* 'hack' for Qt's false assumption that only S_ISREG is seekable */ 0157 if ("inode/blockdevice" == mimetype) 0158 setDevice(new QFileHack(filename)); 0159 else { 0160 if ("application/x-gzip" == mimetype || "application/x-bzip2" == mimetype) 0161 forced = true; 0162 0163 KCompressionDevice *device; 0164 if (mimetype.isEmpty()) { 0165 device = new KFilterDev(filename); 0166 } else { 0167 device = new KCompressionDevice(filename, COMPRESSIONTYPEFORMIMETYPE(mimetype)); 0168 } 0169 if (device->compressionType() == KCompressionDevice::None && forced) { 0170 delete device; 0171 } else { 0172 setDevice(device); 0173 } 0174 } 0175 } 0176 0177 KIso::KIso(QIODevice *dev) 0178 : KArchive(dev) 0179 { 0180 d = new KIsoPrivate; 0181 } 0182 0183 KIso::~KIso() 0184 { 0185 // mjarrett: Closes to prevent ~KArchive from aborting w/o device 0186 if (isOpen()) 0187 close(); 0188 if (!m_filename.isEmpty()) 0189 delete device(); // we created it ourselves 0190 delete d; 0191 } 0192 0193 /* callback function for libisofs */ 0194 static int readf(char *buf, unsigned int start, unsigned int len, void *udata) 0195 { 0196 KRFUNC; 0197 0198 QIODevice *dev = (static_cast<KIso *>(udata))->device(); 0199 0200 // seek(0) ensures integrity with the QIODevice's built-in buffer 0201 // see bug #372023 for details 0202 dev->seek(0); 0203 0204 if (dev->seek((qint64)start << (qint64)11)) { 0205 if ((dev->read(buf, len << 11u)) != -1) 0206 return (len); 0207 } 0208 // qDebug() << "KIso::ReadRequest failed start: " << start << " len: " << len << endl; 0209 0210 return -1; 0211 } 0212 0213 /* callback function for libisofs */ 0214 static int mycallb(struct iso_directory_record *idr, void *udata) 0215 { 0216 KRFUNC; 0217 0218 auto *iso = static_cast<KIso *>(udata); 0219 QString path, user, group, symlink; 0220 int i; 0221 int access; 0222 time_t time, cdate, adate; 0223 rr_entry rr; 0224 bool special = false; 0225 KArchiveEntry *entry = nullptr, *oldentry = nullptr; 0226 char z_algo[2], z_params[2]; 0227 long long z_size = 0; 0228 0229 if ((idr->flags[0] & 1) && !iso->showhidden) 0230 return 0; 0231 if (iso->level) { 0232 if (isonum_711(idr->name_len) == 1) { 0233 switch (idr->name[0]) { 0234 case 0: 0235 path += ("."); 0236 special = true; 0237 break; 0238 case 1: 0239 path += (".."); 0240 special = true; 0241 break; 0242 } 0243 } 0244 if (iso->showrr && ParseRR(idr, &rr) > 0) { 0245 if (!special) 0246 path = rr.name; 0247 symlink = rr.sl; 0248 access = rr.mode; 0249 time = rr.t_mtime; 0250 adate = rr.t_atime; 0251 cdate = rr.t_ctime; 0252 user.setNum(rr.uid); 0253 group.setNum(rr.gid); 0254 z_algo[0] = rr.z_algo[0]; 0255 z_algo[1] = rr.z_algo[1]; 0256 z_params[0] = rr.z_params[0]; 0257 z_params[1] = rr.z_params[1]; 0258 z_size = rr.z_size; 0259 } else { 0260 access = iso->dirent->permissions() & ~S_IFMT; 0261 adate = cdate = time = isodate_915(idr->date, 0); 0262 user = iso->dirent->user(); 0263 group = iso->dirent->group(); 0264 if (idr->flags[0] & 2) 0265 access |= S_IFDIR; 0266 else 0267 access |= S_IFREG; 0268 if (!special) { 0269 if (iso->joliet) { 0270 for (i = 0; i < (isonum_711(idr->name_len) - 1); i += 2) { 0271 void *p = &(idr->name[i]); 0272 QChar ch(be2me_16(*(ushort *)p)); 0273 if (ch == ';') 0274 break; 0275 path += ch; 0276 } 0277 } else { 0278 for (i = 0; i < isonum_711(idr->name_len); ++i) { 0279 if (idr->name[i] == ';') 0280 break; 0281 if (idr->name[i]) 0282 path += (idr->name[i]); 0283 } 0284 } 0285 if (path.endsWith('.')) 0286 path.resize(path.length() - 1); 0287 } 0288 } 0289 if (iso->showrr) 0290 FreeRR(&rr); 0291 if (idr->flags[0] & 2) { 0292 entry = new KIsoDirectory(iso, path, access | S_IFDIR, time, adate, cdate, user, group, symlink); 0293 } else { 0294 entry = new KIsoFile(iso, 0295 path, 0296 access, 0297 time, 0298 adate, 0299 cdate, 0300 user, 0301 group, 0302 symlink, 0303 (long long)(isonum_733(idr->extent)) << (long long)11, 0304 isonum_733(idr->size)); 0305 if (z_size) 0306 (dynamic_cast<KIsoFile *>(entry))->setZF(z_algo, z_params, z_size); 0307 } 0308 iso->dirent->addEntry(entry); 0309 } 0310 if ((idr->flags[0] & 2) && (iso->level == 0 || !special)) { 0311 if (iso->level) { 0312 oldentry = iso->dirent; 0313 iso->dirent = dynamic_cast<KIsoDirectory *>(entry); 0314 } 0315 iso->level++; 0316 ProcessDir(&readf, isonum_733(idr->extent), isonum_733(idr->size), &mycallb, udata); 0317 iso->level--; 0318 if (iso->level) 0319 iso->dirent = dynamic_cast<KIsoDirectory *>(oldentry); 0320 } 0321 return 0; 0322 } 0323 0324 void KIso::addBoot(struct el_torito_boot_descriptor *bootdesc) 0325 { 0326 KRFUNC; 0327 0328 int i; 0329 long long size; 0330 boot_head boot; 0331 boot_entry *be; 0332 QString path; 0333 KIsoFile *entry; 0334 0335 path = "Catalog"; 0336 entry = new KIsoFile(this, 0337 path, 0338 dirent->permissions() & ~S_IFDIR, 0339 dirent->date(), 0340 dirent->adate(), 0341 dirent->cdate(), 0342 dirent->user(), 0343 dirent->group(), 0344 QString(), 0345 (long long)isonum_731(bootdesc->boot_catalog) << (long long)11, 0346 (long long)2048); 0347 dirent->addEntry(entry); 0348 if (!ReadBootTable(&readf, isonum_731(bootdesc->boot_catalog), &boot, this)) { 0349 i = 1; 0350 be = boot.defentry; 0351 while (be) { 0352 size = BootImageSize(isonum_711(be->data.d_e.media), isonum_721(be->data.d_e.seccount)); 0353 path = "Default Image"; 0354 if (i > 1) 0355 path += " (" + QString::number(i) + ')'; 0356 entry = new KIsoFile(this, 0357 path, 0358 dirent->permissions() & ~S_IFDIR, 0359 dirent->date(), 0360 dirent->adate(), 0361 dirent->cdate(), 0362 dirent->user(), 0363 dirent->group(), 0364 QString(), 0365 (long long)isonum_731(be->data.d_e.start) << (long long)11, 0366 size << (long long)9); 0367 dirent->addEntry(entry); 0368 be = be->next; 0369 i++; 0370 } 0371 0372 FreeBootTable(&boot); 0373 } 0374 } 0375 0376 void KIso::readParams() 0377 { 0378 KRFUNC; 0379 KConfig *config; 0380 0381 config = new KConfig("kio_isorc"); 0382 0383 KConfigGroup group(config, QString()); 0384 showhidden = group.readEntry("showhidden", false); 0385 showrr = group.readEntry("showrr", true); 0386 delete config; 0387 } 0388 0389 bool KIso::openArchive(QIODevice::OpenMode mode) 0390 { 0391 KRFUNC; 0392 iso_vol_desc *desc; 0393 QString path, uid, gid; 0394 QT_STATBUF buf; 0395 int tracks[2 * 100], trackno = 0, i, access, c_b, c_i, c_j; 0396 KArchiveDirectory *root; 0397 struct iso_directory_record *idr; 0398 struct el_torito_boot_descriptor *bootdesc; 0399 0400 if (mode == QIODevice::WriteOnly) 0401 return false; 0402 0403 readParams(); 0404 d->dirList.clear(); 0405 0406 tracks[0] = 0; 0407 if (m_startsec > 0) 0408 tracks[0] = m_startsec; 0409 // qDebug() << " m_startsec: " << m_startsec << endl; 0410 /* We'll use the permission and user/group of the 'host' file except 0411 * in Rock Ridge, where the permissions are stored on the file system 0412 */ 0413 if (QT_STAT(m_filename.toLocal8Bit(), &buf) < 0) { 0414 /* defaults, if stat fails */ 0415 memset(&buf, 0, sizeof(struct stat)); 0416 buf.st_mode = 0777; 0417 } else { 0418 /* If it's a block device, try to query the track layout (for multisession) */ 0419 if (m_startsec == -1 && S_ISBLK(buf.st_mode)) 0420 trackno = getTracks(m_filename.toLatin1(), (int *)&tracks); 0421 } 0422 uid.setNum(buf.st_uid); 0423 gid.setNum(buf.st_gid); 0424 access = buf.st_mode & ~S_IFMT; 0425 0426 root = new KIsoDirectory(this, QStringLiteral("/"), 0777 | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); 0427 setRootDir(root); 0428 0429 // qDebug() << "KIso::openArchive number of tracks: " << trackno << endl; 0430 0431 if (trackno == 0) 0432 trackno = 1; 0433 for (i = 0; i < trackno; ++i) { 0434 c_b = 1; 0435 c_i = 1; 0436 c_j = 1; 0437 root = rootDir(); 0438 if (trackno > 1) { 0439 path.clear(); 0440 QTextStream(&path) << "Track " << tracks[(i << 1) + 1]; 0441 root = new KIsoDirectory(this, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); 0442 rootDir()->addEntry(root); 0443 } 0444 0445 desc = ReadISO9660(&readf, tracks[i << 1], this); 0446 if (!desc) { 0447 // qDebug() << "KIso::openArchive no volume descriptors" << endl; 0448 continue; 0449 } 0450 0451 while (desc) { 0452 switch (isonum_711(desc->data.type)) { 0453 case ISO_VD_BOOT: 0454 0455 bootdesc = (struct el_torito_boot_descriptor *)&(desc->data); 0456 if (!memcmp(EL_TORITO_ID, bootdesc->system_id, ISODCL(8, 39))) { 0457 path = "El Torito Boot"; 0458 if (c_b > 1) 0459 path += " (" + QString::number(c_b) + ')'; 0460 0461 dirent = new KIsoDirectory(this, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); 0462 root->addEntry(dirent); 0463 0464 addBoot(bootdesc); 0465 c_b++; 0466 } 0467 break; 0468 0469 case ISO_VD_PRIMARY: 0470 case ISO_VD_SUPPLEMENTARY: 0471 idr = (struct iso_directory_record *)&(((struct iso_primary_descriptor *)&desc->data)->root_directory_record); 0472 joliet = JolietLevel(&desc->data); 0473 if (joliet) { 0474 QTextStream(&path) << "Joliet level " << joliet; 0475 if (c_j > 1) 0476 path += " (" + QString::number(c_j) + ')'; 0477 } else { 0478 path = "ISO9660"; 0479 if (c_i > 1) 0480 path += " (" + QString::number(c_i) + ')'; 0481 } 0482 dirent = new KIsoDirectory(this, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); 0483 root->addEntry(dirent); 0484 level = 0; 0485 mycallb(idr, this); 0486 if (joliet) 0487 c_j++; 0488 else 0489 c_i++; 0490 break; 0491 } 0492 desc = desc->next; 0493 } 0494 free(desc); 0495 } 0496 device()->close(); 0497 return true; 0498 } 0499 0500 bool KIso::closeArchive() 0501 { 0502 KRFUNC; 0503 d->dirList.clear(); 0504 return true; 0505 } 0506 0507 bool KIso::writeDir(const QString &, const QString &, const QString &, mode_t, time_t, time_t, time_t) 0508 { 0509 return false; 0510 } 0511 0512 bool KIso::prepareWriting(const QString &, const QString &, const QString &, qint64, mode_t, time_t, time_t, time_t) 0513 { 0514 return false; 0515 } 0516 0517 bool KIso::finishWriting(qint64) 0518 { 0519 return false; 0520 } 0521 0522 bool KIso::writeSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, time_t, time_t, time_t) 0523 { 0524 return false; 0525 } 0526 0527 bool KIso::doWriteDir(const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 0528 { 0529 return false; 0530 } 0531 0532 bool KIso::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 0533 { 0534 return false; 0535 } 0536 0537 bool KIso::doPrepareWriting(const QString &, const QString &, const QString &, qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 0538 { 0539 return false; 0540 } 0541 0542 bool KIso::doFinishWriting(qint64) 0543 { 0544 return false; 0545 } 0546 0547 void KIso::virtual_hook(int id, void *data) 0548 { 0549 KArchive::virtual_hook(id, data); 0550 }