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.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 10 18:55:28 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003