00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <time.h>
00025 #include <unistd.h>
00026 #include <errno.h>
00027 #include <grp.h>
00028 #include <pwd.h>
00029 #include <assert.h>
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032
00033 #include <qptrlist.h>
00034 #include <qptrstack.h>
00035 #include <qvaluestack.h>
00036 #include <qmap.h>
00037 #include <qcstring.h>
00038 #include <qdir.h>
00039 #include <qfile.h>
00040
00041 #include <kdebug.h>
00042 #include <kfilterdev.h>
00043 #include <kfilterbase.h>
00044
00045 #include "karchive.h"
00046 #include "klimitediodevice.h"
00047
00048 template class QDict<KArchiveEntry>;
00049
00050
00051 class KArchive::KArchivePrivate
00052 {
00053 public:
00054 KArchiveDirectory* rootDir;
00055 };
00056
00057 class PosSortedPtrList : public QPtrList<KArchiveFile> {
00058 protected:
00059 int compareItems( QPtrCollection::Item i1,
00060 QPtrCollection::Item i2 )
00061 {
00062 int pos1 = static_cast<KArchiveFile*>( i1 )->position();
00063 int pos2 = static_cast<KArchiveFile*>( i2 )->position();
00064 return ( pos1 - pos2 );
00065 }
00066 };
00067
00068
00072
00073 KArchive::KArchive( QIODevice * dev )
00074 {
00075 d = new KArchivePrivate;
00076 d->rootDir = 0;
00077 m_dev = dev;
00078 m_open = false;
00079 }
00080
00081 KArchive::~KArchive()
00082 {
00083 if ( m_open )
00084 close();
00085 delete d->rootDir;
00086 delete d;
00087 }
00088
00089 bool KArchive::open( int mode )
00090 {
00091 if(0 == m_dev)
00092 return false;
00093
00094 if ( !m_dev->open( mode ) )
00095 return false;
00096
00097 if ( m_open )
00098 close();
00099
00100 m_mode = mode;
00101 m_open = true;
00102
00103 Q_ASSERT( d->rootDir == 0L );
00104 d->rootDir = 0L;
00105
00106 return openArchive( mode );
00107 }
00108
00109 void KArchive::close()
00110 {
00111 if ( !m_open )
00112 return;
00113
00114
00115 closeArchive();
00116
00117 m_dev->close();
00118
00119 delete d->rootDir;
00120 d->rootDir = 0;
00121 m_open = false;
00122 }
00123
00124 const KArchiveDirectory* KArchive::directory() const
00125 {
00126
00127 return const_cast<KArchive *>(this)->rootDir();
00128 }
00129
00130
00131 bool KArchive::addLocalFile( const QString& fileName, const QString& destName )
00132 {
00133 QFileInfo fileInfo( fileName );
00134 if ( !fileInfo.isFile() && !fileInfo.isSymLink() )
00135 {
00136 kdWarning() << "KArchive::addLocalFile " << fileName << " doesn't exist or is not a regular file." << endl;
00137 return false;
00138 }
00139
00140 struct stat fi;
00141 if (lstat(QFile::encodeName(fileName),&fi) == -1) {
00142 kdWarning() << "KArchive::addLocalFile stating " << fileName
00143 << " failed: " << strerror(errno) << endl;
00144 return false;
00145 }
00146
00147 if (fileInfo.isSymLink()) {
00148 return writeSymLink(destName, fileInfo.readLink(), fileInfo.owner(),
00149 fileInfo.group(), fi.st_mode, fi.st_atime, fi.st_mtime,
00150 fi.st_ctime);
00151 }
00152
00153 uint size = fileInfo.size();
00154
00155
00156
00157
00158 QFile file( fileName );
00159 if ( !file.open( IO_ReadOnly ) )
00160 {
00161 kdWarning() << "KArchive::addLocalFile couldn't open file " << fileName << endl;
00162 return false;
00163 }
00164
00165 if ( !prepareWriting( destName, fileInfo.owner(), fileInfo.group(), size,
00166 fi.st_mode, fi.st_atime, fi.st_mtime, fi.st_ctime ) )
00167 {
00168 kdWarning() << "KArchive::addLocalFile prepareWriting " << destName << " failed" << endl;
00169 return false;
00170 }
00171
00172
00173 QByteArray array(8*1024);
00174 int n;
00175 uint total = 0;
00176 while ( ( n = file.readBlock( array.data(), array.size() ) ) > 0 )
00177 {
00178 if ( !writeData( array.data(), n ) )
00179 {
00180 kdWarning() << "KArchive::addLocalFile writeData failed" << endl;
00181 return false;
00182 }
00183 total += n;
00184 }
00185 Q_ASSERT( total == size );
00186
00187 if ( !doneWriting( size ) )
00188 {
00189 kdWarning() << "KArchive::addLocalFile doneWriting failed" << endl;
00190 return false;
00191 }
00192 return true;
00193 }
00194
00195 bool KArchive::addLocalDirectory( const QString& path, const QString& destName )
00196 {
00197 QString dot = ".";
00198 QString dotdot = "..";
00199 QDir dir( path );
00200 if ( !dir.exists() )
00201 return false;
00202 QStringList files = dir.entryList();
00203 for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
00204 {
00205 if ( *it != dot && *it != dotdot )
00206 {
00207 QString fileName = path + "/" + *it;
00208
00209 QString dest = destName.isEmpty() ? *it : (destName + "/" + *it);
00210 QFileInfo fileInfo( fileName );
00211
00212 if ( fileInfo.isFile() || fileInfo.isSymLink() )
00213 addLocalFile( fileName, dest );
00214 else if ( fileInfo.isDir() )
00215 addLocalDirectory( fileName, dest );
00216
00217 }
00218 }
00219 return true;
00220 }
00221
00222 bool KArchive::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00223 {
00224 mode_t perm = 0100644;
00225 time_t the_time = time(0);
00226 return writeFile(name,user,group,size,perm,the_time,the_time,the_time,data);
00227 }
00228
00229 bool KArchive::prepareWriting( const QString& name, const QString& user,
00230 const QString& group, uint size, mode_t perm,
00231 time_t atime, time_t mtime, time_t ctime ) {
00232 PrepareWritingParams params;
00233 params.name = &name;
00234 params.user = &user;
00235 params.group = &group;
00236 params.size = size;
00237 params.perm = perm;
00238 params.atime = atime;
00239 params.mtime = mtime;
00240 params.ctime = ctime;
00241 virtual_hook(VIRTUAL_PREPARE_WRITING,¶ms);
00242 return params.retval;
00243 }
00244
00245 bool KArchive::prepareWriting_impl(const QString &name, const QString &user,
00246 const QString &group, uint size, mode_t ,
00247 time_t , time_t , time_t ) {
00248 kdWarning(7040) << "New prepareWriting API not implemented in this class." << endl
00249 << "Falling back to old API (metadata information will be lost)" << endl;
00250 return prepareWriting(name,user,group,size);
00251 }
00252
00253 bool KArchive::writeFile( const QString& name, const QString& user,
00254 const QString& group, uint size, mode_t perm,
00255 time_t atime, time_t mtime, time_t ctime,
00256 const char* data ) {
00257 WriteFileParams params;
00258 params.name = &name;
00259 params.user = &user;
00260 params.group = &group;
00261 params.size = size;
00262 params.perm = perm;
00263 params.atime = atime;
00264 params.mtime = mtime;
00265 params.ctime = ctime;
00266 params.data = data;
00267 virtual_hook(VIRTUAL_WRITE_FILE,¶ms);
00268 return params.retval;
00269 }
00270
00271 bool KArchive::writeFile_impl( const QString& name, const QString& user,
00272 const QString& group, uint size, mode_t perm,
00273 time_t atime, time_t mtime, time_t ctime,
00274 const char* data ) {
00275
00276 if ( !prepareWriting( name, user, group, size, perm, atime, mtime, ctime ) )
00277 {
00278 kdWarning() << "KArchive::writeFile prepareWriting failed" << endl;
00279 return false;
00280 }
00281
00282
00283
00284 if ( data && size && !writeData( data, size ) )
00285 {
00286 kdWarning() << "KArchive::writeFile writeData failed" << endl;
00287 return false;
00288 }
00289
00290 if ( !doneWriting( size ) )
00291 {
00292 kdWarning() << "KArchive::writeFile doneWriting failed" << endl;
00293 return false;
00294 }
00295 return true;
00296 }
00297
00298 bool KArchive::writeDir(const QString& name, const QString& user,
00299 const QString& group, mode_t perm,
00300 time_t atime, time_t mtime, time_t ctime) {
00301 WriteDirParams params;
00302 params.name = &name;
00303 params.user = &user;
00304 params.group = &group;
00305 params.perm = perm;
00306 params.atime = atime;
00307 params.mtime = mtime;
00308 params.ctime = ctime;
00309 virtual_hook(VIRTUAL_WRITE_DIR,¶ms);
00310 return params.retval;
00311 }
00312
00313 bool KArchive::writeDir_impl(const QString &name, const QString &user,
00314 const QString &group, mode_t ,
00315 time_t , time_t , time_t ) {
00316 kdWarning(7040) << "New writeDir API not implemented in this class." << endl
00317 << "Falling back to old API (metadata information will be lost)" << endl;
00318 return writeDir(name,user,group);
00319 }
00320
00321 bool KArchive::writeSymLink(const QString &name, const QString &target,
00322 const QString &user, const QString &group,
00323 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
00324 WriteSymlinkParams params;
00325 params.name = &name;
00326 params.target = ⌖
00327 params.user = &user;
00328 params.group = &group;
00329 params.perm = perm;
00330 params.atime = atime;
00331 params.mtime = mtime;
00332 params.ctime = ctime;
00333 virtual_hook(VIRTUAL_WRITE_SYMLINK,¶ms);
00334 return params.retval;
00335 }
00336
00337 bool KArchive::writeSymLink_impl(const QString &,const QString &,
00338 const QString &, const QString &,
00339 mode_t , time_t , time_t ,
00340 time_t ) {
00341 kdWarning(7040) << "writeSymLink not implemented in this class." << endl
00342 << "No fallback available." << endl;
00343
00344 return false;
00345 }
00346
00347 bool KArchive::writeData( const char* data, uint size )
00348 {
00349 WriteDataParams params;
00350 params.data = data;
00351 params.size = size;
00352 virtual_hook( VIRTUAL_WRITE_DATA, ¶ms );
00353 return params.retval;
00354 }
00355
00356 bool KArchive::writeData_impl( const char* data, uint size )
00357 {
00358 return device()->writeBlock( data, size ) == (Q_LONG)size;
00359 }
00360
00361 KArchiveDirectory * KArchive::rootDir()
00362 {
00363 if ( !d->rootDir )
00364 {
00365
00366 struct passwd* pw = getpwuid( getuid() );
00367 struct group* grp = getgrgid( getgid() );
00368 QString username = pw ? QFile::decodeName(pw->pw_name) : QString::number( getuid() );
00369 QString groupname = grp ? QFile::decodeName(grp->gr_name) : QString::number( getgid() );
00370
00371 d->rootDir = new KArchiveDirectory( this, QString::fromLatin1("/"), (int)(0777 + S_IFDIR), 0, username, groupname, QString::null );
00372 }
00373 return d->rootDir;
00374 }
00375
00376 KArchiveDirectory * KArchive::findOrCreate( const QString & path )
00377 {
00378
00379 if ( path.isEmpty() || path == "/" || path == "." )
00380 {
00381
00382 return rootDir();
00383 }
00384
00385
00386
00387
00388
00389
00390
00391 KArchiveEntry* ent = rootDir()->entry( path );
00392 if ( ent && ent->isDirectory() )
00393 {
00394
00395 return (KArchiveDirectory *) ent;
00396 }
00397
00398
00399 int pos = path.findRev( '/' );
00400 KArchiveDirectory * parent;
00401 QString dirname;
00402 if ( pos == -1 )
00403 {
00404 parent = rootDir();
00405 dirname = path;
00406 }
00407 else
00408 {
00409 QString left = path.left( pos );
00410 dirname = path.mid( pos + 1 );
00411 parent = findOrCreate( left );
00412 }
00413
00414
00415
00416 KArchiveDirectory * e = new KArchiveDirectory( this, dirname, d->rootDir->permissions(),
00417 d->rootDir->date(), d->rootDir->user(),
00418 d->rootDir->group(), QString::null );
00419 parent->addEntry( e );
00420 return e;
00421 }
00422
00423 void KArchive::setDevice( QIODevice * dev )
00424 {
00425 m_dev = dev;
00426 }
00427
00428 void KArchive::setRootDir( KArchiveDirectory *rootDir )
00429 {
00430 Q_ASSERT( !d->rootDir );
00431 d->rootDir = rootDir;
00432 }
00433
00437 KArchiveEntry::KArchiveEntry( KArchive* t, const QString& name, int access, int date,
00438 const QString& user, const QString& group, const
00439 QString& symlink)
00440 {
00441 m_name = name;
00442 m_access = access;
00443 m_date = date;
00444 m_user = user;
00445 m_group = group;
00446 m_symlink = symlink;
00447 m_archive = t;
00448
00449 }
00450
00451 QDateTime KArchiveEntry::datetime() const
00452 {
00453 QDateTime d;
00454 d.setTime_t( m_date );
00455 return d;
00456 }
00457
00461
00462 KArchiveFile::KArchiveFile( KArchive* t, const QString& name, int access, int date,
00463 const QString& user, const QString& group,
00464 const QString & symlink,
00465 int pos, int size )
00466 : KArchiveEntry( t, name, access, date, user, group, symlink )
00467 {
00468 m_pos = pos;
00469 m_size = size;
00470 }
00471
00472 int KArchiveFile::position() const
00473 {
00474 return m_pos;
00475 }
00476
00477 int KArchiveFile::size() const
00478 {
00479 return m_size;
00480 }
00481
00482 QByteArray KArchiveFile::data() const
00483 {
00484 archive()->device()->at( m_pos );
00485
00486
00487 QByteArray arr( m_size );
00488 if ( m_size )
00489 {
00490 assert( arr.data() );
00491 int n = archive()->device()->readBlock( arr.data(), m_size );
00492 if ( n != m_size )
00493 arr.resize( n );
00494 }
00495 return arr;
00496 }
00497
00498
00499 QIODevice *KArchiveFile::device() const
00500 {
00501 return new KLimitedIODevice( archive()->device(), m_pos, m_size );
00502 }
00503
00504 void KArchiveFile::copyTo(const QString& dest) const
00505 {
00506 QFile f( dest + "/" + name() );
00507 f.open( IO_ReadWrite | IO_Truncate );
00508 f.writeBlock( data() );
00509 f.close();
00510 }
00511
00515
00516
00517 KArchiveDirectory::KArchiveDirectory( KArchive* t, const QString& name, int access,
00518 int date,
00519 const QString& user, const QString& group,
00520 const QString &symlink)
00521 : KArchiveEntry( t, name, access, date, user, group, symlink )
00522 {
00523 m_entries.setAutoDelete( true );
00524 }
00525
00526 QStringList KArchiveDirectory::entries() const
00527 {
00528 QStringList l;
00529
00530 QDictIterator<KArchiveEntry> it( m_entries );
00531 for( ; it.current(); ++it )
00532 l.append( it.currentKey() );
00533
00534 return l;
00535 }
00536
00537 KArchiveEntry* KArchiveDirectory::entry( QString name )
00538
00539
00540 {
00541 int pos = name.find( '/' );
00542 if ( pos == 0 )
00543 {
00544 if (name.length()>1)
00545 {
00546 name = name.mid( 1 );
00547 pos = name.find( '/' );
00548 }
00549 else
00550 return this;
00551 }
00552
00553 if ( pos != -1 && pos == (int)name.length()-1 )
00554 {
00555 name = name.left( pos );
00556 pos = name.find( '/' );
00557 }
00558 if ( pos != -1 )
00559 {
00560 QString left = name.left( pos );
00561 QString right = name.mid( pos + 1 );
00562
00563
00564
00565 KArchiveEntry* e = m_entries[ left ];
00566 if ( !e || !e->isDirectory() )
00567 return 0;
00568 return ((KArchiveDirectory*)e)->entry( right );
00569 }
00570
00571 return m_entries[ name ];
00572 }
00573
00574 const KArchiveEntry* KArchiveDirectory::entry( QString name ) const
00575 {
00576 return ((KArchiveDirectory*)this)->entry( name );
00577 }
00578
00579 void KArchiveDirectory::addEntry( KArchiveEntry* entry )
00580 {
00581 Q_ASSERT( !entry->name().isEmpty() );
00582 m_entries.insert( entry->name(), entry );
00583 }
00584
00585 void KArchiveDirectory::copyTo(const QString& dest, bool recursiveCopy ) const
00586 {
00587 QDir root;
00588
00589 PosSortedPtrList fileList;
00590 QMap<int, QString> fileToDir;
00591
00592 QStringList::Iterator it;
00593
00594
00595 KArchiveDirectory* curDir;
00596 QString curDirName;
00597
00598 QStringList dirEntries;
00599 KArchiveEntry* curEntry;
00600 KArchiveFile* curFile;
00601
00602
00603 QPtrStack<KArchiveDirectory> dirStack;
00604 QValueStack<QString> dirNameStack;
00605
00606 dirStack.push( this );
00607 dirNameStack.push( dest );
00608 do {
00609 curDir = dirStack.pop();
00610 curDirName = dirNameStack.pop();
00611 root.mkdir(curDirName);
00612
00613 dirEntries = curDir->entries();
00614 for ( it = dirEntries.begin(); it != dirEntries.end(); ++it ) {
00615 curEntry = curDir->entry(*it);
00616 if ( curEntry->isFile() ) {
00617 curFile = dynamic_cast<KArchiveFile*>( curEntry );
00618 if (curFile) {
00619 fileList.append( curFile );
00620 fileToDir.insert( curFile->position(), curDirName );
00621 }
00622 }
00623
00624 if ( curEntry->isDirectory() )
00625 if ( recursiveCopy ) {
00626 KArchiveDirectory *ad = dynamic_cast<KArchiveDirectory*>( curEntry );
00627 if (ad) {
00628 dirStack.push( ad );
00629 dirNameStack.push( curDirName + "/" + curEntry->name() );
00630 }
00631 }
00632 }
00633 } while (!dirStack.isEmpty());
00634
00635 fileList.sort();
00636
00637 KArchiveFile* f;
00638 for ( f = fileList.first(); f; f = fileList.next() ) {
00639 int pos = f->position();
00640 f->copyTo( fileToDir[pos] );
00641 }
00642 }
00643
00644 void KArchive::virtual_hook( int id, void* data )
00645 {
00646 switch (id) {
00647 case VIRTUAL_WRITE_DATA: {
00648 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
00649 params->retval = writeData_impl( params->data, params->size );
00650 break;
00651 }
00652 case VIRTUAL_WRITE_SYMLINK: {
00653 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
00654 params->retval = writeSymLink_impl(*params->name,*params->target,
00655 *params->user,*params->group,params->perm,
00656 params->atime,params->mtime,params->ctime);
00657 break;
00658 }
00659 case VIRTUAL_WRITE_DIR: {
00660 WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data);
00661 params->retval = writeDir_impl(*params->name,*params->user,
00662 *params->group,params->perm,
00663 params->atime,params->mtime,params->ctime);
00664 break;
00665 }
00666 case VIRTUAL_WRITE_FILE: {
00667 WriteFileParams *params = reinterpret_cast<WriteFileParams *>(data);
00668 params->retval = writeFile_impl(*params->name,*params->user,
00669 *params->group,params->size,params->perm,
00670 params->atime,params->mtime,params->ctime,
00671 params->data);
00672 break;
00673 }
00674 case VIRTUAL_PREPARE_WRITING: {
00675 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
00676 params->retval = prepareWriting_impl(*params->name,*params->user,
00677 *params->group,params->size,params->perm,
00678 params->atime,params->mtime,params->ctime);
00679 break;
00680 }
00681 default:
00682 ;
00683 }
00684 }
00685
00686 void KArchiveEntry::virtual_hook( int, void* )
00687 { }
00688
00689 void KArchiveFile::virtual_hook( int id, void* data )
00690 { KArchiveEntry::virtual_hook( id, data ); }
00691
00692 void KArchiveDirectory::virtual_hook( int id, void* data )
00693 { KArchiveEntry::virtual_hook( id, data ); }