|
|
/***************************************************************************
|
|
|
kiso.cpp
|
|
|
-------------------
|
|
|
begin : Oct 25 2002
|
|
|
copyright : (C) 2002 by Szombathelyi Gy<47>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 tdelibs (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 <tqcstring.h>
|
|
|
#include <tqdir.h>
|
|
|
#include <tqfile.h>
|
|
|
#include <kdebug.h>
|
|
|
#include <kurl.h>
|
|
|
#include <kmimetype.h>
|
|
|
#include <tdeconfig.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() {}
|
|
|
TQStringList dirList;
|
|
|
};
|
|
|
|
|
|
KIso::KIso( const TQString& filename, const TQString & _mimetype )
|
|
|
: KArchive( 0L )
|
|
|
{
|
|
|
m_startsec = -1;
|
|
|
m_filename = filename;
|
|
|
d = new KIsoPrivate;
|
|
|
TQString 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)
|
|
|
TQFile 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 TQString & filename,
|
|
|
const TQString & mimetype, bool forced )
|
|
|
{
|
|
|
/* 'hack' for TQt'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;
|
|
|
|
|
|
TQIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced );
|
|
|
if( dev )
|
|
|
setDevice( dev );
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
KIso::KIso( TQIODevice * 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) {
|
|
|
|
|
|
TQIODevice* 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);
|
|
|
TQString 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) {
|
|
|
TQChar 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;
|
|
|
TQString path;
|
|
|
KIsoFile *entry;
|
|
|
|
|
|
entry=new KIsoFile( this, "Catalog", dirent->permissions() & ~S_IFDIR,
|
|
|
dirent->date(), dirent->adate(), dirent->cdate(),
|
|
|
dirent->user(), dirent->group(), TQString(),
|
|
|
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 += " (" + TQString::number(i) + ")";
|
|
|
entry=new KIsoFile( this, path, dirent->permissions() & ~S_IFDIR,
|
|
|
dirent->date(), dirent->adate(), dirent->cdate(),
|
|
|
dirent->user(), dirent->group(), TQString(),
|
|
|
isonum_731(((struct default_entry*) be->data)->start)<<11, size<<9 );
|
|
|
dirent->addEntry(entry);
|
|
|
be=be->next;
|
|
|
i++;
|
|
|
}
|
|
|
|
|
|
FreeBootTable(&boot);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void KIso::readParams()
|
|
|
{
|
|
|
TDEConfig *config;
|
|
|
|
|
|
config = new TDEConfig("tdeio_isorc");
|
|
|
|
|
|
showhidden=config->readBoolEntry("showhidden",false);
|
|
|
showrr=config->readBoolEntry("showrr",true);
|
|
|
delete config;
|
|
|
}
|
|
|
|
|
|
bool KIso::openArchive( int mode )
|
|
|
{
|
|
|
iso_vol_desc *desc;
|
|
|
TQString 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=TQString();
|
|
|
TQTextOStream(&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, TQString() );
|
|
|
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 += " (" + TQString::number(c_b) + ")";
|
|
|
|
|
|
dirent = new KIsoDirectory( this, path, access | S_IFDIR,
|
|
|
buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, TQString() );
|
|
|
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) {
|
|
|
TQTextOStream(&path) << "Joliet level " << joliet;
|
|
|
if (c_j>1) path += " (" + TQString::number(c_j) + ")";
|
|
|
} else {
|
|
|
path = "ISO9660";
|
|
|
if (c_i>1) path += " (" + TQString::number(c_i) + ")";
|
|
|
}
|
|
|
dirent = new KIsoDirectory( this, path, access | S_IFDIR,
|
|
|
buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, TQString() );
|
|
|
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 TQString&, const TQString&, const TQString& )
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
bool KIso::prepareWriting( const TQString&, const TQString&, const TQString&, uint)
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
bool KIso::doneWriting( uint )
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
void KIso::virtual_hook( int id, void* data )
|
|
|
{ KArchive::virtual_hook( id, data ); }
|
|
|
|