• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kio/kio
 

kio/kio

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

kio/kio

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

kio/kio

Skip menu "kio/kio"
  • arts
  • dcop
  • dnssd
  • interfaces
  •     interface
  •     library
  •   kspeech
  •   ktexteditor
  • kabc
  • kate
  • kcmshell
  • kdecore
  • kded
  • kdefx
  • kdeprint
  • kdesu
  • kdeui
  • kdoctools
  • khtml
  • kimgio
  • kinit
  • kio
  •   bookmarks
  •   httpfilter
  •   kfile
  •   kio
  •   kioexec
  •   kpasswdserver
  •   kssl
  • kioslave
  •   http
  • kjs
  • kmdi
  •   kmdi
  • knewstuff
  • kparts
  • krandr
  • kresources
  • kspell2
  • kunittest
  • kutils
  • kwallet
  • libkmid
  • libkscreensaver
Generated for kio/kio by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |