kio Library: ktar.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# ktar.cpp

    00001 /* This file is part of the KDE libraries
    00002    Copyright (C) 2000 David Faure <faure@kde.org>
    00003    Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
    00004
    00005    This library is free software; you can redistribute it and/or
    00006    modify it under the terms of the GNU Library General Public
    00007    License version 2 as published by the Free Software Foundation.
    00008
    00009    This library is distributed in the hope that it will be useful,
    00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
    00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00012    Library General Public License for more details.
    00013
    00014    You should have received a copy of the GNU Library General Public License
    00015    along with this library; see the file COPYING.LIB.  If not, write to
    00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00017    Boston, MA 02111-1307, USA.
    00018 */
    00019
    00020 //#include <stdio.h>
    00021 #include <stdlib.h> // strtol
    00022 #include <time.h> // time()
    00023 /*#include <unistd.h>
    00024 #include <grp.h>
    00025 #include <pwd.h>*/
    00026 #include <assert.h>
    00027
    00028 #include <qcstring.h>
    00029 #include <qdir.h>
    00030 #include <qfile.h>
    00031 #include <kdebug.h>
    00032 #include <kmimetype.h>
    00033 #include <ktempfile.h>
    00034
    00035 #include <kfilterdev.h>
    00036 #include <kfilterbase.h>
    00037
    00038 #include "ktar.h"
    00039 #include <kstandarddirs.h>
    00040
    00044
    00045 class KTar::KTarPrivate
    00046 {
    00047 public:
    00048     KTarPrivate() : tarEnd( 0 ) {}
    00049     QStringList dirList;
    00050     int tarEnd;
    00051     KTempFile* tmpFile;
    00052     QString mimetype;
    00053     QCString origFileName;
    00054
    00055     bool fillTempFile(const QString & filename);
    00056     bool writeBackTempFile( const QString & filename );
    00057 };
    00058
    00059 KTar::KTar( const QString& filename, const QString & _mimetype )
    00060     : KArchive( 0L )
    00061 {
    00062     m_filename = filename;
    00063     d = new KTarPrivate;
    00064     d->tmpFile = 0L;
    00065     d->mimetype = _mimetype;
    00066     QString mimetype( _mimetype );
    00067     bool forced = true;
    00068     if ( mimetype.isEmpty() ) // Find out mimetype manually
    00069     {
    00070         if ( QFile::exists( filename ) )
    00071             mimetype = KMimeType::findByFileContent( filename )->name();
    00072         else
    00073             mimetype = KMimeType::findByPath( filename, 0, true )->name();
    00074         kdDebug(7041) << "KTar::KTar mimetype = " << mimetype << endl;
    00075
    00076         // Don't move to prepareDevice - the other constructor theoretically allows ANY filter
    00077         if ( mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around
    00078              mimetype == "application/x-webarchive" )
    00079         {
    00080             // that's a gzipped tar file, so ask for gzip filter
    00081             mimetype = "application/x-gzip";
    00082         }
    00083         else if ( mimetype == "application/x-tbz" ) // that's a bzipped2 tar file, so ask for bz2 filter
    00084         {
    00085             mimetype = "application/x-bzip2";
    00086         }
    00087         else
    00088         {
    00089             // Something else. Check if it's not really gzip though (e.g. for KOffice docs)
    00090             QFile file( filename );
    00091             if ( file.open( IO_ReadOnly ) )
    00092             {
    00093                 unsigned char firstByte = file.getch();
    00094                 unsigned char secondByte = file.getch();
    00095                 unsigned char thirdByte = file.getch();
    00096                 if ( firstByte == 0037 && secondByte == 0213 )
    00097                     mimetype = "application/x-gzip";
    00098                 else if ( firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h' )
    00099                     mimetype = "application/x-bzip2";
    00100                 else if ( firstByte == 'P' && secondByte == 'K' && thirdByte == 3 )
    00101                 {
    00102                     unsigned char fourthByte = file.getch();
    00103                     if ( fourthByte == 4 )
    00104                         mimetype = "application/x-zip";
    00105                 }
    00106             }
    00107             file.close();
    00108         }
    00109         forced = false;
    00110         d->mimetype = mimetype;
    00111     } // END mimetype.isEmpty()
    00112
    00113     prepareDevice( filename, mimetype, forced );
    00114 }
    00115
    00116 void KTar::prepareDevice( const QString & filename,
    00117                             const QString & mimetype, bool /*forced*/ )
    00118 {
    00119   if( "application/x-tar" == mimetype )
    00120       setDevice( new QFile( filename ) );
    00121   else
    00122   {
    00123     // The compression filters are very slow with random access.
    00124     // So instead of applying the filter to the device,
    00125     // the file is completly extracted instead,
    00126     // and we work on the extracted tar file.
    00127     // This improves the extraction speed by the tar ioslave dramatically,
    00128     // if the archive file contains many files.
    00129     // This is because the tar ioslave extracts one file after the other and normally
    00130     // has to walk through the decompression filter each time.
    00131     // Which is in fact nearly as slow as a complete decompression for each file.
    00132     d->tmpFile = new KTempFile(locateLocal("tmp", "ktar-"),".tar");
    00133     kdDebug( 7041 ) << "KTar::prepareDevice creating TempFile: " << d->tmpFile->name() << endl;
    00134     d->tmpFile->setAutoDelete(true);
    00135
    00136     // KTempFile opens the file automatically,
    00137     // the device must be closed, however, for KArchive.setDevice()
    00138     QFile* file = d->tmpFile->file();
    00139     file->close();
    00140     setDevice(file);
    00141   }
    00142 }
    00143
    00144 KTar::KTar( QIODevice * dev )
    00145     : KArchive( dev )
    00146 {
    00147     d = new KTarPrivate;
    00148 }
    00149
    00150 KTar::~KTar()
    00151 {
    00152     // mjarrett: Closes to prevent ~KArchive from aborting w/o device
    00153     if( isOpened() )
    00154         close();
    00155
    00156     if (d->tmpFile)
    00157         delete d->tmpFile; // will delete the device
    00158     else if ( !m_filename.isEmpty() )
    00159         delete device(); // we created it ourselves
    00160
    00161
    00162     delete d;
    00163 }
    00164
    00165 void KTar::setOrigFileName( const QCString & fileName )
    00166 {
    00167     if ( !isOpened() || !(mode() & IO_WriteOnly) )
    00168     {
    00169         kdWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n";
    00170         return;
    00171     }
    00172     d->origFileName = fileName;
    00173 }
    00174
    00175 Q_LONG KTar::readRawHeader(char *buffer) {
    00176   // Read header
    00177   Q_LONG n = device()->readBlock( buffer, 0x200 );
    00178   if ( n == 0x200 && buffer[0] != 0 ) {
    00179     // Make sure this is actually a tar header
    00180     if (strncmp(buffer + 257, "ustar", 5)) {
    00181       // The magic isn't there (broken/old tars), but maybe a correct checksum?
    00182       QCString s;
    00183
    00184       int check = 0;
    00185       for( uint j = 0; j < 0x200; ++j )
    00186         check += buffer[j];
    00187
    00188       // adjust checksum to count the checksum fields as blanks
    00189       for( uint j = 0; j < 8 /*size of the checksum field including the \0 and the space*/; j++ )
    00190         check -= buffer[148 + j];
    00191       check += 8 * ' ';
    00192
    00193       s.sprintf("%o", check );
    00194
    00195       // only compare those of the 6 checksum digits that mean something,
    00196       // because the other digits are filled with all sorts of different chars by different tars ...
    00197       if( strncmp( buffer + 148 + 6 - s.length(), s.data(), s.length() ) ) {
    00198         kdWarning(7041) << "KTar: invalid TAR file. Header is: " << QCString( buffer+257, 5 ) << endl;
    00199         return -1;
    00200       }
    00201     }/*end if*/
    00202   } else {
    00203     // reset to 0 if 0x200 because logical end of archive has been reached
    00204     if (n == 0x200) n = 0;
    00205   }/*end if*/
    00206   return n;
    00207 }
    00208
    00209 bool KTar::readLonglink(char *buffer,QCString &longlink) {
    00210   Q_LONG n = 0;
    00211   QIODevice *dev = device();
    00212   // read size of longlink from size field in header
    00213   // size is in bytes including the trailing null (which we ignore)
    00214   buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
    00215   char *dummy;
    00216   const char* p = buffer + 0x7c;
    00217   while( *p == ' ' ) ++p;
    00218   int size = (int)strtol( p, &dummy, 8 );
    00219
    00220   longlink.resize(size);
    00221   size--;   // ignore trailing null
    00222   dummy = longlink.data();
    00223   int offset = 0;
    00224   while (size > 0) {
    00225     int chunksize = QMIN(size, 0x200);
    00226     n = dev->readBlock( dummy + offset, chunksize );
    00227     if (n == -1) return false;
    00228     size -= chunksize;
    00229     offset += 0x200;
    00230   }/*wend*/
    00231   // jump over the rest
    00232   int skip = 0x200 - (n % 0x200);
    00233   if (skip < 0x200) {
    00234     if (dev->readBlock(buffer,skip) != skip) return false;
    00235   }
    00236   return true;
    00237 }
    00238
    00239 Q_LONG KTar::readHeader(char *buffer,QString &name,QString &symlink) {
    00240   name.truncate(0);
    00241   symlink.truncate(0);
    00242   while (true) {
    00243     Q_LONG n = readRawHeader(buffer);
    00244     if (n != 0x200) return n;
    00245
    00246     // is it a longlink?
    00247     if (strcmp(buffer,"././@LongLink") == 0) {
    00248       char typeflag = buffer[0x9c];
    00249       QCString longlink;
    00250       readLonglink(buffer,longlink);
    00251       switch (typeflag) {
    00252         case 'L': name = QFile::decodeName(longlink); break;
    00253         case 'K': symlink = QFile::decodeName(longlink); break;
    00254       }/*end switch*/
    00255     } else {
    00256       break;
    00257     }/*end if*/
    00258   }/*wend*/
    00259
    00260   // if not result of longlink, read names directly from the header
    00261   if (name.isEmpty())
    00262     name = QFile::decodeName(buffer);
    00263   if (symlink.isEmpty())
    00264     symlink = QFile::decodeName(buffer + 0x9d);
    00265
    00266   return 0x200;
    00267 }
    00268
    00269 /*
    00270  * If we have created a temporary file, we have
    00271  * to decompress the original file now and write
    00272  * the contents to the temporary file.
    00273  */
    00274 bool KTar::KTarPrivate::fillTempFile( const QString & filename) {
    00275     if ( ! tmpFile )
    00276         return true;
    00277
    00278     kdDebug( 7041 ) <<
    00279         "KTar::openArchive: filling tmpFile of mimetype '" << mimetype <<
    00280         "' ... " << endl;
    00281
    00282     bool forced = false;
    00283     if( "application/x-gzip" == mimetype
    00284     || "application/x-bzip2" == mimetype)
    00285         forced = true;
    00286
    00287     QIODevice *filterDev = KFilterDev::deviceForFile( filename, mimetype, forced );
    00288
    00289     if( filterDev ) {
    00290         QFile* file = tmpFile->file();
    00291         file->close();
    00292         if ( ! file->open( IO_WriteOnly ) )
    00293         {
    00294             delete filterDev;
    00295             return false;
    00296         }
    00297         QByteArray buffer(8*1024);
    00298         if ( ! filterDev->open( IO_ReadOnly ) )
    00299         {
    00300             delete filterDev;
    00301             return false;
    00302         }
    00303         Q_LONG len;
    00304         while ( !filterDev->atEnd() ) {
    00305             len = filterDev->readBlock(buffer.data(),buffer.size());
    00306             if ( len <= 0 ) { // corrupted archive
    00307                 delete filterDev;
    00308                 return false;
    00309             }
    00310             file->writeBlock(buffer.data(),len);
    00311         }
    00312         filterDev->close();
    00313         delete filterDev;
    00314
    00315         file->close();
    00316         if ( ! file->open( IO_ReadOnly ) )
    00317             return false;
    00318     }
    00319     else
    00320         kdDebug( 7041 ) << "KTar::openArchive: no filterdevice found!" << endl;
    00321
    00322     kdDebug( 7041 ) << "KTar::openArchive: filling tmpFile finished." << endl;
    00323     return true;
    00324 }
    00325
    00326 bool KTar::openArchive( int mode )
    00327 {
    00328     kdDebug( 7041 ) << "KTar::openArchive" << endl;
    00329     if ( !(mode & IO_ReadOnly) )
    00330         return true;
    00331
    00332     if ( !d->fillTempFile( m_filename ) )
    00333         return false;
    00334
    00335     // We'll use the permission and user/group of d->rootDir
    00336     // for any directory we emulate (see findOrCreate)
    00337     //struct stat buf;
    00338     //stat( m_filename, &buf );
    00339
    00340     d->dirList.clear();
    00341     QIODevice* dev = device();
    00342
    00343     if ( !dev )
    00344         return false;
    00345
    00346     // read dir infos
    00347     char buffer[ 0x200 ];
    00348     bool ende = false;
    00349     do
    00350     {
    00351         QString name;
    00352         QString symlink;
    00353
    00354         // Read header
    00355         Q_LONG n = readHeader(buffer,name,symlink);
    00356         if (n < 0) return false;
    00357         if (n == 0x200)
    00358         {
    00359             bool isdir = false;
    00360             QString nm;
    00361
    00362             if ( name.right(1) == "/" )
    00363             {
    00364                 isdir = true;
    00365                 name = name.left( name.length() - 1 );
    00366             }
    00367
    00368             int pos = name.findRev( '/' );
    00369             if ( pos == -1 )
    00370                 nm = name;
    00371             else
    00372                 nm = name.mid( pos + 1 );
    00373
    00374             // read access
    00375             buffer[ 0x6b ] = 0;
    00376             char *dummy;
    00377             const char* p = buffer + 0x64;
    00378             while( *p == ' ' ) ++p;
    00379             int access = (int)strtol( p, &dummy, 8 );
    00380
    00381             // read user and group
    00382             QString user( buffer + 0x109 );
    00383             QString group( buffer + 0x129 );
    00384
    00385             // read time
    00386             buffer[ 0x93 ] = 0;
    00387             p = buffer + 0x88;
    00388             while( *p == ' ' ) ++p;
    00389             int time = (int)strtol( p, &dummy, 8 );
    00390
    00391             // read type flag
    00392             char typeflag = buffer[ 0x9c ];
    00393             // '0' for files, '1' hard link, '2' symlink, '5' for directory
    00394             // (and 'L' for longlink filenames, 'K' for longlink symlink targets)
    00395             // and 'D' for GNU tar extension DUMPDIR
    00396             if ( typeflag == '1' )
    00397                 isdir = true;
    00398
    00399             bool isDumpDir = false;
    00400             if ( typeflag == 'D' )
    00401             {
    00402                 isdir = false;
    00403                 isDumpDir = true;
    00404             }
    00405             //bool islink = ( typeflag == '1' || typeflag == '2' );
    00406             //kdDebug(7041) << "typeflag=" << typeflag << " islink=" << islink << endl;
    00407
    00408             if (isdir)
    00409                 access |= S_IFDIR; // f*cking broken tar files
    00410
    00411             KArchiveEntry* e;
    00412             if ( isdir )
    00413             {
    00414                 //kdDebug(7041) << "KArchive::open directory " << nm << endl;
    00415                 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
    00416             }
    00417             else
    00418             {
    00419                 // read size
    00420                 buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
    00421                 char *dummy;
    00422                 const char* p = buffer + 0x7c;
    00423                 while( *p == ' ' ) ++p;
    00424                 int size = (int)strtol( p, &dummy, 8 );
    00425
    00426                 // for isDumpDir we will skip the additional info about that dirs contents
    00427                 if ( isDumpDir )
    00428                 {
    00429             e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
    00430                 }
    00431         else
    00432         {
    00433
    00434                     // Let's hack around hard links. Our classes don't support that, so make them symlinks
    00435                     if ( typeflag == '1' )
    00436                     {
    00437                         size = nm.length(); // in any case, we don't want to skip the real size, hence this resetting of size
    00438                         kdDebug(7041) << "HARD LINK, setting size to " << size << endl;
    00439                     }
    00440
    00441                     // kdDebug(7041) << "KArchive::open file " << nm << " size=" << size << endl;
    00442                     e = new KArchiveFile( this, nm, access, time, user, group, symlink,
    00443                                           dev->at(), size );
    00444         }
    00445
    00446                 // Skip contents + align bytes
    00447                 int rest = size % 0x200;
    00448                 int skip = size + (rest ? 0x200 - rest : 0);
    00449                 //kdDebug(7041) << "KArchive::open, at()=" << dev->at() << " rest=" << rest << " skipping " << skip << endl;
    00450                 if (! dev->at( dev->at() + skip ) )
    00451                     kdWarning(7041) << "KArchive::open skipping " << skip << " failed" << endl;
    00452             }
    00453
    00454             if ( pos == -1 )
    00455             {
    00456                 if ( nm == "." ) // special case
    00457                 {
    00458                     Q_ASSERT( isdir );
    00459                     if ( isdir )
    00460                         setRootDir( static_cast<KArchiveDirectory *>( e ) );
    00461                 }
    00462                 else
    00463                     rootDir()->addEntry( e );
    00464             }
    00465             else
    00466             {
    00467                 // In some tar files we can find dir/./file => call cleanDirPath
    00468                 QString path = QDir::cleanDirPath( name.left( pos ) );
    00469                 // Ensure container directory exists, create otherwise
    00470                 KArchiveDirectory * d = findOrCreate( path );
    00471                 d->addEntry( e );
    00472             }
    00473         }
    00474         else
    00475         {
    00476             //qDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]);
    00477             d->tarEnd = dev->at() - n; // Remember end of archive
    00478             ende = true;
    00479         }
    00480     } while( !ende );
    00481     return true;
    00482 }
    00483
    00484 /*
    00485  * Writes back the changes of the temporary file
    00486  * to the original file.
    00487  * Must only be called if in IO_WriteOnly mode
    00488  */
    00489 bool KTar::KTarPrivate::writeBackTempFile( const QString & filename ) {
    00490     if ( ! tmpFile )
    00491         return true;
    00492
    00493     kdDebug(7041) << "Write temporary file to compressed file" << endl;
    00494     kdDebug(7041) << filename << " " << mimetype << endl;
    00495
    00496     bool forced = false;
    00497     if( "application/x-gzip" == mimetype
    00498         || "application/x-bzip2" == mimetype)
    00499         forced = true;
    00500
    00501
    00502     QIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced );
    00503     if( dev ) {
    00504         QFile* file = tmpFile->file();
    00505         file->close();
    00506         if ( ! file->open(IO_ReadOnly) || ! dev->open(IO_WriteOnly) )
    00507         {
    00508             file->close();
    00509             delete dev;
    00510             return false;
    00511         }
    00512         if ( forced )
    00513             static_cast<KFilterDev *>(dev)->setOrigFileName( origFileName );
    00514         QByteArray buffer(8*1024);
    00515         Q_LONG len;
    00516         while ( ! file->atEnd()) {
    00517             len = file->readBlock(buffer.data(),buffer.size());
    00518             dev->writeBlock(buffer.data(),len);
    00519         }
    00520         file->close();
    00521         dev->close();
    00522         delete dev;
    00523     }
    00524
    00525     kdDebug(7041) << "Write temporary file to compressed file done." << endl;
    00526     return true;
    00527 }
    00528
    00529 bool KTar::closeArchive()
    00530 {
    00531     d->dirList.clear();
    00532
    00533     // If we are in write mode and had created
    00534     // a temporary tar file, we have to write
    00535     // back the changes to the original file
    00536     if( mode() == IO_WriteOnly)
    00537         return d->writeBackTempFile( m_filename );
    00538
    00539     return true;
    00540 }
    00541
    00542 bool KTar::writeDir( const QString& name, const QString& user, const QString& group )
    00543 {
    00544     mode_t perm = 040755;
    00545     time_t the_time = time(0);
    00546     return writeDir(name,user,group,perm,the_time,the_time,the_time);
    00547 #if 0
    00548     if ( !isOpened() )
    00549     {
    00550         kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
    00551         return false;
    00552     }
    00553
    00554     if ( !(mode() & IO_WriteOnly) )
    00555     {
    00556         kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
    00557         return false;
    00558     }
    00559
    00560     // In some tar files we can find dir/./ => call cleanDirPath
    00561     QString dirName ( QDir::cleanDirPath( name ) );
    00562
    00563     // Need trailing '/'
    00564     if ( dirName.right(1) != "/" )
    00565         dirName += "/";
    00566
    00567     if ( d->dirList.contains( dirName ) )
    00568         return true; // already there
    00569
    00570     char buffer[ 0x201 ];
    00571     memset( buffer, 0, 0x200 );
    00572     if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
    00573
    00574     // If more than 100 chars, we need to use the LongLink trick
    00575     if ( dirName.length() > 99 )
    00576     {
    00577         strcpy( buffer, "././@LongLink" );
    00578         fillBuffer( buffer, "     0", dirName.length()+1, 'L', user.local8Bit(), group.local8Bit() );
    00579         device()->writeBlock( buffer, 0x200 );
    00580         strncpy( buffer, QFile::encodeName(dirName), 0x200 );
    00581         buffer[0x200] = 0;
    00582         // write long name
    00583         device()->writeBlock( buffer, 0x200 );
    00584         // not even needed to reclear the buffer, tar doesn't do it
    00585     }
    00586     else
    00587     {
    00588         // Write name
    00589         strncpy( buffer, QFile::encodeName(dirName), 0x200 );
    00590         buffer[0x200] = 0;
    00591     }
    00592
    00593     fillBuffer( buffer, " 40755", 0, 0x35, user.local8Bit(), group.local8Bit());
    00594
    00595     // Write header
    00596     device()->writeBlock( buffer, 0x200 );
    00597     if ( mode() & IO_ReadWrite )  d->tarEnd = device()->at();
    00598
    00599     d->dirList.append( dirName ); // contains trailing slash
    00600     return true; // TODO if wanted, better error control
    00601 #endif
    00602 }
    00603
    00604 bool KTar::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
    00605 {
    00606     mode_t dflt_perm = 0100644;
    00607     time_t the_time = time(0);
    00608     return prepareWriting(name,user,group,size,dflt_perm,
    00609             the_time,the_time,the_time);
    00610 }
    00611
    00612 bool KTar::doneWriting( uint size )
    00613 {
    00614     // Write alignment
    00615     int rest = size % 0x200;
    00616     if ( mode() & IO_ReadWrite )
    00617         d->tarEnd = device()->at() + (rest ? 0x200 - rest : 0); // Record our new end of archive
    00618     if ( rest )
    00619     {
    00620         char buffer[ 0x201 ];
    00621         for( uint i = 0; i < 0x200; ++i )
    00622             buffer[i] = 0;
    00623         Q_LONG nwritten = device()->writeBlock( buffer, 0x200 - rest );
    00624         return nwritten == 0x200 - rest;
    00625     }
    00626     return true;
    00627 }
    00628
    00629 /*** Some help from the tar sources
    00630 struct posix_header
    00631 {                               byte offset
    00632   char name[100];               *   0 *     0x0
    00633   char mode[8];                 * 100 *     0x64
    00634   char uid[8];                  * 108 *     0x6c
    00635   char gid[8];                  * 116 *     0x74
    00636   char size[12];                * 124 *     0x7c
    00637   char mtime[12];               * 136 *     0x88
    00638   char chksum[8];               * 148 *     0x94
    00639   char typeflag;                * 156 *     0x9c
    00640   char linkname[100];           * 157 *     0x9d
    00641   char magic[6];                * 257 *     0x101
    00642   char version[2];              * 263 *     0x107
    00643   char uname[32];               * 265 *     0x109
    00644   char gname[32];               * 297 *     0x129
    00645   char devmajor[8];             * 329 *     0x149
    00646   char devminor[8];             * 337 *     ...
    00647   char prefix[155];             * 345 *
    00648                                 * 500 *
    00649 };
    00650 */
    00651
    00652 void KTar::fillBuffer( char * buffer,
    00653     const char * mode, int size, time_t mtime, char typeflag,
    00654     const char * uname, const char * gname )
    00655 {
    00656   // mode (as in stat())
    00657   assert( strlen(mode) == 6 );
    00658   strcpy( buffer+0x64, mode );
    00659   buffer[ 0x6a ] = ' ';
    00660   buffer[ 0x6b ] = '\0';
    00661
    00662   // dummy uid
    00663   strcpy( buffer + 0x6c, "   765 ");
    00664   // dummy gid
    00665   strcpy( buffer + 0x74, "   144 ");
    00666
    00667   // size
    00668   QCString s;
    00669   s.sprintf("%o", size); // OCT
    00670   s = s.rightJustify( 11, ' ' );
    00671   strcpy( buffer + 0x7c, s.data() );
    00672   buffer[ 0x87 ] = ' '; // space-terminate (no null after)
    00673
    00674   // modification time
    00675   s.sprintf("%lo", static_cast<unsigned long>(mtime) ); // OCT
    00676   s = s.rightJustify( 11, ' ' );
    00677   strcpy( buffer + 0x88, s.data() );
    00678   buffer[ 0x93 ] = ' '; // space-terminate (no null after)
    00679
    00680   // spaces, replaced by the check sum later
    00681   buffer[ 0x94 ] = 0x20;
    00682   buffer[ 0x95 ] = 0x20;
    00683   buffer[ 0x96 ] = 0x20;
    00684   buffer[ 0x97 ] = 0x20;
    00685   buffer[ 0x98 ] = 0x20;
    00686   buffer[ 0x99 ] = 0x20;
    00687
    00688   /* From the tar sources :
    00689      Fill in the checksum field.  It's formatted differently from the
    00690      other fields: it has [6] digits, a null, then a space -- rather than
    00691      digits, a space, then a null. */
    00692
    00693   buffer[ 0x9a ] = '\0';
    00694   buffer[ 0x9b ] = ' ';
    00695
    00696   // type flag (dir, file, link)
    00697   buffer[ 0x9c ] = typeflag;
    00698
    00699  // magic + version
    00700   strcpy( buffer + 0x101, "ustar");
    00701   strcpy( buffer + 0x107, "00" );
    00702
    00703   // user
    00704   strcpy( buffer + 0x109, uname );
    00705   // group
    00706   strcpy( buffer + 0x129, gname );
    00707
    00708   // Header check sum
    00709   int check = 32;
    00710   for( uint j = 0; j < 0x200; ++j )
    00711     check += buffer[j];
    00712   s.sprintf("%o", check ); // OCT
    00713   s = s.rightJustify( 7, ' ' );
    00714   strcpy( buffer + 0x94, s.data() );
    00715 }
    00716
    00717 void KTar::writeLonglink(char *buffer, const QCString &name, char typeflag,
    00718     const char *uname, const char *gname) {
    00719   strcpy( buffer, "././@LongLink" );
    00720   int namelen = name.length() + 1;
    00721   fillBuffer( buffer, "     0", namelen, 0, typeflag, uname, gname );
    00722   device()->writeBlock( buffer, 0x200 );
    00723   int offset = 0;
    00724   while (namelen > 0) {
    00725     int chunksize = QMIN(namelen, 0x200);
    00726     memcpy(buffer, name.data()+offset, chunksize);
    00727     // write long name
    00728     device()->writeBlock( buffer, 0x200 );
    00729     // not even needed to reclear the buffer, tar doesn't do it
    00730     namelen -= chunksize;
    00731     offset += 0x200;
    00732   }/*wend*/
    00733 }
    00734
    00735 bool KTar::prepareWriting(const QString& name, const QString& user,
    00736                 const QString& group, uint size, mode_t perm,
    00737                 time_t atime, time_t mtime, time_t ctime) {
    00738   return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
    00739 }
    00740
    00741 bool KTar::prepareWriting_impl(const QString &name, const QString &user,
    00742                 const QString &group, uint size, mode_t perm,
    00743                 time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
    00744     if ( !isOpened() )
    00745     {
    00746         kdWarning(7041) << "KTar::prepareWriting: You must open the tar file before writing to it\n";
    00747         return false;
    00748     }
    00749
    00750     if ( !(mode() & IO_WriteOnly) )
    00751     {
    00752         kdWarning(7041) << "KTar::prepareWriting: You must open the tar file for writing\n";
    00753         return false;
    00754     }
    00755
    00756     // In some tar files we can find dir/./file => call cleanDirPath
    00757     QString fileName ( QDir::cleanDirPath( name ) );
    00758
    00759     /*
    00760       // Create toplevel dirs
    00761       // Commented out by David since it's not necessary, and if anybody thinks it is,
    00762       // he needs to implement a findOrCreate equivalent in writeDir.
    00763       // But as KTar and the "tar" program both handle tar files without
    00764       // dir entries, there's really no need for that
    00765       QString tmp ( fileName );
    00766       int i = tmp.findRev( '/' );
    00767       if ( i != -1 )
    00768       {
    00769       QString d = tmp.left( i + 1 ); // contains trailing slash
    00770       if ( !m_dirList.contains( d ) )
    00771       {
    00772       tmp = tmp.mid( i + 1 );
    00773       writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs
    00774       }
    00775       }
    00776     */
    00777
    00778     char buffer[ 0x201 ];
    00779     memset( buffer, 0, 0x200 );
    00780     if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
    00781
    00782     // provide converted stuff we need lateron
    00783     QCString encodedFilename = QFile::encodeName(fileName);
    00784     QCString uname = user.local8Bit();
    00785     QCString gname = group.local8Bit();
    00786
    00787     // If more than 100 chars, we need to use the LongLink trick
    00788     if ( fileName.length() > 99 )
    00789         writeLonglink(buffer,encodedFilename,'L',uname,gname);
    00790
    00791     // Write (potentially truncated) name
    00792     strncpy( buffer, encodedFilename, 99 );
    00793     buffer[99] = 0;
    00794     // zero out the rest (except for what gets filled anyways)
    00795     memset(buffer+0x9d, 0, 0x200 - 0x9d);
    00796
    00797     QCString permstr;
    00798     permstr.sprintf("%o",perm);
    00799     permstr.rightJustify(6, ' ');
    00800     fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
    00801
    00802     // Write header
    00803     return device()->writeBlock( buffer, 0x200 ) == 0x200;
    00804 }
    00805
    00806 bool KTar::writeDir(const QString& name, const QString& user,
    00807                 const QString& group, mode_t perm,
    00808                 time_t atime, time_t mtime, time_t ctime) {
    00809   return KArchive::writeDir(name,user,group,perm,atime,mtime,ctime);
    00810 }
    00811
    00812 bool KTar::writeDir_impl(const QString &name, const QString &user,
    00813                 const QString &group, mode_t perm,
    00814                 time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
    00815     if ( !isOpened() )
    00816     {
    00817         kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
    00818         return false;
    00819     }
    00820
    00821     if ( !(mode() & IO_WriteOnly) )
    00822     {
    00823         kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
    00824         return false;
    00825     }
    00826
    00827     // In some tar files we can find dir/./ => call cleanDirPath
    00828     QString dirName ( QDir::cleanDirPath( name ) );
    00829
    00830     // Need trailing '/'
    00831     if ( dirName.right(1) != "/" )
    00832         dirName += "/";
    00833
    00834     if ( d->dirList.contains( dirName ) )
    00835         return true; // already there
    00836
    00837     char buffer[ 0x201 ];
    00838     memset( buffer, 0, 0x200 );
    00839     if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
    00840
    00841     // provide converted stuff we need lateron
    00842     QCString encodedDirname = QFile::encodeName(dirName);
    00843     QCString uname = user.local8Bit();
    00844     QCString gname = group.local8Bit();
    00845
    00846     // If more than 100 chars, we need to use the LongLink trick
    00847     if ( dirName.length() > 99 )
    00848         writeLonglink(buffer,encodedDirname,'L',uname,gname);
    00849
    00850     // Write (potentially truncated) name
    00851     strncpy( buffer, encodedDirname, 99 );
    00852     buffer[99] = 0;
    00853     // zero out the rest (except for what gets filled anyways)
    00854     memset(buffer+0x9d, 0, 0x200 - 0x9d);
    00855
    00856     QCString permstr;
    00857     permstr.sprintf("%o",perm);
    00858     permstr.rightJustify(6, ' ');
    00859     fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
    00860
    00861     // Write header
    00862     device()->writeBlock( buffer, 0x200 );
    00863     if ( mode() & IO_ReadWrite )  d->tarEnd = device()->at();
    00864
    00865     d->dirList.append( dirName ); // contains trailing slash
    00866     return true; // TODO if wanted, better error control
    00867 }
    00868
    00869 bool KTar::writeSymLink(const QString &name, const QString &target,
    00870                 const QString &user, const QString &group,
    00871                 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
    00872   return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
    00873 }
    00874
    00875 bool KTar::writeSymLink_impl(const QString &name, const QString &target,
    00876                 const QString &user, const QString &group,
    00877                 mode_t perm, time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
    00878     if ( !isOpened() )
    00879     {
    00880         kdWarning(7041) << "KTar::writeSymLink: You must open the tar file before writing to it\n";
    00881         return false;
    00882     }
    00883
    00884     if ( !(mode() & IO_WriteOnly) )
    00885     {
    00886         kdWarning(7041) << "KTar::writeSymLink: You must open the tar file for writing\n";
    00887         return false;
    00888     }
    00889
    00890     device()->flush();
    00891
    00892     // In some tar files we can find dir/./file => call cleanDirPath
    00893     QString fileName ( QDir::cleanDirPath( name ) );
    00894
    00895     char buffer[ 0x201 ];
    00896     memset( buffer, 0, 0x200 );
    00897     if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
    00898
    00899     // provide converted stuff we need lateron
    00900     QCString encodedFilename = QFile::encodeName(fileName);
    00901     QCString encodedTarget = QFile::encodeName(target);
    00902     QCString uname = user.local8Bit();
    00903     QCString gname = group.local8Bit();
    00904
    00905     // If more than 100 chars, we need to use the LongLink trick
    00906     if (target.length() > 99)
    00907         writeLonglink(buffer,encodedTarget,'K',uname,gname);
    00908     if ( fileName.length() > 99 )
    00909         writeLonglink(buffer,encodedFilename,'L',uname,gname);
    00910
    00911     // Write (potentially truncated) name
    00912     strncpy( buffer, encodedFilename, 99 );
    00913     buffer[99] = 0;
    00914     // Write (potentially truncated) symlink target
    00915     strncpy(buffer+0x9d, encodedTarget, 99);
    00916     buffer[0x9d+99] = 0;
    00917     // zero out the rest
    00918     memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
    00919
    00920     QCString permstr;
    00921     permstr.sprintf("%o",perm);
    00922     permstr.rightJustify(6, ' ');
    00923     fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
    00924
    00925     // Write header
    00926     bool retval = device()->writeBlock( buffer, 0x200 ) == 0x200;
    00927     if ( mode() & IO_ReadWrite )  d->tarEnd = device()->at();
    00928     return retval;
    00929 }
    00930
    00931 void KTar::virtual_hook( int id, void* data ) {
    00932   switch (id) {
    00933     case VIRTUAL_WRITE_SYMLINK: {
    00934       WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
    00935       params->retval = writeSymLink_impl(*params->name,*params->target,
    00936                 *params->user,*params->group,params->perm,
    00937                 params->atime,params->mtime,params->ctime);
    00938       break;
    00939     }
    00940     case VIRTUAL_WRITE_DIR: {
    00941       WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data);
    00942       params->retval = writeDir_impl(*params->name,*params->user,
    00943             *params->group,params->perm,
    00944                 params->atime,params->mtime,params->ctime);
    00945       break;
    00946     }
    00947     case VIRTUAL_PREPARE_WRITING: {
    00948       PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
    00949       params->retval = prepareWriting_impl(*params->name,*params->user,
    00950                 *params->group,params->size,params->perm,
    00951                 params->atime,params->mtime,params->ctime);
    00952       break;
    00953     }
    00954     default:
    00955       KArchive::virtual_hook( id, data );
    00956   }/*end switch*/
    00957 }
    00958

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:24:04 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003