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