kio Library API Documentation

ktar.cpp

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