diff options
Diffstat (limited to 'kioslave/iso/kiso.cpp')
-rw-r--r-- | kioslave/iso/kiso.cpp | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/kioslave/iso/kiso.cpp b/kioslave/iso/kiso.cpp new file mode 100644 index 000000000..6bc4241fe --- /dev/null +++ b/kioslave/iso/kiso.cpp @@ -0,0 +1,459 @@ +/*************************************************************************** + kiso.cpp + ------------------- + begin : Oct 25 2002 + copyright : (C) 2002 by Szombathelyi Gy�gy + email : gyurco@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + /* This file is heavily based on ktar.cpp from kdelibs (c) David Faure */ + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <grp.h> +#include <pwd.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <qcstring.h> +#include <qdir.h> +#include <qfile.h> +#include <kdebug.h> +#include <kurl.h> +#include <kmimetype.h> +#include <kconfig.h> +#include <kfilterdev.h> +#include <kfilterbase.h> + +#include "kiso.h" +#include "libisofs/isofs.h" +#include "qfilehack.h" + + +#ifdef __linux__ +#undef __STRICT_ANSI__ +#include <linux/cdrom.h> +#define __STRICT_ANSI__ +#include <sys/ioctl.h> +#include <fcntl.h> +#endif + +//////////////////////////////////////////////////////////////////////// +/////////////////////////// KIso /////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +/** + * puts the track layout of the device 'fname' into 'tracks' + * tracks structure: start sector, track number, ... + * tracks should be 100*2 entry long (this is the maximum in the CD-ROM standard) + * currently it's linux only, porters are welcome + */ +static int getTracks(const char *fname,int *tracks) { + int ret=0; + memset(tracks,0,200*sizeof(int)); + +#ifdef __linux__ + int fd,i; + struct cdrom_tochdr tochead; + struct cdrom_tocentry tocentry; + + kdDebug() << "getTracks open:" << fname << endl; + fd=open(fname, O_RDONLY | O_NONBLOCK); + if (fd > 0) { + if (ioctl(fd,CDROMREADTOCHDR,&tochead)!=-1) { + kdDebug() << "getTracks first track:" << tochead.cdth_trk0 + << " last track " << tochead.cdth_trk1 << endl; + for (i=tochead.cdth_trk0;i<=tochead.cdth_trk1;i++) { + if (ret>99) break; + memset(&tocentry,0,sizeof(struct cdrom_tocentry)); + tocentry.cdte_track=i; + tocentry.cdte_format=CDROM_LBA; + if (ioctl(fd,CDROMREADTOCENTRY,&tocentry)<0) break; + kdDebug() << "getTracks got track " << i << " starting at: " << + tocentry.cdte_addr.lba << endl; + if ((tocentry.cdte_ctrl & 0x4) == 0x4) { + tracks[ret<<1]=tocentry.cdte_addr.lba; + tracks[(ret<<1)+1]=i; + ret++; + } + } + } + close(fd); + } + +#endif + + return ret; +} + +class KIso::KIsoPrivate +{ +public: + KIsoPrivate() {} + QStringList dirList; +}; + +KIso::KIso( const QString& filename, const QString & _mimetype ) + : KArchive( 0L ) +{ + m_startsec = -1; + m_filename = filename; + d = new KIsoPrivate; + QString mimetype( _mimetype ); + bool forced = true; + if ( mimetype.isEmpty() ) + { + mimetype = KMimeType::findByFileContent( filename )->name(); + kdDebug() << "KIso::KIso mimetype=" << mimetype << endl; + + // Don't move to prepareDevice - the other constructor theoretically allows ANY filter + if ( mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around + mimetype == "application/x-webarchive" ) + // that's a gzipped tar file, so ask for gzip filter + mimetype = "application/x-gzip"; + else if ( mimetype == "application/x-tbz" ) // that's a bzipped2 tar file, so ask for bz2 filter + mimetype = "application/x-bzip2"; + else + { + // Something else. Check if it's not really gzip though (e.g. for KOffice docs) + QFile file( filename ); + if ( file.open( IO_ReadOnly ) ) + { + unsigned char firstByte = file.getch(); + unsigned char secondByte = file.getch(); + unsigned char thirdByte = file.getch(); + if ( firstByte == 0037 && secondByte == 0213 ) + mimetype = "application/x-gzip"; + else if ( firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h' ) + mimetype = "application/x-bzip2"; + else if ( firstByte == 'P' && secondByte == 'K' && thirdByte == 3 ) + { + unsigned char fourthByte = file.getch(); + if ( fourthByte == 4 ) + mimetype = "application/x-zip"; + } + } + } + forced = false; + } + + prepareDevice( filename, mimetype, forced ); +} + +void KIso::prepareDevice( const QString & filename, + const QString & mimetype, bool forced ) +{ + /* 'hack' for Qt's false assumption that only S_ISREG is seekable */ + if( "inode/blockdevice" == mimetype ) + setDevice( new QFileHack( filename ) ); + else + { + if( "application/x-gzip" == mimetype + || "application/x-bzip2" == mimetype) + forced = true; + + QIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced ); + if( dev ) + setDevice( dev ); + } + +} + +KIso::KIso( QIODevice * dev ) + : KArchive( dev ) +{ + d = new KIsoPrivate; +} + +KIso::~KIso() +{ + // mjarrett: Closes to prevent ~KArchive from aborting w/o device + if( isOpened() ) + close(); + if ( !m_filename.isEmpty() ) + delete device(); // we created it ourselves + delete d; +} + +/* callback function for libisofs */ +static int readf(char *buf, int start, int len,void *udata) { + + QIODevice* dev = ( static_cast<KIso*> (udata) )->device(); + + if (dev->at(start<<11)) { + if ((dev->readBlock(buf, len<<11)) != -1) return (len); + } + kdDebug() << "KIso::ReadRequest failed start: " << start << " len: " << len << endl; + + return -1; +} + +/* callback function for libisofs */ +static int mycallb(struct iso_directory_record *idr,void *udata) { + + KIso *iso = static_cast<KIso*> (udata); + QString path,user,group,symlink; + int i; + int access; + int time,cdate,adate; + rr_entry rr; + bool special=false; + KArchiveEntry *entry=NULL,*oldentry=NULL; + char z_algo[2],z_params[2]; + int z_size=0; + + if ((idr->flags[0] & 1) && !iso->showhidden) return 0; + if (iso->level) { + if (isonum_711(idr->name_len)==1) { + switch (idr->name[0]) { + case 0: + path+=("."); + special=true; + break; + case 1: + path+=(".."); + special=true; + break; + } + } + if (iso->showrr && ParseRR(idr,&rr)>0) { + if (!special) path=rr.name; + symlink=rr.sl; + access=rr.mode; + time=rr.t_mtime; + adate=rr.t_atime; + cdate=rr.t_ctime; + user.setNum(rr.uid); + group.setNum(rr.gid); + z_algo[0]=rr.z_algo[0];z_algo[1]=rr.z_algo[1]; + z_params[0]=rr.z_params[0];z_params[1]=rr.z_params[1]; + z_size=rr.z_size; + } else { + access=iso->dirent->permissions() & ~S_IFMT; + adate=cdate=time=isodate_915(idr->date,0); + user=iso->dirent->user(); + group=iso->dirent->group(); + if (idr->flags[0] & 2) access |= S_IFDIR; else access |= S_IFREG; + if (!special) { + if (iso->joliet) { + for (i=0;i<(isonum_711(idr->name_len)-1);i+=2) { + QChar ch( be2me_16(*((ushort*)&(idr->name[i]))) ); + if (ch==';') break; + path+=ch; + } + } else { + for (i=0;i<isonum_711(idr->name_len);i++) { + if (idr->name[i]==';') break; + if (idr->name[i]) path+=(idr->name[i]); + } + } + if (path.endsWith(".")) path.setLength(path.length()-1); + } + } + if (iso->showrr) FreeRR(&rr); + if (idr->flags[0] & 2) { + entry = new KIsoDirectory( iso, path, access | S_IFDIR, time, adate, cdate, + user, group, symlink ); + } else { + entry = new KIsoFile( iso, path, access, time, adate, cdate, + user, group, symlink, isonum_733(idr->extent)<<11,isonum_733(idr->size) ); + if (z_size) (static_cast <KIsoFile*> (entry))->setZF(z_algo,z_params,z_size); + + } + iso->dirent->addEntry(entry); + } + if ( (idr->flags[0] & 2) && (iso->level==0 || !special) ) { + if (iso->level) { + oldentry=iso->dirent; + iso->dirent=static_cast<KIsoDirectory*> (entry); + } + iso->level++; + ProcessDir(&readf,isonum_733(idr->extent),isonum_733(idr->size),&mycallb,udata); + iso->level--; + if (iso->level) iso->dirent=static_cast<KIsoDirectory*> (oldentry); + } + return 0; +} + +void KIso::addBoot(struct el_torito_boot_descriptor* bootdesc) { + + int i,size; + boot_head boot; + boot_entry *be; + QString path; + KIsoFile *entry; + + entry=new KIsoFile( this, "Catalog", dirent->permissions() & ~S_IFDIR, + dirent->date(), dirent->adate(), dirent->cdate(), + dirent->user(), dirent->group(), QString::null, + isonum_731(bootdesc->boot_catalog)<<11, 2048 ); + dirent->addEntry(entry); + if (!ReadBootTable(&readf,isonum_731(bootdesc->boot_catalog),&boot,this)) { + i=1; + be=boot.defentry; + while (be) { + size=BootImageSize( isonum_711(((struct default_entry*) be->data)->media), + isonum_721(((struct default_entry*) be->data)->seccount)); + path="Default Image"; + if (i>1) path += " (" + QString::number(i) + ")"; + entry=new KIsoFile( this, path, dirent->permissions() & ~S_IFDIR, + dirent->date(), dirent->adate(), dirent->cdate(), + dirent->user(), dirent->group(), QString::null, + isonum_731(((struct default_entry*) be->data)->start)<<11, size<<9 ); + dirent->addEntry(entry); + be=be->next; + i++; + } + + FreeBootTable(&boot); + } +} + +void KIso::readParams() +{ + KConfig *config; + + config = new KConfig("kio_isorc"); + + showhidden=config->readBoolEntry("showhidden",false); + showrr=config->readBoolEntry("showrr",true); + delete config; +} + +bool KIso::openArchive( int mode ) +{ + iso_vol_desc *desc; + QString path,tmp,uid,gid; + struct stat buf; + int tracks[2*100],trackno=0,i,access,c_b,c_i,c_j; + KArchiveDirectory *root; + struct iso_directory_record* idr; + struct el_torito_boot_descriptor* bootdesc; + + if ( mode == IO_WriteOnly ) + return false; + + readParams(); + d->dirList.clear(); + + tracks[0]=0; + if (m_startsec>0) tracks[0]=m_startsec; + kdDebug() << " m_startsec: " << m_startsec << endl; + /* We'll use the permission and user/group of the 'host' file except + * in Rock Ridge, where the permissions are stored on the file system + */ + if (::stat( m_filename.local8Bit(), &buf )<0) { + /* defaults, if stat fails */ + memset(&buf,0,sizeof(struct stat)); + buf.st_mode=0777; + } else { + /* If it's a block device, try to query the track layout (for multisession) */ + if (m_startsec == -1 && S_ISBLK(buf.st_mode)) + trackno=getTracks(m_filename.latin1(),(int*) &tracks); + } + uid.setNum(buf.st_uid); + gid.setNum(buf.st_gid); + access = buf.st_mode & ~S_IFMT; + + kdDebug() << "KIso::openArchive number of tracks: " << trackno << endl; + + if (trackno==0) trackno=1; + for (i=0;i<trackno;i++) { + + c_b=1;c_i=1;c_j=1; + root=rootDir(); + if (trackno>1) { + path=QString::null; + QTextOStream(&path) << "Track " << tracks[(i<<1)+1]; + root = new KIsoDirectory( this, path, access | S_IFDIR, + buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString::null ); + rootDir()->addEntry(root); + } + + desc=ReadISO9660(&readf,tracks[i<<1],this); + if (!desc) { + kdDebug() << "KIso::openArchive no volume descriptors" << endl; + continue; + } + + while (desc) { + switch (isonum_711(desc->data.type)) { + case ISO_VD_BOOT: + + bootdesc=(struct el_torito_boot_descriptor*) &(desc->data); + if ( !memcmp(EL_TORITO_ID,bootdesc->system_id,ISODCL(8,39)) ) { + path="El Torito Boot"; + if (c_b>1) path += " (" + QString::number(c_b) + ")"; + + dirent = new KIsoDirectory( this, path, access | S_IFDIR, + buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString::null ); + root->addEntry(dirent); + + addBoot(bootdesc); + c_b++; + } + break; + + case ISO_VD_PRIMARY: + case ISO_VD_SUPPLEMENTARY: + idr=(struct iso_directory_record*) &( ((struct iso_primary_descriptor*) &desc->data)->root_directory_record); + joliet = JolietLevel(&desc->data); + if (joliet) { + QTextOStream(&path) << "Joliet level " << joliet; + if (c_j>1) path += " (" + QString::number(c_j) + ")"; + } else { + path = "ISO9660"; + if (c_i>1) path += " (" + QString::number(c_i) + ")"; + } + dirent = new KIsoDirectory( this, path, access | S_IFDIR, + buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString::null ); + root->addEntry(dirent); + level=0; + mycallb(idr, this ); + if (joliet) c_j++; else c_i++; + break; + } + desc=desc->next; + } + free(desc); + } + device()->close(); + return true; +} + +bool KIso::closeArchive() +{ + d->dirList.clear(); + return true; +} + +bool KIso::writeDir( const QString&, const QString&, const QString& ) +{ + return false; +} + +bool KIso::prepareWriting( const QString&, const QString&, const QString&, uint) +{ + return false; +} + +bool KIso::doneWriting( uint ) +{ + return false; +} + +void KIso::virtual_hook( int id, void* data ) +{ KArchive::virtual_hook( id, data ); } + |