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

kio

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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