00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "karchive.h"
00023 #include "klimitediodevice.h"
00024
00025 #include <config.h>
00026
00027 #include <kdebug.h>
00028 #include <ksavefile.h>
00029 #include <kde_file.h>
00030
00031 #include <QStack>
00032 #include <QtCore/QMap>
00033 #include <QtCore/QDir>
00034 #include <QtCore/QFile>
00035
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <time.h>
00039 #include <unistd.h>
00040 #include <errno.h>
00041 #include <grp.h>
00042 #include <pwd.h>
00043 #include <assert.h>
00044 #include <sys/types.h>
00045 #include <sys/stat.h>
00046 #ifdef Q_OS_UNIX
00047 #include <limits.h>
00048 #endif
00049
00050 class KArchivePrivate
00051 {
00052 public:
00053 KArchivePrivate()
00054 : rootDir( 0 ),
00055 saveFile( 0 ),
00056 dev ( 0 ),
00057 fileName(),
00058 mode( QIODevice::NotOpen ),
00059 deviceOwned( false )
00060 {}
00061 ~KArchivePrivate()
00062 {
00063 delete saveFile;
00064 delete rootDir;
00065 }
00066 void abortWriting();
00067
00068 KArchiveDirectory* rootDir;
00069 KSaveFile* saveFile;
00070 QIODevice * dev;
00071 QString fileName;
00072 QIODevice::OpenMode mode;
00073 bool deviceOwned;
00074 };
00075
00076
00080
00081 KArchive::KArchive( const QString& fileName )
00082 : d(new KArchivePrivate)
00083 {
00084 Q_ASSERT( !fileName.isEmpty() );
00085 d->fileName = fileName;
00086
00087
00088 }
00089
00090 KArchive::KArchive( QIODevice * dev )
00091 : d(new KArchivePrivate)
00092 {
00093 d->dev = dev;
00094 }
00095
00096 KArchive::~KArchive()
00097 {
00098 if ( isOpen() )
00099 close();
00100
00101 delete d;
00102 }
00103
00104 bool KArchive::open( QIODevice::OpenMode mode )
00105 {
00106 Q_ASSERT( mode != QIODevice::NotOpen );
00107
00108 if ( isOpen() )
00109 close();
00110
00111 if ( !d->fileName.isEmpty() )
00112 {
00113 Q_ASSERT( !d->dev );
00114 if ( !createDevice( mode ) )
00115 return false;
00116 }
00117
00118 Q_ASSERT( d->dev );
00119
00120 if ( !d->dev->isOpen() && !d->dev->open( mode ) )
00121 return false;
00122
00123 d->mode = mode;
00124
00125 Q_ASSERT( !d->rootDir );
00126 d->rootDir = 0;
00127
00128 return openArchive( mode );
00129 }
00130
00131 bool KArchive::createDevice( QIODevice::OpenMode mode )
00132 {
00133 switch( mode ) {
00134 case QIODevice::WriteOnly:
00135 if ( !d->fileName.isEmpty() ) {
00136
00137
00138 d->saveFile = new KSaveFile( d->fileName );
00139 if ( !d->saveFile->open() ) {
00140 kWarning() << "KSaveFile creation for " << d->fileName << " failed, " << d->saveFile->errorString();
00141 delete d->saveFile;
00142 d->saveFile = 0;
00143 return false;
00144 }
00145 d->dev = d->saveFile;
00146 Q_ASSERT( d->dev );
00147 }
00148 break;
00149 case QIODevice::ReadOnly:
00150 case QIODevice::ReadWrite:
00151
00152 if ( !d->fileName.isEmpty() ) {
00153 d->dev = new QFile( d->fileName );
00154 d->deviceOwned = true;
00155 }
00156 break;
00157 default:
00158 kWarning() << "Unsupported mode " << d->mode;
00159 return false;
00160 }
00161 return true;
00162 }
00163
00164 bool KArchive::close()
00165 {
00166 if ( !isOpen() )
00167 return false;
00168
00169
00170
00171
00172 bool closeSucceeded = true;
00173 if ( d->dev ) {
00174 closeSucceeded = closeArchive();
00175 if ( d->mode == QIODevice::WriteOnly && !closeSucceeded )
00176 d->abortWriting();
00177 }
00178
00179 if ( d->dev )
00180 d->dev->close();
00181
00182 if ( d->deviceOwned ) {
00183 delete d->dev;
00184 }
00185 if ( d->saveFile ) {
00186 closeSucceeded = d->saveFile->finalize();
00187 delete d->saveFile;
00188 d->saveFile = 0;
00189 }
00190
00191 delete d->rootDir;
00192 d->rootDir = 0;
00193 d->mode = QIODevice::NotOpen;
00194 d->dev = 0;
00195 return closeSucceeded;
00196 }
00197
00198 const KArchiveDirectory* KArchive::directory() const
00199 {
00200
00201 return const_cast<KArchive *>(this)->rootDir();
00202 }
00203
00204
00205 bool KArchive::addLocalFile( const QString& fileName, const QString& destName )
00206 {
00207 QFileInfo fileInfo( fileName );
00208 if ( !fileInfo.isFile() && !fileInfo.isSymLink() )
00209 {
00210 kWarning() << fileName << "doesn't exist or is not a regular file.";
00211 return false;
00212 }
00213
00214 KDE_struct_stat fi;
00215 if (KDE_lstat(QFile::encodeName(fileName),&fi) == -1) {
00216 kWarning() << "stat'ing" << fileName
00217 << "failed:" << strerror(errno);
00218 return false;
00219 }
00220
00221 if (fileInfo.isSymLink()) {
00222 QString symLinkTarget;
00223
00224
00225 #if defined(Q_OS_UNIX) && !defined(Q_OS_OS2EMX)
00226 QByteArray s;
00227 s.resize(PATH_MAX+1);
00228 int len = readlink( QFile::encodeName(fileName).data(), s.data(), PATH_MAX );
00229 if ( len >= 0 ) {
00230 s[len] = '\0';
00231 symLinkTarget = QFile::decodeName(s);
00232 }
00233 #endif
00234 if (symLinkTarget.isEmpty())
00235 symLinkTarget = fileInfo.symLinkTarget();
00236 return writeSymLink(destName, symLinkTarget, fileInfo.owner(),
00237 fileInfo.group(), fi.st_mode, fi.st_atime, fi.st_mtime,
00238 fi.st_ctime);
00239 }
00240
00241 qint64 size = fileInfo.size();
00242
00243
00244
00245
00246 QFile file( fileName );
00247 if ( !file.open( QIODevice::ReadOnly ) )
00248 {
00249 kWarning() << "couldn't open file " << fileName;
00250 return false;
00251 }
00252
00253 if ( !prepareWriting( destName, fileInfo.owner(), fileInfo.group(), size,
00254 fi.st_mode, fi.st_atime, fi.st_mtime, fi.st_ctime ) )
00255 {
00256 kWarning() << " prepareWriting" << destName << "failed";
00257 return false;
00258 }
00259
00260
00261 QByteArray array;
00262 array.resize(8*1024);
00263 qint64 n;
00264 qint64 total = 0;
00265 while ( ( n = file.read( array.data(), array.size() ) ) > 0 )
00266 {
00267 if ( !writeData( array.data(), n ) )
00268 {
00269 kWarning() << "writeData failed";
00270 return false;
00271 }
00272 total += n;
00273 }
00274 Q_ASSERT( total == size );
00275
00276 if ( !finishWriting( size ) )
00277 {
00278 kWarning() << "finishWriting failed";
00279 return false;
00280 }
00281 return true;
00282 }
00283
00284 bool KArchive::addLocalDirectory( const QString& path, const QString& destName )
00285 {
00286 QDir dir( path );
00287 if ( !dir.exists() )
00288 return false;
00289 dir.setFilter(dir.filter() | QDir::Hidden);
00290 const QStringList files = dir.entryList();
00291 for ( QStringList::ConstIterator it = files.begin(); it != files.end(); ++it )
00292 {
00293 if ( *it != "." && *it != ".." )
00294 {
00295 QString fileName = path + '/' + *it;
00296
00297 QString dest = destName.isEmpty() ? *it : (destName + '/' + *it);
00298 QFileInfo fileInfo( fileName );
00299
00300 if ( fileInfo.isFile() || fileInfo.isSymLink() )
00301 addLocalFile( fileName, dest );
00302 else if ( fileInfo.isDir() )
00303 addLocalDirectory( fileName, dest );
00304
00305 }
00306 }
00307 return true;
00308 }
00309
00310 bool KArchive::writeFile( const QString& name, const QString& user,
00311 const QString& group, const char* data, qint64 size,
00312 mode_t perm, time_t atime, time_t mtime, time_t ctime )
00313 {
00314 if ( !prepareWriting( name, user, group, size, perm, atime, mtime, ctime ) )
00315 {
00316 kWarning() << "prepareWriting failed";
00317 return false;
00318 }
00319
00320
00321
00322 if ( data && size && !writeData( data, size ) )
00323 {
00324 kWarning() << "writeData failed";
00325 return false;
00326 }
00327
00328 if ( !finishWriting( size ) )
00329 {
00330 kWarning() << "finishWriting failed";
00331 return false;
00332 }
00333 return true;
00334 }
00335
00336 bool KArchive::writeData( const char* data, qint64 size )
00337 {
00338 bool ok = device()->write( data, size ) == size;
00339 if ( !ok )
00340 d->abortWriting();
00341 return ok;
00342 }
00343
00344
00345
00346
00347
00348
00349
00350 bool KArchive::writeDir( const QString& name, const QString& user, const QString& group,
00351 mode_t perm, time_t atime,
00352 time_t mtime, time_t ctime )
00353 {
00354 return doWriteDir( name, user, group, perm, atime, mtime, ctime );
00355 }
00356
00357 bool KArchive::writeSymLink(const QString &name, const QString &target,
00358 const QString &user, const QString &group,
00359 mode_t perm, time_t atime,
00360 time_t mtime, time_t ctime )
00361 {
00362 return doWriteSymLink( name, target, user, group, perm, atime, mtime, ctime );
00363 }
00364
00365
00366 bool KArchive::prepareWriting( const QString& name, const QString& user,
00367 const QString& group, qint64 size,
00368 mode_t perm, time_t atime,
00369 time_t mtime, time_t ctime )
00370 {
00371 bool ok = doPrepareWriting( name, user, group, size, perm, atime, mtime, ctime );
00372 if ( !ok )
00373 d->abortWriting();
00374 return ok;
00375 }
00376
00377 bool KArchive::finishWriting( qint64 size )
00378 {
00379 return doFinishWriting( size );
00380 }
00381
00382 KArchiveDirectory * KArchive::rootDir()
00383 {
00384 if ( !d->rootDir )
00385 {
00386
00387 struct passwd* pw = getpwuid( getuid() );
00388 struct group* grp = getgrgid( getgid() );
00389 QString username = pw ? QFile::decodeName(pw->pw_name) : QString::number( getuid() );
00390 QString groupname = grp ? QFile::decodeName(grp->gr_name) : QString::number( getgid() );
00391
00392 d->rootDir = new KArchiveDirectory( this, QLatin1String("/"), (int)(0777 + S_IFDIR), 0, username, groupname, QString() );
00393 }
00394 return d->rootDir;
00395 }
00396
00397 KArchiveDirectory * KArchive::findOrCreate( const QString & path )
00398 {
00399
00400 if ( path.isEmpty() || path == "/" || path == "." )
00401 {
00402
00403 return rootDir();
00404 }
00405
00406
00407
00408
00409
00410
00411
00412 const KArchiveEntry* ent = rootDir()->entry( path );
00413 if ( ent )
00414 {
00415 if ( ent->isDirectory() )
00416
00417 return (KArchiveDirectory *) ent;
00418 else
00419 kWarning() << "Found" << path << "but it's not a directory";
00420 }
00421
00422
00423 int pos = path.lastIndexOf( '/' );
00424 KArchiveDirectory * parent;
00425 QString dirname;
00426 if ( pos == -1 )
00427 {
00428 parent = rootDir();
00429 dirname = path;
00430 }
00431 else
00432 {
00433 QString left = path.left( pos );
00434 dirname = path.mid( pos + 1 );
00435 parent = findOrCreate( left );
00436 }
00437
00438
00439
00440 KArchiveDirectory * e = new KArchiveDirectory( this, dirname, d->rootDir->permissions(),
00441 d->rootDir->date(), d->rootDir->user(),
00442 d->rootDir->group(), QString() );
00443 parent->addEntry( e );
00444 return e;
00445 }
00446
00447 void KArchive::setDevice( QIODevice * dev )
00448 {
00449 if ( d->deviceOwned )
00450 delete d->dev;
00451 d->dev = dev;
00452 d->deviceOwned = false;
00453 }
00454
00455 void KArchive::setRootDir( KArchiveDirectory *rootDir )
00456 {
00457 Q_ASSERT( !d->rootDir );
00458 d->rootDir = rootDir;
00459 }
00460
00461 QIODevice::OpenMode KArchive::mode() const
00462 {
00463 return d->mode;
00464 }
00465
00466 QIODevice * KArchive::device() const
00467 {
00468 return d->dev;
00469 }
00470
00471 bool KArchive::isOpen() const
00472 {
00473 return d->mode != QIODevice::NotOpen;
00474 }
00475
00476 QString KArchive::fileName() const
00477 {
00478 return d->fileName;
00479 }
00480
00481 void KArchivePrivate::abortWriting()
00482 {
00483 if ( saveFile ) {
00484 saveFile->abort();
00485 delete saveFile;
00486 saveFile = 0;
00487 dev = 0;
00488 }
00489 }
00490
00494
00495 class KArchiveEntryPrivate
00496 {
00497 public:
00498 KArchiveEntryPrivate( KArchive* _archive, const QString& _name, int _access,
00499 int _date, const QString& _user, const QString& _group,
00500 const QString& _symlink) :
00501 name(_name),
00502 date(_date),
00503 access(_access),
00504 user(_user),
00505 group(_group),
00506 symlink(_symlink),
00507 archive(_archive)
00508 {}
00509 QString name;
00510 int date;
00511 mode_t access;
00512 QString user;
00513 QString group;
00514 QString symlink;
00515 KArchive* archive;
00516 };
00517
00518 KArchiveEntry::KArchiveEntry( KArchive* t, const QString& name, int access, int date,
00519 const QString& user, const QString& group, const
00520 QString& symlink) :
00521 d(new KArchiveEntryPrivate(t,name,access,date,user,group,symlink))
00522 {
00523 }
00524
00525 KArchiveEntry::~KArchiveEntry()
00526 {
00527 delete d;
00528 }
00529
00530 QDateTime KArchiveEntry::datetime() const
00531 {
00532 QDateTime datetimeobj;
00533 datetimeobj.setTime_t( d->date );
00534 return datetimeobj;
00535 }
00536
00537 int KArchiveEntry::date() const
00538 {
00539 return d->date;
00540 }
00541
00542 QString KArchiveEntry::name() const
00543 {
00544 return d->name;
00545 }
00546
00547 mode_t KArchiveEntry::permissions() const
00548 {
00549 return d->access;
00550 }
00551
00552 QString KArchiveEntry::user() const
00553 {
00554 return d->user;
00555 }
00556
00557 QString KArchiveEntry::group() const
00558 {
00559 return d->group;
00560 }
00561
00562 QString KArchiveEntry::symLinkTarget() const
00563 {
00564 return d->symlink;
00565 }
00566
00567 bool KArchiveEntry::isFile() const
00568 {
00569 return false;
00570 }
00571
00572 bool KArchiveEntry::isDirectory() const
00573 {
00574 return false;
00575 }
00576
00577 KArchive* KArchiveEntry::archive() const
00578 {
00579 return d->archive;
00580 }
00581
00585
00586 class KArchiveFilePrivate
00587 {
00588 public:
00589 KArchiveFilePrivate( qint64 _pos, qint64 _size ) :
00590 pos(_pos),
00591 size(_size)
00592 {}
00593 qint64 pos;
00594 qint64 size;
00595 };
00596
00597 KArchiveFile::KArchiveFile( KArchive* t, const QString& name, int access, int date,
00598 const QString& user, const QString& group,
00599 const QString & symlink,
00600 qint64 pos, qint64 size )
00601 : KArchiveEntry( t, name, access, date, user, group, symlink ),
00602 d( new KArchiveFilePrivate(pos, size) )
00603 {
00604 }
00605
00606 KArchiveFile::~KArchiveFile()
00607 {
00608 delete d;
00609 }
00610
00611 qint64 KArchiveFile::position() const
00612 {
00613 return d->pos;
00614 }
00615
00616 qint64 KArchiveFile::size() const
00617 {
00618 return d->size;
00619 }
00620
00621 void KArchiveFile::setSize( qint64 s )
00622 {
00623 d->size = s;
00624 }
00625
00626 QByteArray KArchiveFile::data() const
00627 {
00628 archive()->device()->seek( d->pos );
00629
00630
00631 QByteArray arr;
00632 if ( d->size )
00633 {
00634 assert( arr.data() );
00635 arr = archive()->device()->read( d->size );
00636 Q_ASSERT( arr.size() == d->size );
00637 }
00638 return arr;
00639 }
00640
00641 QIODevice * KArchiveFile::createDevice() const
00642 {
00643 return new KLimitedIODevice( archive()->device(), d->pos, d->size );
00644 }
00645
00646 bool KArchiveFile::isFile() const
00647 {
00648 return true;
00649 }
00650
00651 void KArchiveFile::copyTo(const QString& dest) const
00652 {
00653 QFile f( dest + '/' + name() );
00654 if ( f.open( QIODevice::ReadWrite | QIODevice::Truncate ) )
00655 {
00656 f.write( data() );
00657 f.close();
00658 }
00659 }
00660
00664
00665 class KArchiveDirectoryPrivate
00666 {
00667 public:
00668 ~KArchiveDirectoryPrivate()
00669 {
00670 qDeleteAll(entries);
00671 }
00672 QHash<QString, KArchiveEntry *> entries;
00673 };
00674
00675 KArchiveDirectory::KArchiveDirectory( KArchive* t, const QString& name, int access,
00676 int date,
00677 const QString& user, const QString& group,
00678 const QString &symlink)
00679 : KArchiveEntry( t, name, access, date, user, group, symlink ),
00680 d( new KArchiveDirectoryPrivate )
00681 {
00682 }
00683
00684 KArchiveDirectory::~KArchiveDirectory()
00685 {
00686 delete d;
00687 }
00688
00689 QStringList KArchiveDirectory::entries() const
00690 {
00691 return d->entries.keys();
00692 }
00693
00694 const KArchiveEntry* KArchiveDirectory::entry( const QString& _name ) const
00695 {
00696 QString name = QDir::cleanPath(_name);
00697 int pos = name.indexOf( '/' );
00698 if ( pos == 0 )
00699 {
00700 if (name.length()>1)
00701 {
00702 name = name.mid( 1 );
00703 pos = name.indexOf( '/' );
00704 }
00705 else
00706 return this;
00707 }
00708
00709 if ( pos != -1 && pos == name.length()-1 )
00710 {
00711 name = name.left( pos );
00712 pos = name.indexOf( '/' );
00713 }
00714 if ( pos != -1 )
00715 {
00716 const QString left = name.left(pos);
00717 const QString right = name.mid(pos + 1);
00718
00719
00720
00721 const KArchiveEntry* e = d->entries.value( left );
00722 if ( !e || !e->isDirectory() )
00723 return 0;
00724 return static_cast<const KArchiveDirectory*>(e)->entry( right );
00725 }
00726
00727 return d->entries.value( name );
00728 }
00729
00730 void KArchiveDirectory::addEntry( KArchiveEntry* entry )
00731 {
00732 Q_ASSERT( !entry->name().isEmpty() );
00733 if( d->entries.value( entry->name() ) ) {
00734 kWarning() << "directory " << name()
00735 << "has entry" << entry->name() << "already";
00736 }
00737 d->entries.insert( entry->name(), entry );
00738 }
00739
00740 bool KArchiveDirectory::isDirectory() const
00741 {
00742 return true;
00743 }
00744
00745 static int sortByPosition( const KArchiveFile* file1, const KArchiveFile* file2 ) {
00746 return file1->position() - file2->position();
00747 }
00748
00749 void KArchiveDirectory::copyTo(const QString& dest, bool recursiveCopy ) const
00750 {
00751 QDir root;
00752
00753 QList<const KArchiveFile*> fileList;
00754 QMap<qint64, QString> fileToDir;
00755
00756
00757 QStack<const KArchiveDirectory *> dirStack;
00758 QStack<QString> dirNameStack;
00759
00760 dirStack.push( this );
00761 dirNameStack.push( dest );
00762 do {
00763 const KArchiveDirectory* curDir = dirStack.pop();
00764 const QString curDirName = dirNameStack.pop();
00765 root.mkdir(curDirName);
00766
00767 const QStringList dirEntries = curDir->entries();
00768 for ( QStringList::const_iterator it = dirEntries.begin(); it != dirEntries.end(); ++it ) {
00769 const KArchiveEntry* curEntry = curDir->entry(*it);
00770 if (!curEntry->symLinkTarget().isEmpty()) {
00771 const QString linkName = curDirName+'/'+curEntry->name();
00772 #ifdef Q_OS_UNIX
00773 if (!::symlink(curEntry->symLinkTarget().toLocal8Bit(), linkName.toLocal8Bit())) {
00774 kDebug() << "symlink(" << curEntry->symLinkTarget() << ',' << linkName << ") failed:" << strerror(errno);
00775 }
00776 #else
00777
00778 #endif
00779 } else {
00780 if ( curEntry->isFile() ) {
00781 const KArchiveFile* curFile = dynamic_cast<const KArchiveFile*>( curEntry );
00782 if (curFile) {
00783 fileList.append( curFile );
00784 fileToDir.insert( curFile->position(), curDirName );
00785 }
00786 }
00787
00788 if ( curEntry->isDirectory() && recursiveCopy ) {
00789 const KArchiveDirectory *ad = dynamic_cast<const KArchiveDirectory*>( curEntry );
00790 if (ad) {
00791 dirStack.push( ad );
00792 dirNameStack.push( curDirName + '/' + curEntry->name() );
00793 }
00794 }
00795 }
00796 }
00797 } while (!dirStack.isEmpty());
00798
00799 qSort( fileList.begin(), fileList.end(), sortByPosition );
00800
00801 for ( QList<const KArchiveFile*>::const_iterator it = fileList.constBegin(), end = fileList.constEnd() ;
00802 it != end ; ++it ) {
00803 const KArchiveFile* f = *it;
00804 qint64 pos = f->position();
00805 f->copyTo( fileToDir[pos] );
00806 }
00807 }
00808
00809 void KArchive::virtual_hook( int, void* )
00810 { ; }
00811
00812 void KArchiveEntry::virtual_hook( int, void* )
00813 { }
00814
00815 void KArchiveFile::virtual_hook( int id, void* data )
00816 { KArchiveEntry::virtual_hook( id, data ); }
00817
00818 void KArchiveDirectory::virtual_hook( int id, void* data )
00819 { KArchiveEntry::virtual_hook( id, data ); }