00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <stdlib.h>
00022 #include <time.h>
00023
00024
00025
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
00067 if ( mimetype == "application/x-tgz" || mimetype == "application/x-targz" ||
00068 mimetype == "application/x-webarchive" )
00069
00070 mimetype = "application/x-gzip";
00071 else if ( mimetype == "application/x-tbz" )
00072 mimetype = "application/x-bzip2";
00073 else
00074 {
00075
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
00126 if( isOpened() )
00127 close();
00128 if ( !m_filename.isEmpty() )
00129 delete device();
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
00145 Q_LONG n = device()->readBlock( buffer, 0x200 );
00146 if ( n == 0x200 && buffer[0] != 0 ) {
00147
00148 if (strncmp(buffer + 257, "ustar", 5)) {
00149
00150 QCString s;
00151
00152 int check = 0;
00153 for( uint j = 0; j < 0x200; ++j )
00154 check += buffer[j];
00155
00156
00157 for( uint j = 0; j < 8 ; j++ )
00158 check -= buffer[148 + j];
00159 check += 8 * ' ';
00160
00161 s.sprintf("%o", check );
00162
00163
00164
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 }
00170 } else {
00171
00172 if (n == 0x200) n = 0;
00173 }
00174 return n;
00175 }
00176
00177 bool KTar::readLonglink(char *buffer,QCString &longlink) {
00178 Q_LONG n = 0;
00179 QIODevice *dev = device();
00180
00181
00182 buffer[ 0x88 ] = 0;
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--;
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 }
00199
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
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 }
00223 } else {
00224 break;
00225 }
00226 }
00227
00228
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
00243
00244
00245
00246
00247 d->dirList.clear();
00248 QIODevice* dev = device();
00249
00250
00251 char buffer[ 0x200 ];
00252 bool ende = false;
00253 do
00254 {
00255 QString name;
00256 QString symlink;
00257
00258
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
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
00286 QString user( buffer + 0x109 );
00287 QString group( buffer + 0x129 );
00288
00289
00290 buffer[ 0x93 ] = 0;
00291 p = buffer + 0x88;
00292 while( *p == ' ' ) ++p;
00293 int time = (int)strtol( p, &dummy, 8 );
00294
00295
00296 char typeflag = buffer[ 0x9c ];
00297
00298
00299
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
00310
00311
00312 if (isdir)
00313 access |= S_IFDIR;
00314
00315 KArchiveEntry* e;
00316 if ( isdir )
00317 {
00318
00319 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
00320 }
00321 else
00322 {
00323
00324 buffer[ 0x88 ] = 0;
00325 char *dummy;
00326 const char* p = buffer + 0x7c;
00327 while( *p == ' ' ) ++p;
00328 int size = (int)strtol( p, &dummy, 8 );
00329
00330
00331 if ( isDumpDir )
00332 {
00333 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
00334 }
00335 else
00336 {
00337
00338
00339 if ( typeflag == '1' )
00340 {
00341 size = nm.length();
00342 kdDebug(7041) << "HARD LINK, setting size to " << size << endl;
00343 }
00344
00345
00346
00347 e = new KArchiveFile( this, nm, access, time, user, group, symlink,
00348 dev->at(), size );
00349 }
00350
00351
00352 int rest = size % 0x200;
00353 int skip = size + (rest ? 0x200 - rest : 0);
00354
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 == "." )
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
00373 QString path = QDir::cleanDirPath( name.left( pos ) );
00374
00375 KArchiveDirectory * d = findOrCreate( path );
00376 d->addEntry( e );
00377 }
00378 }
00379 else
00380 {
00381
00382 d->tarEnd = dev->at() - n;
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
00414 QString dirName ( QDir::cleanDirPath( name ) );
00415
00416
00417 if ( dirName.right(1) != "/" )
00418 dirName += "/";
00419
00420 if ( d->dirList.contains( dirName ) )
00421 return true;
00422
00423 char buffer[ 0x201 ];
00424 memset( buffer, 0, 0x200 );
00425 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd);
00426
00427
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
00436 device()->writeBlock( buffer, 0x200 );
00437
00438 }
00439 else
00440 {
00441
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
00449 device()->writeBlock( buffer, 0x200 );
00450 if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
00451
00452 d->dirList.append( dirName );
00453 return true;
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
00468 int rest = size % 0x200;
00469 if ( mode() & IO_ReadWrite )
00470 d->tarEnd = device()->at() + (rest ? 0x200 - rest : 0);
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
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
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
00510 assert( strlen(mode) == 6 );
00511 strcpy( buffer+0x64, mode );
00512 buffer[ 0x6a ] = ' ';
00513 buffer[ 0x6b ] = '\0';
00514
00515
00516 strcpy( buffer + 0x6c, " 765 ");
00517
00518 strcpy( buffer + 0x74, " 144 ");
00519
00520
00521 QCString s;
00522 s.sprintf("%o", size);
00523 s = s.rightJustify( 11, ' ' );
00524 strcpy( buffer + 0x7c, s.data() );
00525 buffer[ 0x87 ] = ' ';
00526
00527
00528 s.sprintf("%lo", static_cast<unsigned long>(mtime) );
00529 s = s.rightJustify( 11, ' ' );
00530 strcpy( buffer + 0x88, s.data() );
00531 buffer[ 0x93 ] = ' ';
00532
00533
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
00542
00543
00544
00545
00546 buffer[ 0x9a ] = '\0';
00547 buffer[ 0x9b ] = ' ';
00548
00549
00550 buffer[ 0x9c ] = typeflag;
00551
00552
00553 strcpy( buffer + 0x101, "ustar");
00554 strcpy( buffer + 0x107, "00" );
00555
00556
00557 strcpy( buffer + 0x109, uname );
00558
00559 strcpy( buffer + 0x129, gname );
00560
00561
00562 int check = 32;
00563 for( uint j = 0; j < 0x200; ++j )
00564 check += buffer[j];
00565 s.sprintf("%o", check );
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
00581 device()->writeBlock( buffer, 0x200 );
00582
00583 namelen -= chunksize;
00584 offset += 0x200;
00585 }
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 , time_t mtime, time_t ) {
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
00610 QString fileName ( QDir::cleanDirPath( name ) );
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631 char buffer[ 0x201 ];
00632 memset( buffer, 0, 0x200 );
00633 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd);
00634
00635
00636 QCString encodedFilename = QFile::encodeName(fileName);
00637 QCString uname = user.local8Bit();
00638 QCString gname = group.local8Bit();
00639
00640
00641 if ( fileName.length() > 99 )
00642 writeLonglink(buffer,encodedFilename,'L',uname,gname);
00643
00644
00645 strncpy( buffer, encodedFilename, 99 );
00646 buffer[99] = 0;
00647
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
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 , time_t mtime, time_t ) {
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
00681 QString dirName ( QDir::cleanDirPath( name ) );
00682
00683
00684 if ( dirName.right(1) != "/" )
00685 dirName += "/";
00686
00687 if ( d->dirList.contains( dirName ) )
00688 return true;
00689
00690 char buffer[ 0x201 ];
00691 memset( buffer, 0, 0x200 );
00692 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd);
00693
00694
00695 QCString encodedDirname = QFile::encodeName(dirName);
00696 QCString uname = user.local8Bit();
00697 QCString gname = group.local8Bit();
00698
00699
00700 if ( dirName.length() > 99 )
00701 writeLonglink(buffer,encodedDirname,'L',uname,gname);
00702
00703
00704 strncpy( buffer, encodedDirname, 99 );
00705 buffer[99] = 0;
00706
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
00715 device()->writeBlock( buffer, 0x200 );
00716 if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
00717
00718 d->dirList.append( dirName );
00719 return true;
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 , time_t mtime, time_t ) {
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
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);
00751
00752
00753 QCString encodedFilename = QFile::encodeName(fileName);
00754 QCString encodedTarget = QFile::encodeName(target);
00755 QCString uname = user.local8Bit();
00756 QCString gname = group.local8Bit();
00757
00758
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
00765 strncpy( buffer, encodedFilename, 99 );
00766 buffer[99] = 0;
00767
00768 strncpy(buffer+0x9d, encodedTarget, 99);
00769 buffer[0x9d+99] = 0;
00770
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
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 }
00810 }
00811