• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDECore

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

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • 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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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