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