diff options
Diffstat (limited to 'tdeio/tdeio/ktar.cpp')
-rw-r--r-- | tdeio/tdeio/ktar.cpp | 980 |
1 files changed, 980 insertions, 0 deletions
diff --git a/tdeio/tdeio/ktar.cpp b/tdeio/tdeio/ktar.cpp new file mode 100644 index 000000000..9bde2873a --- /dev/null +++ b/tdeio/tdeio/ktar.cpp @@ -0,0 +1,980 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 David Faure <faure@kde.org> + Copyright (C) 2003 Leo Savernik <l.savernik@aon.at> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//#include <stdio.h> +#include <stdlib.h> // strtol +#include <time.h> // time() +/*#include <unistd.h> +#include <grp.h> +#include <pwd.h>*/ +#include <assert.h> + +#include <tqcstring.h> +#include <tqdir.h> +#include <tqfile.h> +#include <kdebug.h> +#include <kmimetype.h> +#include <ktempfile.h> + +#include <kfilterdev.h> +#include <kfilterbase.h> + +#include "ktar.h" +#include <kstandarddirs.h> + +//////////////////////////////////////////////////////////////////////// +/////////////////////////// KTar /////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +class KTar::KTarPrivate +{ +public: + KTarPrivate() : tarEnd( 0 ), tmpFile( 0 ) {} + TQStringList dirList; + int tarEnd; + KTempFile* tmpFile; + TQString mimetype; + TQCString origFileName; + + bool fillTempFile(const TQString & filename); + bool writeBackTempFile( const TQString & filename ); +}; + +KTar::KTar( const TQString& filename, const TQString & _mimetype ) + : KArchive( 0 ) +{ + m_filename = filename; + d = new KTarPrivate; + TQString mimetype( _mimetype ); + bool forced = true; + if ( mimetype.isEmpty() ) // Find out mimetype manually + { + if ( TQFile::exists( filename ) ) + mimetype = KMimeType::findByFileContent( filename )->name(); + else + mimetype = KMimeType::findByPath( filename, 0, true )->name(); + kdDebug(7041) << "KTar::KTar 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"; + } + else if ( firstByte == 0xfd && secondByte == '7' && thirdByte == 'z' ) + { + unsigned char fourthByte = file.getch(); + unsigned char fifthByte = file.getch(); + unsigned char sixthByte = file.getch(); + if ( fourthByte == 'X' && fifthByte == 'Z' && sixthByte == 0x00 ) + mimetype = "application/x-xz"; + } + else if ( firstByte == 0x5d && secondByte == 0x00 && thirdByte == 0x00 ) + { + unsigned char fourthByte = file.getch(); + if ( fourthByte == 0x80 ) + mimetype = "application/x-lzma"; + } + } + file.close(); + } + forced = false; + } + d->mimetype = mimetype; + + prepareDevice( filename, mimetype, forced ); +} + +void KTar::prepareDevice( const TQString & filename, + const TQString & mimetype, bool /*forced*/ ) +{ + if( "application/x-tar" == mimetype ) + setDevice( TQT_TQIODEVICE(new TQFile( filename )) ); + else + { + // The compression filters are very slow with random access. + // So instead of applying the filter to the device, + // the file is completly extracted instead, + // and we work on the extracted tar file. + // This improves the extraction speed by the tar ioslave dramatically, + // if the archive file contains many files. + // This is because the tar ioslave extracts one file after the other and normally + // has to walk through the decompression filter each time. + // Which is in fact nearly as slow as a complete decompression for each file. + d->tmpFile = new KTempFile(locateLocal("tmp", "ktar-"),".tar"); + kdDebug( 7041 ) << "KTar::prepareDevice creating TempFile: " << d->tmpFile->name() << endl; + d->tmpFile->setAutoDelete(true); + + // KTempFile opens the file automatically, + // the device must be closed, however, for KArchive.setDevice() + TQFile* file = d->tmpFile->file(); + file->close(); + setDevice(TQT_TQIODEVICE(file)); + } +} + +KTar::KTar( TQIODevice * dev ) + : KArchive( dev ) +{ + Q_ASSERT( dev ); + d = new KTarPrivate; +} + +KTar::~KTar() +{ + // mjarrett: Closes to prevent ~KArchive from aborting w/o device + if( isOpened() ) + close(); + + if (d->tmpFile) + delete d->tmpFile; // will delete the device + else if ( !m_filename.isEmpty() ) + delete device(); // we created it ourselves + + + delete d; +} + +void KTar::setOrigFileName( const TQCString & fileName ) +{ + if ( !isOpened() || !(mode() & IO_WriteOnly) ) + { + kdWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n"; + return; + } + d->origFileName = fileName; +} + +TQ_LONG KTar::readRawHeader(char *buffer) { + // Read header + TQ_LONG n = device()->readBlock( buffer, 0x200 ); + if ( n == 0x200 && buffer[0] != 0 ) { + // Make sure this is actually a tar header + if (strncmp(buffer + 257, "ustar", 5)) { + // The magic isn't there (broken/old tars), but maybe a correct checksum? + TQCString s; + + int check = 0; + for( uint j = 0; j < 0x200; ++j ) + check += buffer[j]; + + // adjust checksum to count the checksum fields as blanks + for( uint j = 0; j < 8 /*size of the checksum field including the \0 and the space*/; j++ ) + check -= buffer[148 + j]; + check += 8 * ' '; + + s.sprintf("%o", check ); + + // only compare those of the 6 checksum digits that mean something, + // because the other digits are filled with all sorts of different chars by different tars ... + // Some tars right-justify the checksum so it could start in one of three places - we have to check each. + if( strncmp( buffer + 148 + 6 - s.length(), s.data(), s.length() ) + && strncmp( buffer + 148 + 7 - s.length(), s.data(), s.length() ) + && strncmp( buffer + 148 + 8 - s.length(), s.data(), s.length() ) ) { + kdWarning(7041) << "KTar: invalid TAR file. Header is: " << TQCString( buffer+257, 5 ) << endl; + return -1; + } + }/*end if*/ + } else { + // reset to 0 if 0x200 because logical end of archive has been reached + if (n == 0x200) n = 0; + }/*end if*/ + return n; +} + +bool KTar::readLonglink(char *buffer,TQCString &longlink) { + TQ_LONG n = 0; + TQIODevice *dev = device(); + // read size of longlink from size field in header + // size is in bytes including the trailing null (which we ignore) + buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437 + char *dummy; + const char* p = buffer + 0x7c; + while( *p == ' ' ) ++p; + int size = (int)strtol( p, &dummy, 8 ); + + longlink.resize(size); + size--; // ignore trailing null + dummy = longlink.data(); + int offset = 0; + while (size > 0) { + int chunksize = QMIN(size, 0x200); + n = dev->readBlock( dummy + offset, chunksize ); + if (n == -1) return false; + size -= chunksize; + offset += 0x200; + }/*wend*/ + // jump over the rest + int skip = 0x200 - (n % 0x200); + if (skip < 0x200) { + if (dev->readBlock(buffer,skip) != skip) return false; + } + return true; +} + +TQ_LONG KTar::readHeader(char *buffer,TQString &name,TQString &symlink) { + name.truncate(0); + symlink.truncate(0); + while (true) { + TQ_LONG n = readRawHeader(buffer); + if (n != 0x200) return n; + + // is it a longlink? + if (strcmp(buffer,"././@LongLink") == 0) { + char typeflag = buffer[0x9c]; + TQCString longlink; + readLonglink(buffer,longlink); + switch (typeflag) { + case 'L': name = TQFile::decodeName(longlink); break; + case 'K': symlink = TQFile::decodeName(longlink); break; + }/*end switch*/ + } else { + break; + }/*end if*/ + }/*wend*/ + + // if not result of longlink, read names directly from the header + if (name.isEmpty()) + // there are names that are exactly 100 bytes long + // and neither longlink nor \0 terminated (bug:101472) + name = TQFile::decodeName(TQCString(buffer, 101)); + if (symlink.isEmpty()) + symlink = TQFile::decodeName(TQCString(buffer + 0x9d, 101)); + + return 0x200; +} + +/* + * If we have created a temporary file, we have + * to decompress the original file now and write + * the contents to the temporary file. + */ +bool KTar::KTarPrivate::fillTempFile( const TQString & filename) { + if ( ! tmpFile ) + return true; + + kdDebug( 7041 ) << + "KTar::openArchive: filling tmpFile of mimetype '" << mimetype << + "' ... " << endl; + + bool forced = false; + if( "application/x-gzip" == mimetype + || "application/x-bzip2" == mimetype + || "application/x-lzma" == mimetype + || "application/x-xz" == mimetype) + forced = true; + + TQIODevice *filterDev = KFilterDev::deviceForFile( filename, mimetype, forced ); + + if( filterDev ) { + TQFile* file = tmpFile->file(); + file->close(); + if ( ! file->open( IO_WriteOnly ) ) + { + delete filterDev; + return false; + } + TQByteArray buffer(8*1024); + if ( ! filterDev->open( IO_ReadOnly ) ) + { + delete filterDev; + return false; + } + TQ_LONG len = -1; + while ( !filterDev->atEnd() && len != 0) { + len = filterDev->readBlock(buffer.data(),buffer.size()); + if ( len < 0 ) { // corrupted archive + delete filterDev; + return false; + } + file->writeBlock(buffer.data(),len); + } + filterDev->close(); + delete filterDev; + + file->close(); + if ( ! file->open( IO_ReadOnly ) ) + return false; + } + else + kdDebug( 7041 ) << "KTar::openArchive: no filterdevice found!" << endl; + + kdDebug( 7041 ) << "KTar::openArchive: filling tmpFile finished." << endl; + return true; +} + +bool KTar::openArchive( int mode ) +{ + kdDebug( 7041 ) << "KTar::openArchive" << endl; + if ( !(mode & IO_ReadOnly) ) + return true; + + if ( !d->fillTempFile( m_filename ) ) + return false; + + // We'll use the permission and user/group of d->rootDir + // for any directory we emulate (see findOrCreate) + //struct stat buf; + //stat( m_filename, &buf ); + + d->dirList.clear(); + TQIODevice* dev = device(); + + if ( !dev ) + return false; + + // read dir infos + char buffer[ 0x200 ]; + bool ende = false; + do + { + TQString name; + TQString symlink; + + // Read header + TQ_LONG n = readHeader(buffer,name,symlink); + if (n < 0) return false; + if (n == 0x200) + { + bool isdir = false; + TQString nm; + + if ( name.right(1) == "/" ) + { + isdir = true; + name = name.left( name.length() - 1 ); + } + + int pos = name.findRev( '/' ); + if ( pos == -1 ) + nm = name; + else + nm = name.mid( pos + 1 ); + + // read access + buffer[ 0x6b ] = 0; + char *dummy; + const char* p = buffer + 0x64; + while( *p == ' ' ) ++p; + int access = (int)strtol( p, &dummy, 8 ); + + // read user and group + TQString user( buffer + 0x109 ); + TQString group( buffer + 0x129 ); + + // read time + buffer[ 0x93 ] = 0; + p = buffer + 0x88; + while( *p == ' ' ) ++p; + int time = (int)strtol( p, &dummy, 8 ); + + // read type flag + char typeflag = buffer[ 0x9c ]; + // '0' for files, '1' hard link, '2' symlink, '5' for directory + // (and 'L' for longlink filenames, 'K' for longlink symlink targets) + // and 'D' for GNU tar extension DUMPDIR + if ( typeflag == '5' ) + isdir = true; + + bool isDumpDir = false; + if ( typeflag == 'D' ) + { + isdir = false; + isDumpDir = true; + } + //bool islink = ( typeflag == '1' || typeflag == '2' ); + //kdDebug(7041) << "typeflag=" << typeflag << " islink=" << islink << endl; + + if (isdir) + access |= S_IFDIR; // f*cking broken tar files + + KArchiveEntry* e; + if ( isdir ) + { + //kdDebug(7041) << "KTar::openArchive directory " << nm << endl; + e = new KArchiveDirectory( this, nm, access, time, user, group, symlink ); + } + else + { + // read size + buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437 + char *dummy; + const char* p = buffer + 0x7c; + while( *p == ' ' ) ++p; + int size = (int)strtol( p, &dummy, 8 ); + + // for isDumpDir we will skip the additional info about that dirs contents + if ( isDumpDir ) + { + //kdDebug(7041) << "KTar::openArchive " << nm << " isDumpDir" << endl; + e = new KArchiveDirectory( this, nm, access, time, user, group, symlink ); + } + else + { + + // Let's hack around hard links. Our classes don't support that, so make them symlinks + if ( typeflag == '1' ) + { + kdDebug(7041) << "HARD LINK, setting size to 0 instead of " << size << endl; + size = 0; // no contents + } + + //kdDebug(7041) << "KTar::openArchive file " << nm << " size=" << size << endl; + e = new KArchiveFile( this, nm, access, time, user, group, symlink, + dev->at(), size ); + } + + // Skip contents + align bytes + int rest = size % 0x200; + int skip = size + (rest ? 0x200 - rest : 0); + //kdDebug(7041) << "KTar::openArchive, at()=" << dev->at() << " rest=" << rest << " skipping " << skip << endl; + if (! dev->at( dev->at() + skip ) ) + kdWarning(7041) << "KTar::openArchive skipping " << skip << " failed" << endl; + } + + if ( pos == -1 ) + { + if ( nm == "." ) // special case + { + Q_ASSERT( isdir ); + if ( isdir ) + setRootDir( static_cast<KArchiveDirectory *>( e ) ); + } + else + rootDir()->addEntry( e ); + } + else + { + // In some tar files we can find dir/./file => call cleanDirPath + TQString path = TQDir::cleanDirPath( name.left( pos ) ); + // Ensure container directory exists, create otherwise + KArchiveDirectory * d = findOrCreate( path ); + d->addEntry( e ); + } + } + else + { + //tqDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]); + d->tarEnd = dev->at() - n; // Remember end of archive + ende = true; + } + } while( !ende ); + return true; +} + +/* + * Writes back the changes of the temporary file + * to the original file. + * Must only be called if in IO_WriteOnly mode + */ +bool KTar::KTarPrivate::writeBackTempFile( const TQString & filename ) { + if ( ! tmpFile ) + return true; + + kdDebug(7041) << "Write temporary file to compressed file" << endl; + kdDebug(7041) << filename << " " << mimetype << endl; + + bool forced = false; + if( "application/x-gzip" == mimetype + || "application/x-bzip2" == mimetype + || "application/x-lzma" == mimetype + || "application/x-xz" == mimetype) + forced = true; + + TQIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced ); + if( dev ) { + TQFile* file = tmpFile->file(); + file->close(); + if ( ! file->open(IO_ReadOnly) || ! dev->open(IO_WriteOnly) ) + { + file->close(); + delete dev; + return false; + } + if ( forced ) + static_cast<KFilterDev *>(dev)->setOrigFileName( origFileName ); + TQByteArray buffer(8*1024); + TQ_LONG len; + while ( ! file->atEnd()) { + len = file->readBlock(buffer.data(),buffer.size()); + dev->writeBlock(buffer.data(),len); + } + file->close(); + dev->close(); + delete dev; + } + + kdDebug(7041) << "Write temporary file to compressed file done." << endl; + return true; +} + +bool KTar::closeArchive() +{ + d->dirList.clear(); + + // If we are in write mode and had created + // a temporary tar file, we have to write + // back the changes to the original file + if( mode() == IO_WriteOnly) + return d->writeBackTempFile( m_filename ); + + return true; +} + +bool KTar::writeDir( const TQString& name, const TQString& user, const TQString& group ) +{ + mode_t perm = 040755; + time_t the_time = time(0); + return writeDir(name,user,group,perm,the_time,the_time,the_time); +#if 0 + if ( !isOpened() ) + { + kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n"; + return false; + } + + if ( !(mode() & IO_WriteOnly) ) + { + kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n"; + return false; + } + + // In some tar files we can find dir/./ => call cleanDirPath + TQString dirName ( TQDir::cleanDirPath( name ) ); + + // Need trailing '/' + if ( dirName.right(1) != "/" ) + dirName += "/"; + + if ( d->dirList.contains( dirName ) ) + return true; // already there + + char buffer[ 0x201 ]; + memset( buffer, 0, 0x200 ); + if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read + + // If more than 100 chars, we need to use the LongLink trick + if ( dirName.length() > 99 ) + { + strcpy( buffer, "././@LongLink" ); + fillBuffer( buffer, " 0", dirName.length()+1, 'L', user.local8Bit(), group.local8Bit() ); + device()->writeBlock( buffer, 0x200 ); + strncpy( buffer, TQFile::encodeName(dirName), 0x200 ); + buffer[0x200] = 0; + // write long name + device()->writeBlock( buffer, 0x200 ); + // not even needed to reclear the buffer, tar doesn't do it + } + else + { + // Write name + strncpy( buffer, TQFile::encodeName(dirName), 0x200 ); + buffer[0x200] = 0; + } + + fillBuffer( buffer, " 40755", 0, 0x35, user.local8Bit(), group.local8Bit()); + + // Write header + device()->writeBlock( buffer, 0x200 ); + if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at(); + + d->dirList.append( dirName ); // contains trailing slash + return true; // TODO if wanted, better error control +#endif +} + +bool KTar::prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size ) +{ + mode_t dflt_perm = 0100644; + time_t the_time = time(0); + return prepareWriting(name,user,group,size,dflt_perm, + the_time,the_time,the_time); +} + +bool KTar::doneWriting( uint size ) +{ + // Write alignment + int rest = size % 0x200; + if ( mode() & IO_ReadWrite ) + d->tarEnd = device()->at() + (rest ? 0x200 - rest : 0); // Record our new end of archive + if ( rest ) + { + char buffer[ 0x201 ]; + for( uint i = 0; i < 0x200; ++i ) + buffer[i] = 0; + TQ_LONG nwritten = device()->writeBlock( buffer, 0x200 - rest ); + return nwritten == 0x200 - rest; + } + return true; +} + +/*** Some help from the tar sources +struct posix_header +{ byte offset + char name[100]; * 0 * 0x0 + char mode[8]; * 100 * 0x64 + char uid[8]; * 108 * 0x6c + char gid[8]; * 116 * 0x74 + char size[12]; * 124 * 0x7c + char mtime[12]; * 136 * 0x88 + char chksum[8]; * 148 * 0x94 + char typeflag; * 156 * 0x9c + char linkname[100]; * 157 * 0x9d + char magic[6]; * 257 * 0x101 + char version[2]; * 263 * 0x107 + char uname[32]; * 265 * 0x109 + char gname[32]; * 297 * 0x129 + char devmajor[8]; * 329 * 0x149 + char devminor[8]; * 337 * ... + char prefix[155]; * 345 * + * 500 * +}; +*/ + +void KTar::fillBuffer( char * buffer, + const char * mode, int size, time_t mtime, char typeflag, + const char * uname, const char * gname ) +{ + // mode (as in stat()) + assert( strlen(mode) == 6 ); + strcpy( buffer+0x64, mode ); + buffer[ 0x6a ] = ' '; + buffer[ 0x6b ] = '\0'; + + // dummy uid + strcpy( buffer + 0x6c, " 765 "); + // dummy gid + strcpy( buffer + 0x74, " 144 "); + + // size + TQCString s; + s.sprintf("%o", size); // OCT + s = s.rightJustify( 11, ' ' ); + strcpy( buffer + 0x7c, s.data() ); + buffer[ 0x87 ] = ' '; // space-terminate (no null after) + + // modification time + s.sprintf("%lo", static_cast<unsigned long>(mtime) ); // OCT + s = s.rightJustify( 11, ' ' ); + strcpy( buffer + 0x88, s.data() ); + buffer[ 0x93 ] = ' '; // space-terminate (no null after) + + // spaces, replaced by the check sum later + buffer[ 0x94 ] = 0x20; + buffer[ 0x95 ] = 0x20; + buffer[ 0x96 ] = 0x20; + buffer[ 0x97 ] = 0x20; + buffer[ 0x98 ] = 0x20; + buffer[ 0x99 ] = 0x20; + + /* From the tar sources : + Fill in the checksum field. It's formatted differently from the + other fields: it has [6] digits, a null, then a space -- rather than + digits, a space, then a null. */ + + buffer[ 0x9a ] = '\0'; + buffer[ 0x9b ] = ' '; + + // type flag (dir, file, link) + buffer[ 0x9c ] = typeflag; + + // magic + version + strcpy( buffer + 0x101, "ustar"); + strcpy( buffer + 0x107, "00" ); + + // user + strcpy( buffer + 0x109, uname ); + // group + strcpy( buffer + 0x129, gname ); + + // Header check sum + int check = 32; + for( uint j = 0; j < 0x200; ++j ) + check += buffer[j]; + s.sprintf("%o", check ); // OCT + s = s.rightJustify( 7, ' ' ); + strcpy( buffer + 0x94, s.data() ); +} + +void KTar::writeLonglink(char *buffer, const TQCString &name, char typeflag, + const char *uname, const char *gname) { + strcpy( buffer, "././@LongLink" ); + int namelen = name.length() + 1; + fillBuffer( buffer, " 0", namelen, 0, typeflag, uname, gname ); + device()->writeBlock( buffer, 0x200 ); + int offset = 0; + while (namelen > 0) { + int chunksize = QMIN(namelen, 0x200); + memcpy(buffer, name.data()+offset, chunksize); + // write long name + device()->writeBlock( buffer, 0x200 ); + // not even needed to reclear the buffer, tar doesn't do it + namelen -= chunksize; + offset += 0x200; + }/*wend*/ +} + +bool KTar::prepareWriting(const TQString& name, const TQString& user, + const TQString& group, uint size, mode_t perm, + time_t atime, time_t mtime, time_t ctime) { + return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime); +} + +bool KTar::prepareWriting_impl(const TQString &name, const TQString &user, + const TQString &group, uint size, mode_t perm, + time_t /*atime*/, time_t mtime, time_t /*ctime*/) { + if ( !isOpened() ) + { + kdWarning(7041) << "KTar::prepareWriting: You must open the tar file before writing to it\n"; + return false; + } + + if ( !(mode() & IO_WriteOnly) ) + { + kdWarning(7041) << "KTar::prepareWriting: You must open the tar file for writing\n"; + return false; + } + + // In some tar files we can find dir/./file => call cleanDirPath + TQString fileName ( TQDir::cleanDirPath( name ) ); + + /* + // Create toplevel dirs + // Commented out by David since it's not necessary, and if anybody thinks it is, + // he needs to implement a findOrCreate equivalent in writeDir. + // But as KTar and the "tar" program both handle tar files without + // dir entries, there's really no need for that + TQString tmp ( fileName ); + int i = tmp.findRev( '/' ); + if ( i != -1 ) + { + TQString d = tmp.left( i + 1 ); // contains trailing slash + if ( !m_dirList.contains( d ) ) + { + tmp = tmp.mid( i + 1 ); + writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs + } + } + */ + + char buffer[ 0x201 ]; + memset( buffer, 0, 0x200 ); + if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read + + // provide converted stuff we need lateron + TQCString encodedFilename = TQFile::encodeName(fileName); + TQCString uname = user.local8Bit(); + TQCString gname = group.local8Bit(); + + // If more than 100 chars, we need to use the LongLink trick + if ( fileName.length() > 99 ) + writeLonglink(buffer,encodedFilename,'L',uname,gname); + + // Write (potentially truncated) name + strncpy( buffer, encodedFilename, 99 ); + buffer[99] = 0; + // zero out the rest (except for what gets filled anyways) + memset(buffer+0x9d, 0, 0x200 - 0x9d); + + TQCString permstr; + permstr.sprintf("%o",perm); + permstr = permstr.rightJustify(6, ' '); + fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname); + + // Write header + return device()->writeBlock( buffer, 0x200 ) == 0x200; +} + +bool KTar::writeDir(const TQString& name, const TQString& user, + const TQString& group, mode_t perm, + time_t atime, time_t mtime, time_t ctime) { + return KArchive::writeDir(name,user,group,perm,atime,mtime,ctime); +} + +bool KTar::writeDir_impl(const TQString &name, const TQString &user, + const TQString &group, mode_t perm, + time_t /*atime*/, time_t mtime, time_t /*ctime*/) { + if ( !isOpened() ) + { + kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n"; + return false; + } + + if ( !(mode() & IO_WriteOnly) ) + { + kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n"; + return false; + } + + // In some tar files we can find dir/./ => call cleanDirPath + TQString dirName ( TQDir::cleanDirPath( name ) ); + + // Need trailing '/' + if ( dirName.right(1) != "/" ) + dirName += "/"; + + if ( d->dirList.contains( dirName ) ) + return true; // already there + + char buffer[ 0x201 ]; + memset( buffer, 0, 0x200 ); + if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read + + // provide converted stuff we need lateron + TQCString encodedDirname = TQFile::encodeName(dirName); + TQCString uname = user.local8Bit(); + TQCString gname = group.local8Bit(); + + // If more than 100 chars, we need to use the LongLink trick + if ( dirName.length() > 99 ) + writeLonglink(buffer,encodedDirname,'L',uname,gname); + + // Write (potentially truncated) name + strncpy( buffer, encodedDirname, 99 ); + buffer[99] = 0; + // zero out the rest (except for what gets filled anyways) + memset(buffer+0x9d, 0, 0x200 - 0x9d); + + TQCString permstr; + permstr.sprintf("%o",perm); + permstr = permstr.rightJustify(6, ' '); + fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname); + + // Write header + device()->writeBlock( buffer, 0x200 ); + if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at(); + + d->dirList.append( dirName ); // contains trailing slash + return true; // TODO if wanted, better error control +} + +bool KTar::writeSymLink(const TQString &name, const TQString &target, + const TQString &user, const TQString &group, + mode_t perm, time_t atime, time_t mtime, time_t ctime) { + return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime); +} + +bool KTar::writeSymLink_impl(const TQString &name, const TQString &target, + const TQString &user, const TQString &group, + mode_t perm, time_t /*atime*/, time_t mtime, time_t /*ctime*/) { + if ( !isOpened() ) + { + kdWarning(7041) << "KTar::writeSymLink: You must open the tar file before writing to it\n"; + return false; + } + + if ( !(mode() & IO_WriteOnly) ) + { + kdWarning(7041) << "KTar::writeSymLink: You must open the tar file for writing\n"; + return false; + } + + device()->flush(); + + // In some tar files we can find dir/./file => call cleanDirPath + TQString fileName ( TQDir::cleanDirPath( name ) ); + + char buffer[ 0x201 ]; + memset( buffer, 0, 0x200 ); + if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read + + // provide converted stuff we need lateron + TQCString encodedFilename = TQFile::encodeName(fileName); + TQCString encodedTarget = TQFile::encodeName(target); + TQCString uname = user.local8Bit(); + TQCString gname = group.local8Bit(); + + // If more than 100 chars, we need to use the LongLink trick + if (target.length() > 99) + writeLonglink(buffer,encodedTarget,'K',uname,gname); + if ( fileName.length() > 99 ) + writeLonglink(buffer,encodedFilename,'L',uname,gname); + + // Write (potentially truncated) name + strncpy( buffer, encodedFilename, 99 ); + buffer[99] = 0; + // Write (potentially truncated) symlink target + strncpy(buffer+0x9d, encodedTarget, 99); + buffer[0x9d+99] = 0; + // zero out the rest + memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d); + + TQCString permstr; + permstr.sprintf("%o",perm); + permstr = permstr.rightJustify(6, ' '); + fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname); + + // Write header + bool retval = device()->writeBlock( buffer, 0x200 ) == 0x200; + if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at(); + return retval; +} + +void KTar::virtual_hook( int id, void* data ) { + switch (id) { + case VIRTUAL_WRITE_SYMLINK: { + WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data); + params->retval = writeSymLink_impl(*params->name,*params->target, + *params->user,*params->group,params->perm, + params->atime,params->mtime,params->ctime); + break; + } + case VIRTUAL_WRITE_DIR: { + WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data); + params->retval = writeDir_impl(*params->name,*params->user, + *params->group,params->perm, + params->atime,params->mtime,params->ctime); + break; + } + case VIRTUAL_PREPARE_WRITING: { + PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data); + params->retval = prepareWriting_impl(*params->name,*params->user, + *params->group,params->size,params->perm, + params->atime,params->mtime,params->ctime); + break; + } + default: + KArchive::virtual_hook( id, data ); + }/*end switch*/ +} + |