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 if ( ! filterDev->open( IO_ReadOnly ) ) 00299 { 00300 delete filterDev; 00301 return false; 00302 } 00303 Q_LONG len; 00304 while ( !filterDev->atEnd() ) { 00305 len = filterDev->readBlock(buffer.data(),buffer.size()); 00306 if ( len <= 0 ) { // corrupted archive 00307 delete filterDev; 00308 return false; 00309 } 00310 file->writeBlock(buffer.data(),len); 00311 } 00312 filterDev->close(); 00313 delete filterDev; 00314 00315 file->close(); 00316 if ( ! file->open( IO_ReadOnly ) ) 00317 return false; 00318 } 00319 else 00320 kdDebug( 7041 ) << "KTar::openArchive: no filterdevice found!" << endl; 00321 00322 kdDebug( 7041 ) << "KTar::openArchive: filling tmpFile finished." << endl; 00323 return true; 00324 } 00325 00326 bool KTar::openArchive( int mode ) 00327 { 00328 kdDebug( 7041 ) << "KTar::openArchive" << endl; 00329 if ( !(mode & IO_ReadOnly) ) 00330 return true; 00331 00332 if ( !d->fillTempFile( m_filename ) ) 00333 return false; 00334 00335 // We'll use the permission and user/group of d->rootDir 00336 // for any directory we emulate (see findOrCreate) 00337 //struct stat buf; 00338 //stat( m_filename, &buf ); 00339 00340 d->dirList.clear(); 00341 QIODevice* dev = device(); 00342 00343 if ( !dev ) 00344 return false; 00345 00346 // read dir infos 00347 char buffer[ 0x200 ]; 00348 bool ende = false; 00349 do 00350 { 00351 QString name; 00352 QString symlink; 00353 00354 // Read header 00355 Q_LONG n = readHeader(buffer,name,symlink); 00356 if (n < 0) return false; 00357 if (n == 0x200) 00358 { 00359 bool isdir = false; 00360 QString nm; 00361 00362 if ( name.right(1) == "/" ) 00363 { 00364 isdir = true; 00365 name = name.left( name.length() - 1 ); 00366 } 00367 00368 int pos = name.findRev( '/' ); 00369 if ( pos == -1 ) 00370 nm = name; 00371 else 00372 nm = name.mid( pos + 1 ); 00373 00374 // read access 00375 buffer[ 0x6b ] = 0; 00376 char *dummy; 00377 const char* p = buffer + 0x64; 00378 while( *p == ' ' ) ++p; 00379 int access = (int)strtol( p, &dummy, 8 ); 00380 00381 // read user and group 00382 QString user( buffer + 0x109 ); 00383 QString group( buffer + 0x129 ); 00384 00385 // read time 00386 buffer[ 0x93 ] = 0; 00387 p = buffer + 0x88; 00388 while( *p == ' ' ) ++p; 00389 int time = (int)strtol( p, &dummy, 8 ); 00390 00391 // read type flag 00392 char typeflag = buffer[ 0x9c ]; 00393 // '0' for files, '1' hard link, '2' symlink, '5' for directory 00394 // (and 'L' for longlink filenames, 'K' for longlink symlink targets) 00395 // and 'D' for GNU tar extension DUMPDIR 00396 if ( typeflag == '1' ) 00397 isdir = true; 00398 00399 bool isDumpDir = false; 00400 if ( typeflag == 'D' ) 00401 { 00402 isdir = false; 00403 isDumpDir = true; 00404 } 00405 //bool islink = ( typeflag == '1' || typeflag == '2' ); 00406 //kdDebug(7041) << "typeflag=" << typeflag << " islink=" << islink << endl; 00407 00408 if (isdir) 00409 access |= S_IFDIR; // f*cking broken tar files 00410 00411 KArchiveEntry* e; 00412 if ( isdir ) 00413 { 00414 //kdDebug(7041) << "KArchive::open directory " << nm << endl; 00415 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink ); 00416 } 00417 else 00418 { 00419 // read size 00420 buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437 00421 char *dummy; 00422 const char* p = buffer + 0x7c; 00423 while( *p == ' ' ) ++p; 00424 int size = (int)strtol( p, &dummy, 8 ); 00425 00426 // for isDumpDir we will skip the additional info about that dirs contents 00427 if ( isDumpDir ) 00428 { 00429 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink ); 00430 } 00431 else 00432 { 00433 00434 // Let's hack around hard links. Our classes don't support that, so make them symlinks 00435 if ( typeflag == '1' ) 00436 { 00437 size = nm.length(); // in any case, we don't want to skip the real size, hence this resetting of size 00438 kdDebug(7041) << "HARD LINK, setting size to " << size << endl; 00439 } 00440 00441 // kdDebug(7041) << "KArchive::open file " << nm << " size=" << size << endl; 00442 e = new KArchiveFile( this, nm, access, time, user, group, symlink, 00443 dev->at(), size ); 00444 } 00445 00446 // Skip contents + align bytes 00447 int rest = size % 0x200; 00448 int skip = size + (rest ? 0x200 - rest : 0); 00449 //kdDebug(7041) << "KArchive::open, at()=" << dev->at() << " rest=" << rest << " skipping " << skip << endl; 00450 if (! dev->at( dev->at() + skip ) ) 00451 kdWarning(7041) << "KArchive::open skipping " << skip << " failed" << endl; 00452 } 00453 00454 if ( pos == -1 ) 00455 { 00456 if ( nm == "." ) // special case 00457 { 00458 Q_ASSERT( isdir ); 00459 if ( isdir ) 00460 setRootDir( static_cast<KArchiveDirectory *>( e ) ); 00461 } 00462 else 00463 rootDir()->addEntry( e ); 00464 } 00465 else 00466 { 00467 // In some tar files we can find dir/./file => call cleanDirPath 00468 QString path = QDir::cleanDirPath( name.left( pos ) ); 00469 // Ensure container directory exists, create otherwise 00470 KArchiveDirectory * d = findOrCreate( path ); 00471 d->addEntry( e ); 00472 } 00473 } 00474 else 00475 { 00476 //qDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]); 00477 d->tarEnd = dev->at() - n; // Remember end of archive 00478 ende = true; 00479 } 00480 } while( !ende ); 00481 return true; 00482 } 00483 00484 /* 00485 * Writes back the changes of the temporary file 00486 * to the original file. 00487 * Must only be called if in IO_WriteOnly mode 00488 */ 00489 bool KTar::KTarPrivate::writeBackTempFile( const QString & filename ) { 00490 if ( ! tmpFile ) 00491 return true; 00492 00493 kdDebug(7041) << "Write temporary file to compressed file" << endl; 00494 kdDebug(7041) << filename << " " << mimetype << endl; 00495 00496 bool forced = false; 00497 if( "application/x-gzip" == mimetype 00498 || "application/x-bzip2" == mimetype) 00499 forced = true; 00500 00501 00502 QIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced ); 00503 if( dev ) { 00504 QFile* file = tmpFile->file(); 00505 file->close(); 00506 if ( ! file->open(IO_ReadOnly) || ! dev->open(IO_WriteOnly) ) 00507 { 00508 file->close(); 00509 delete dev; 00510 return false; 00511 } 00512 if ( forced ) 00513 static_cast<KFilterDev *>(dev)->setOrigFileName( origFileName ); 00514 QByteArray buffer(8*1024); 00515 Q_LONG len; 00516 while ( ! file->atEnd()) { 00517 len = file->readBlock(buffer.data(),buffer.size()); 00518 dev->writeBlock(buffer.data(),len); 00519 } 00520 file->close(); 00521 dev->close(); 00522 delete dev; 00523 } 00524 00525 kdDebug(7041) << "Write temporary file to compressed file done." << endl; 00526 return true; 00527 } 00528 00529 bool KTar::closeArchive() 00530 { 00531 d->dirList.clear(); 00532 00533 // If we are in write mode and had created 00534 // a temporary tar file, we have to write 00535 // back the changes to the original file 00536 if( mode() == IO_WriteOnly) 00537 return d->writeBackTempFile( m_filename ); 00538 00539 return true; 00540 } 00541 00542 bool KTar::writeDir( const QString& name, const QString& user, const QString& group ) 00543 { 00544 mode_t perm = 040755; 00545 time_t the_time = time(0); 00546 return writeDir(name,user,group,perm,the_time,the_time,the_time); 00547 #if 0 00548 if ( !isOpened() ) 00549 { 00550 kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n"; 00551 return false; 00552 } 00553 00554 if ( !(mode() & IO_WriteOnly) ) 00555 { 00556 kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n"; 00557 return false; 00558 } 00559 00560 // In some tar files we can find dir/./ => call cleanDirPath 00561 QString dirName ( QDir::cleanDirPath( name ) ); 00562 00563 // Need trailing '/' 00564 if ( dirName.right(1) != "/" ) 00565 dirName += "/"; 00566 00567 if ( d->dirList.contains( dirName ) ) 00568 return true; // already there 00569 00570 char buffer[ 0x201 ]; 00571 memset( buffer, 0, 0x200 ); 00572 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read 00573 00574 // If more than 100 chars, we need to use the LongLink trick 00575 if ( dirName.length() > 99 ) 00576 { 00577 strcpy( buffer, "././@LongLink" ); 00578 fillBuffer( buffer, " 0", dirName.length()+1, 'L', user.local8Bit(), group.local8Bit() ); 00579 device()->writeBlock( buffer, 0x200 ); 00580 strncpy( buffer, QFile::encodeName(dirName), 0x200 ); 00581 buffer[0x200] = 0; 00582 // write long name 00583 device()->writeBlock( buffer, 0x200 ); 00584 // not even needed to reclear the buffer, tar doesn't do it 00585 } 00586 else 00587 { 00588 // Write name 00589 strncpy( buffer, QFile::encodeName(dirName), 0x200 ); 00590 buffer[0x200] = 0; 00591 } 00592 00593 fillBuffer( buffer, " 40755", 0, 0x35, user.local8Bit(), group.local8Bit()); 00594 00595 // Write header 00596 device()->writeBlock( buffer, 0x200 ); 00597 if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at(); 00598 00599 d->dirList.append( dirName ); // contains trailing slash 00600 return true; // TODO if wanted, better error control 00601 #endif 00602 } 00603 00604 bool KTar::prepareWriting( const QString& name, const QString& user, const QString& group, uint size ) 00605 { 00606 mode_t dflt_perm = 0100644; 00607 time_t the_time = time(0); 00608 return prepareWriting(name,user,group,size,dflt_perm, 00609 the_time,the_time,the_time); 00610 } 00611 00612 bool KTar::doneWriting( uint size ) 00613 { 00614 // Write alignment 00615 int rest = size % 0x200; 00616 if ( mode() & IO_ReadWrite ) 00617 d->tarEnd = device()->at() + (rest ? 0x200 - rest : 0); // Record our new end of archive 00618 if ( rest ) 00619 { 00620 char buffer[ 0x201 ]; 00621 for( uint i = 0; i < 0x200; ++i ) 00622 buffer[i] = 0; 00623 Q_LONG nwritten = device()->writeBlock( buffer, 0x200 - rest ); 00624 return nwritten == 0x200 - rest; 00625 } 00626 return true; 00627 } 00628 00629 /*** Some help from the tar sources 00630 struct posix_header 00631 { byte offset 00632 char name[100]; * 0 * 0x0 00633 char mode[8]; * 100 * 0x64 00634 char uid[8]; * 108 * 0x6c 00635 char gid[8]; * 116 * 0x74 00636 char size[12]; * 124 * 0x7c 00637 char mtime[12]; * 136 * 0x88 00638 char chksum[8]; * 148 * 0x94 00639 char typeflag; * 156 * 0x9c 00640 char linkname[100]; * 157 * 0x9d 00641 char magic[6]; * 257 * 0x101 00642 char version[2]; * 263 * 0x107 00643 char uname[32]; * 265 * 0x109 00644 char gname[32]; * 297 * 0x129 00645 char devmajor[8]; * 329 * 0x149 00646 char devminor[8]; * 337 * ... 00647 char prefix[155]; * 345 * 00648 * 500 * 00649 }; 00650 */ 00651 00652 void KTar::fillBuffer( char * buffer, 00653 const char * mode, int size, time_t mtime, char typeflag, 00654 const char * uname, const char * gname ) 00655 { 00656 // mode (as in stat()) 00657 assert( strlen(mode) == 6 ); 00658 strcpy( buffer+0x64, mode ); 00659 buffer[ 0x6a ] = ' '; 00660 buffer[ 0x6b ] = '\0'; 00661 00662 // dummy uid 00663 strcpy( buffer + 0x6c, " 765 "); 00664 // dummy gid 00665 strcpy( buffer + 0x74, " 144 "); 00666 00667 // size 00668 QCString s; 00669 s.sprintf("%o", size); // OCT 00670 s = s.rightJustify( 11, ' ' ); 00671 strcpy( buffer + 0x7c, s.data() ); 00672 buffer[ 0x87 ] = ' '; // space-terminate (no null after) 00673 00674 // modification time 00675 s.sprintf("%lo", static_cast<unsigned long>(mtime) ); // OCT 00676 s = s.rightJustify( 11, ' ' ); 00677 strcpy( buffer + 0x88, s.data() ); 00678 buffer[ 0x93 ] = ' '; // space-terminate (no null after) 00679 00680 // spaces, replaced by the check sum later 00681 buffer[ 0x94 ] = 0x20; 00682 buffer[ 0x95 ] = 0x20; 00683 buffer[ 0x96 ] = 0x20; 00684 buffer[ 0x97 ] = 0x20; 00685 buffer[ 0x98 ] = 0x20; 00686 buffer[ 0x99 ] = 0x20; 00687 00688 /* From the tar sources : 00689 Fill in the checksum field. It's formatted differently from the 00690 other fields: it has [6] digits, a null, then a space -- rather than 00691 digits, a space, then a null. */ 00692 00693 buffer[ 0x9a ] = '\0'; 00694 buffer[ 0x9b ] = ' '; 00695 00696 // type flag (dir, file, link) 00697 buffer[ 0x9c ] = typeflag; 00698 00699 // magic + version 00700 strcpy( buffer + 0x101, "ustar"); 00701 strcpy( buffer + 0x107, "00" ); 00702 00703 // user 00704 strcpy( buffer + 0x109, uname ); 00705 // group 00706 strcpy( buffer + 0x129, gname ); 00707 00708 // Header check sum 00709 int check = 32; 00710 for( uint j = 0; j < 0x200; ++j ) 00711 check += buffer[j]; 00712 s.sprintf("%o", check ); // OCT 00713 s = s.rightJustify( 7, ' ' ); 00714 strcpy( buffer + 0x94, s.data() ); 00715 } 00716 00717 void KTar::writeLonglink(char *buffer, const QCString &name, char typeflag, 00718 const char *uname, const char *gname) { 00719 strcpy( buffer, "././@LongLink" ); 00720 int namelen = name.length() + 1; 00721 fillBuffer( buffer, " 0", namelen, 0, typeflag, uname, gname ); 00722 device()->writeBlock( buffer, 0x200 ); 00723 int offset = 0; 00724 while (namelen > 0) { 00725 int chunksize = QMIN(namelen, 0x200); 00726 memcpy(buffer, name.data()+offset, chunksize); 00727 // write long name 00728 device()->writeBlock( buffer, 0x200 ); 00729 // not even needed to reclear the buffer, tar doesn't do it 00730 namelen -= chunksize; 00731 offset += 0x200; 00732 }/*wend*/ 00733 } 00734 00735 bool KTar::prepareWriting(const QString& name, const QString& user, 00736 const QString& group, uint size, mode_t perm, 00737 time_t atime, time_t mtime, time_t ctime) { 00738 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime); 00739 } 00740 00741 bool KTar::prepareWriting_impl(const QString &name, const QString &user, 00742 const QString &group, uint size, mode_t perm, 00743 time_t /*atime*/, time_t mtime, time_t /*ctime*/) { 00744 if ( !isOpened() ) 00745 { 00746 kdWarning(7041) << "KTar::prepareWriting: You must open the tar file before writing to it\n"; 00747 return false; 00748 } 00749 00750 if ( !(mode() & IO_WriteOnly) ) 00751 { 00752 kdWarning(7041) << "KTar::prepareWriting: You must open the tar file for writing\n"; 00753 return false; 00754 } 00755 00756 // In some tar files we can find dir/./file => call cleanDirPath 00757 QString fileName ( QDir::cleanDirPath( name ) ); 00758 00759 /* 00760 // Create toplevel dirs 00761 // Commented out by David since it's not necessary, and if anybody thinks it is, 00762 // he needs to implement a findOrCreate equivalent in writeDir. 00763 // But as KTar and the "tar" program both handle tar files without 00764 // dir entries, there's really no need for that 00765 QString tmp ( fileName ); 00766 int i = tmp.findRev( '/' ); 00767 if ( i != -1 ) 00768 { 00769 QString d = tmp.left( i + 1 ); // contains trailing slash 00770 if ( !m_dirList.contains( d ) ) 00771 { 00772 tmp = tmp.mid( i + 1 ); 00773 writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs 00774 } 00775 } 00776 */ 00777 00778 char buffer[ 0x201 ]; 00779 memset( buffer, 0, 0x200 ); 00780 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read 00781 00782 // provide converted stuff we need lateron 00783 QCString encodedFilename = QFile::encodeName(fileName); 00784 QCString uname = user.local8Bit(); 00785 QCString gname = group.local8Bit(); 00786 00787 // If more than 100 chars, we need to use the LongLink trick 00788 if ( fileName.length() > 99 ) 00789 writeLonglink(buffer,encodedFilename,'L',uname,gname); 00790 00791 // Write (potentially truncated) name 00792 strncpy( buffer, encodedFilename, 99 ); 00793 buffer[99] = 0; 00794 // zero out the rest (except for what gets filled anyways) 00795 memset(buffer+0x9d, 0, 0x200 - 0x9d); 00796 00797 QCString permstr; 00798 permstr.sprintf("%o",perm); 00799 permstr.rightJustify(6, ' '); 00800 fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname); 00801 00802 // Write header 00803 return device()->writeBlock( buffer, 0x200 ) == 0x200; 00804 } 00805 00806 bool KTar::writeDir(const QString& name, const QString& user, 00807 const QString& group, mode_t perm, 00808 time_t atime, time_t mtime, time_t ctime) { 00809 return KArchive::writeDir(name,user,group,perm,atime,mtime,ctime); 00810 } 00811 00812 bool KTar::writeDir_impl(const QString &name, const QString &user, 00813 const QString &group, mode_t perm, 00814 time_t /*atime*/, time_t mtime, time_t /*ctime*/) { 00815 if ( !isOpened() ) 00816 { 00817 kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n"; 00818 return false; 00819 } 00820 00821 if ( !(mode() & IO_WriteOnly) ) 00822 { 00823 kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n"; 00824 return false; 00825 } 00826 00827 // In some tar files we can find dir/./ => call cleanDirPath 00828 QString dirName ( QDir::cleanDirPath( name ) ); 00829 00830 // Need trailing '/' 00831 if ( dirName.right(1) != "/" ) 00832 dirName += "/"; 00833 00834 if ( d->dirList.contains( dirName ) ) 00835 return true; // already there 00836 00837 char buffer[ 0x201 ]; 00838 memset( buffer, 0, 0x200 ); 00839 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read 00840 00841 // provide converted stuff we need lateron 00842 QCString encodedDirname = QFile::encodeName(dirName); 00843 QCString uname = user.local8Bit(); 00844 QCString gname = group.local8Bit(); 00845 00846 // If more than 100 chars, we need to use the LongLink trick 00847 if ( dirName.length() > 99 ) 00848 writeLonglink(buffer,encodedDirname,'L',uname,gname); 00849 00850 // Write (potentially truncated) name 00851 strncpy( buffer, encodedDirname, 99 ); 00852 buffer[99] = 0; 00853 // zero out the rest (except for what gets filled anyways) 00854 memset(buffer+0x9d, 0, 0x200 - 0x9d); 00855 00856 QCString permstr; 00857 permstr.sprintf("%o",perm); 00858 permstr.rightJustify(6, ' '); 00859 fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname); 00860 00861 // Write header 00862 device()->writeBlock( buffer, 0x200 ); 00863 if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at(); 00864 00865 d->dirList.append( dirName ); // contains trailing slash 00866 return true; // TODO if wanted, better error control 00867 } 00868 00869 bool KTar::writeSymLink(const QString &name, const QString &target, 00870 const QString &user, const QString &group, 00871 mode_t perm, time_t atime, time_t mtime, time_t ctime) { 00872 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime); 00873 } 00874 00875 bool KTar::writeSymLink_impl(const QString &name, const QString &target, 00876 const QString &user, const QString &group, 00877 mode_t perm, time_t /*atime*/, time_t mtime, time_t /*ctime*/) { 00878 if ( !isOpened() ) 00879 { 00880 kdWarning(7041) << "KTar::writeSymLink: You must open the tar file before writing to it\n"; 00881 return false; 00882 } 00883 00884 if ( !(mode() & IO_WriteOnly) ) 00885 { 00886 kdWarning(7041) << "KTar::writeSymLink: You must open the tar file for writing\n"; 00887 return false; 00888 } 00889 00890 device()->flush(); 00891 00892 // In some tar files we can find dir/./file => call cleanDirPath 00893 QString fileName ( QDir::cleanDirPath( name ) ); 00894 00895 char buffer[ 0x201 ]; 00896 memset( buffer, 0, 0x200 ); 00897 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read 00898 00899 // provide converted stuff we need lateron 00900 QCString encodedFilename = QFile::encodeName(fileName); 00901 QCString encodedTarget = QFile::encodeName(target); 00902 QCString uname = user.local8Bit(); 00903 QCString gname = group.local8Bit(); 00904 00905 // If more than 100 chars, we need to use the LongLink trick 00906 if (target.length() > 99) 00907 writeLonglink(buffer,encodedTarget,'K',uname,gname); 00908 if ( fileName.length() > 99 ) 00909 writeLonglink(buffer,encodedFilename,'L',uname,gname); 00910 00911 // Write (potentially truncated) name 00912 strncpy( buffer, encodedFilename, 99 ); 00913 buffer[99] = 0; 00914 // Write (potentially truncated) symlink target 00915 strncpy(buffer+0x9d, encodedTarget, 99); 00916 buffer[0x9d+99] = 0; 00917 // zero out the rest 00918 memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d); 00919 00920 QCString permstr; 00921 permstr.sprintf("%o",perm); 00922 permstr.rightJustify(6, ' '); 00923 fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname); 00924 00925 // Write header 00926 bool retval = device()->writeBlock( buffer, 0x200 ) == 0x200; 00927 if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at(); 00928 return retval; 00929 } 00930 00931 void KTar::virtual_hook( int id, void* data ) { 00932 switch (id) { 00933 case VIRTUAL_WRITE_SYMLINK: { 00934 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data); 00935 params->retval = writeSymLink_impl(*params->name,*params->target, 00936 *params->user,*params->group,params->perm, 00937 params->atime,params->mtime,params->ctime); 00938 break; 00939 } 00940 case VIRTUAL_WRITE_DIR: { 00941 WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data); 00942 params->retval = writeDir_impl(*params->name,*params->user, 00943 *params->group,params->perm, 00944 params->atime,params->mtime,params->ctime); 00945 break; 00946 } 00947 case VIRTUAL_PREPARE_WRITING: { 00948 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data); 00949 params->retval = prepareWriting_impl(*params->name,*params->user, 00950 *params->group,params->size,params->perm, 00951 params->atime,params->mtime,params->ctime); 00952 break; 00953 } 00954 default: 00955 KArchive::virtual_hook( id, data ); 00956 }/*end switch*/ 00957 } 00958
KDE Logo
This file is part of the documentation for kio Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Apr 12 23:09:28 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003