• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

ktar.cpp

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

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal