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

KDECore

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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.5
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