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 #include <kde_file.h>
00045
00046 #include "karchive.h"
00047 #include "klimitediodevice.h"
00048
00049 template class QDict<KArchiveEntry>;
00050
00051
00052 class KArchive::KArchivePrivate
00053 {
00054 public:
00055 KArchiveDirectory* rootDir;
00056 };
00057
00058 class PosSortedPtrList : public QPtrList<KArchiveFile> {
00059 protected:
00060 int compareItems( QPtrCollection::Item i1,
00061 QPtrCollection::Item i2 )
00062 {
00063 int pos1 = static_cast<KArchiveFile*>( i1 )->position();
00064 int pos2 = static_cast<KArchiveFile*>( i2 )->position();
00065 return ( pos1 - pos2 );
00066 }
00067 };
00068
00069
00073
00074 KArchive::KArchive( QIODevice * dev )
00075 {
00076 d = new KArchivePrivate;
00077 d->rootDir = 0;
00078 m_dev = dev;
00079 m_open = false;
00080 }
00081
00082 KArchive::~KArchive()
00083 {
00084 if ( m_open )
00085 close();
00086 delete d->rootDir;
00087 delete d;
00088 }
00089
00090 bool KArchive::open( int mode )
00091 {
00092 if ( m_dev && !m_dev->open( mode ) )
00093 return false;
00094
00095 if ( m_open )
00096 close();
00097
00098 m_mode = mode;
00099 m_open = true;
00100
00101 Q_ASSERT( d->rootDir == 0L );
00102 d->rootDir = 0L;
00103
00104 return openArchive( mode );
00105 }
00106
00107 void KArchive::close()
00108 {
00109 if ( !m_open )
00110 return;
00111
00112
00113 closeArchive();
00114
00115 if ( m_dev )
00116 m_dev->close();
00117
00118 delete d->rootDir;
00119 d->rootDir = 0;
00120 m_open = false;
00121 }
00122
00123 const KArchiveDirectory* KArchive::directory() const
00124 {
00125
00126 return const_cast<KArchive *>(this)->rootDir();
00127 }
00128
00129
00130 bool KArchive::addLocalFile( const QString& fileName, const QString& destName )
00131 {
00132 QFileInfo fileInfo( fileName );
00133 if ( !fileInfo.isFile() && !fileInfo.isSymLink() )
00134 {
00135 kdWarning() << "KArchive::addLocalFile " << fileName << " doesn't exist or is not a regular file." << endl;
00136 return false;
00137 }
00138
00139 KDE_struct_stat fi;
00140 if (KDE_lstat(QFile::encodeName(fileName),&fi) == -1) {
00141 kdWarning() << "KArchive::addLocalFile stating " << fileName
00142 << " failed: " << strerror(errno) << endl;
00143 return false;
00144 }
00145
00146 if (fileInfo.isSymLink()) {
00147 return writeSymLink(destName, fileInfo.readLink(), fileInfo.owner(),
00148 fileInfo.group(), fi.st_mode, fi.st_atime, fi.st_mtime,
00149 fi.st_ctime);
00150 }
00151
00152 uint size = fileInfo.size();
00153
00154
00155
00156
00157 QFile file( fileName );
00158 if ( !file.open( IO_ReadOnly ) )
00159 {
00160 kdWarning() << "KArchive::addLocalFile couldn't open file " << fileName << endl;
00161 return false;
00162 }
00163
00164 if ( !prepareWriting( destName, fileInfo.owner(), fileInfo.group(), size,
00165 fi.st_mode, fi.st_atime, fi.st_mtime, fi.st_ctime ) )
00166 {
00167 kdWarning() << "KArchive::addLocalFile prepareWriting " << destName << " failed" << endl;
00168 return false;
00169 }
00170
00171
00172 QByteArray array(8*1024);
00173 int n;
00174 uint total = 0;
00175 while ( ( n = file.readBlock( array.data(), array.size() ) ) > 0 )
00176 {
00177 if ( !writeData( array.data(), n ) )
00178 {
00179 kdWarning() << "KArchive::addLocalFile writeData failed" << endl;
00180 return false;
00181 }
00182 total += n;
00183 }
00184 Q_ASSERT( total == size );
00185
00186 if ( !doneWriting( size ) )
00187 {
00188 kdWarning() << "KArchive::addLocalFile doneWriting failed" << endl;
00189 return false;
00190 }
00191 return true;
00192 }
00193
00194 bool KArchive::addLocalDirectory( const QString& path, const QString& destName )
00195 {
00196 QString dot = ".";
00197 QString dotdot = "..";
00198 QDir dir( path );
00199 if ( !dir.exists() )
00200 return false;
00201 QStringList files = dir.entryList();
00202 for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
00203 {
00204 if ( *it != dot && *it != dotdot )
00205 {
00206 QString fileName = path + "/" + *it;
00207
00208 QString dest = destName.isEmpty() ? *it : (destName + "/" + *it);
00209 QFileInfo fileInfo( fileName );
00210
00211 if ( fileInfo.isFile() || fileInfo.isSymLink() )
00212 addLocalFile( fileName, dest );
00213 else if ( fileInfo.isDir() )
00214 addLocalDirectory( fileName, dest );
00215
00216 }
00217 }
00218 return true;
00219 }
00220
00221 bool KArchive::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00222 {
00223 mode_t perm = 0100644;
00224 time_t the_time = time(0);
00225 return writeFile(name,user,group,size,perm,the_time,the_time,the_time,data);
00226 }
00227
00228 bool KArchive::prepareWriting( const QString& name, const QString& user,
00229 const QString& group, uint size, mode_t perm,
00230 time_t atime, time_t mtime, time_t ctime ) {
00231 PrepareWritingParams params;
00232 params.name = &name;
00233 params.user = &user;
00234 params.group = &group;
00235 params.size = size;
00236 params.perm = perm;
00237 params.atime = atime;
00238 params.mtime = mtime;
00239 params.ctime = ctime;
00240 virtual_hook(VIRTUAL_PREPARE_WRITING,¶ms);
00241 return params.retval;
00242 }
00243
00244 bool KArchive::prepareWriting_impl(const QString &name, const QString &user,
00245 const QString &group, uint size, mode_t ,
00246 time_t , time_t , time_t ) {
00247 kdWarning(7040) << "New prepareWriting API not implemented in this class." << endl
00248 << "Falling back to old API (metadata information will be lost)" << endl;
00249 return prepareWriting(name,user,group,size);
00250 }
00251
00252 bool KArchive::writeFile( const QString& name, const QString& user,
00253 const QString& group, uint size, mode_t perm,
00254 time_t atime, time_t mtime, time_t ctime,
00255 const char* data ) {
00256 WriteFileParams params;
00257 params.name = &name;
00258 params.user = &user;
00259 params.group = &group;
00260 params.size = size;
00261 params.perm = perm;
00262 params.atime = atime;
00263 params.mtime = mtime;
00264 params.ctime = ctime;
00265 params.data = data;
00266 virtual_hook(VIRTUAL_WRITE_FILE,¶ms);
00267 return params.retval;
00268 }
00269
00270 bool KArchive::writeFile_impl( const QString& name, const QString& user,
00271 const QString& group, uint size, mode_t perm,
00272 time_t atime, time_t mtime, time_t ctime,
00273 const char* data ) {
00274
00275 if ( !prepareWriting( name, user, group, size, perm, atime, mtime, ctime ) )
00276 {
00277 kdWarning() << "KArchive::writeFile prepareWriting failed" << endl;
00278 return false;
00279 }
00280
00281
00282
00283 if ( data && size && !writeData( data, size ) )
00284 {
00285 kdWarning() << "KArchive::writeFile writeData failed" << endl;
00286 return false;
00287 }
00288
00289 if ( !doneWriting( size ) )
00290 {
00291 kdWarning() << "KArchive::writeFile doneWriting failed" << endl;
00292 return false;
00293 }
00294 return true;
00295 }
00296
00297 bool KArchive::writeDir(const QString& name, const QString& user,
00298 const QString& group, mode_t perm,
00299 time_t atime, time_t mtime, time_t ctime) {
00300 WriteDirParams params;
00301 params.name = &name;
00302 params.user = &user;
00303 params.group = &group;
00304 params.perm = perm;
00305 params.atime = atime;
00306 params.mtime = mtime;
00307 params.ctime = ctime;
00308 virtual_hook(VIRTUAL_WRITE_DIR,¶ms);
00309 return params.retval;
00310 }
00311
00312 bool KArchive::writeDir_impl(const QString &name, const QString &user,
00313 const QString &group, mode_t ,
00314 time_t , time_t , time_t ) {
00315 kdWarning(7040) << "New writeDir API not implemented in this class." << endl
00316 << "Falling back to old API (metadata information will be lost)" << endl;
00317 return writeDir(name,user,group);
00318 }
00319
00320 bool KArchive::writeSymLink(const QString &name, const QString &target,
00321 const QString &user, const QString &group,
00322 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
00323 WriteSymlinkParams params;
00324 params.name = &name;
00325 params.target = ⌖
00326 params.user = &user;
00327 params.group = &group;
00328 params.perm = perm;
00329 params.atime = atime;
00330 params.mtime = mtime;
00331 params.ctime = ctime;
00332 virtual_hook(VIRTUAL_WRITE_SYMLINK,¶ms);
00333 return params.retval;
00334 }
00335
00336 bool KArchive::writeSymLink_impl(const QString &,const QString &,
00337 const QString &, const QString &,
00338 mode_t , time_t , time_t ,
00339 time_t ) {
00340 kdWarning(7040) << "writeSymLink not implemented in this class." << endl
00341 << "No fallback available." << endl;
00342
00343 return false;
00344 }
00345
00346 bool KArchive::writeData( const char* data, uint size )
00347 {
00348 WriteDataParams params;
00349 params.data = data;
00350 params.size = size;
00351 virtual_hook( VIRTUAL_WRITE_DATA, ¶ms );
00352 return params.retval;
00353 }
00354
00355 bool KArchive::writeData_impl( const char* data, uint size )
00356 {
00357 Q_ASSERT( device() );
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 ); }