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 QStringList files = dir.entryList();
00208 for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
00209 {
00210 if ( *it != dot && *it != dotdot )
00211 {
00212 QString fileName = path + "/" + *it;
00213
00214 QString dest = destName.isEmpty() ? *it : (destName + "/" + *it);
00215 QFileInfo fileInfo( fileName );
00216
00217 if ( fileInfo.isFile() || fileInfo.isSymLink() )
00218 addLocalFile( fileName, dest );
00219 else if ( fileInfo.isDir() )
00220 addLocalDirectory( fileName, dest );
00221
00222 }
00223 }
00224 return true;
00225 }
00226
00227 bool KArchive::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00228 {
00229 mode_t perm = 0100644;
00230 time_t the_time = time(0);
00231 return writeFile(name,user,group,size,perm,the_time,the_time,the_time,data);
00232 }
00233
00234 bool KArchive::prepareWriting( const QString& name, const QString& user,
00235 const QString& group, uint size, mode_t perm,
00236 time_t atime, time_t mtime, time_t ctime ) {
00237 PrepareWritingParams params;
00238 params.name = &name;
00239 params.user = &user;
00240 params.group = &group;
00241 params.size = size;
00242 params.perm = perm;
00243 params.atime = atime;
00244 params.mtime = mtime;
00245 params.ctime = ctime;
00246 virtual_hook(VIRTUAL_PREPARE_WRITING,¶ms);
00247 return params.retval;
00248 }
00249
00250 bool KArchive::prepareWriting_impl(const QString &name, const QString &user,
00251 const QString &group, uint size, mode_t ,
00252 time_t , time_t , time_t ) {
00253 kdWarning(7040) << "New prepareWriting API not implemented in this class." << endl
00254 << "Falling back to old API (metadata information will be lost)" << endl;
00255 return prepareWriting(name,user,group,size);
00256 }
00257
00258 bool KArchive::writeFile( const QString& name, const QString& user,
00259 const QString& group, uint size, mode_t perm,
00260 time_t atime, time_t mtime, time_t ctime,
00261 const char* data ) {
00262 WriteFileParams params;
00263 params.name = &name;
00264 params.user = &user;
00265 params.group = &group;
00266 params.size = size;
00267 params.perm = perm;
00268 params.atime = atime;
00269 params.mtime = mtime;
00270 params.ctime = ctime;
00271 params.data = data;
00272 virtual_hook(VIRTUAL_WRITE_FILE,¶ms);
00273 return params.retval;
00274 }
00275
00276 bool KArchive::writeFile_impl( const QString& name, const QString& user,
00277 const QString& group, uint size, mode_t perm,
00278 time_t atime, time_t mtime, time_t ctime,
00279 const char* data ) {
00280
00281 if ( !prepareWriting( name, user, group, size, perm, atime, mtime, ctime ) )
00282 {
00283 kdWarning() << "KArchive::writeFile prepareWriting failed" << endl;
00284 return false;
00285 }
00286
00287
00288
00289 if ( data && size && !writeData( data, size ) )
00290 {
00291 kdWarning() << "KArchive::writeFile writeData failed" << endl;
00292 return false;
00293 }
00294
00295 if ( !doneWriting( size ) )
00296 {
00297 kdWarning() << "KArchive::writeFile doneWriting failed" << endl;
00298 return false;
00299 }
00300 return true;
00301 }
00302
00303 bool KArchive::writeDir(const QString& name, const QString& user,
00304 const QString& group, mode_t perm,
00305 time_t atime, time_t mtime, time_t ctime) {
00306 WriteDirParams params;
00307 params.name = &name;
00308 params.user = &user;
00309 params.group = &group;
00310 params.perm = perm;
00311 params.atime = atime;
00312 params.mtime = mtime;
00313 params.ctime = ctime;
00314 virtual_hook(VIRTUAL_WRITE_DIR,¶ms);
00315 return params.retval;
00316 }
00317
00318 bool KArchive::writeDir_impl(const QString &name, const QString &user,
00319 const QString &group, mode_t ,
00320 time_t , time_t , time_t ) {
00321 kdWarning(7040) << "New writeDir API not implemented in this class." << endl
00322 << "Falling back to old API (metadata information will be lost)" << endl;
00323 return writeDir(name,user,group);
00324 }
00325
00326 bool KArchive::writeSymLink(const QString &name, const QString &target,
00327 const QString &user, const QString &group,
00328 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
00329 WriteSymlinkParams params;
00330 params.name = &name;
00331 params.target = ⌖
00332 params.user = &user;
00333 params.group = &group;
00334 params.perm = perm;
00335 params.atime = atime;
00336 params.mtime = mtime;
00337 params.ctime = ctime;
00338 virtual_hook(VIRTUAL_WRITE_SYMLINK,¶ms);
00339 return params.retval;
00340 }
00341
00342 bool KArchive::writeSymLink_impl(const QString &,const QString &,
00343 const QString &, const QString &,
00344 mode_t , time_t , time_t ,
00345 time_t ) {
00346 kdWarning(7040) << "writeSymLink not implemented in this class." << endl
00347 << "No fallback available." << endl;
00348
00349 return false;
00350 }
00351
00352 bool KArchive::writeData( const char* data, uint size )
00353 {
00354 WriteDataParams params;
00355 params.data = data;
00356 params.size = size;
00357 virtual_hook( VIRTUAL_WRITE_DATA, ¶ms );
00358 return params.retval;
00359 }
00360
00361 bool KArchive::writeData_impl( const char* data, uint size )
00362 {
00363 Q_ASSERT( device() );
00364 return device()->writeBlock( data, size ) == (Q_LONG)size;
00365 }
00366
00367 KArchiveDirectory * KArchive::rootDir()
00368 {
00369 if ( !d->rootDir )
00370 {
00371
00372 struct passwd* pw = getpwuid( getuid() );
00373 struct group* grp = getgrgid( getgid() );
00374 QString username = pw ? QFile::decodeName(pw->pw_name) : QString::number( getuid() );
00375 QString groupname = grp ? QFile::decodeName(grp->gr_name) : QString::number( getgid() );
00376
00377 d->rootDir = new KArchiveDirectory( this, QString::fromLatin1("/"), (int)(0777 + S_IFDIR), 0, username, groupname, QString::null );
00378 }
00379 return d->rootDir;
00380 }
00381
00382 KArchiveDirectory * KArchive::findOrCreate( const QString & path )
00383 {
00384
00385 if ( path.isEmpty() || path == "/" || path == "." )
00386 {
00387
00388 return rootDir();
00389 }
00390
00391
00392
00393
00394
00395
00396
00397 KArchiveEntry* ent = rootDir()->entry( path );
00398 if ( ent )
00399 {
00400 if ( ent->isDirectory() )
00401
00402 return (KArchiveDirectory *) ent;
00403 else
00404 kdWarning() << "Found " << path << " but it's not a directory" << endl;
00405 }
00406
00407
00408 int pos = path.findRev( '/' );
00409 KArchiveDirectory * parent;
00410 QString dirname;
00411 if ( pos == -1 )
00412 {
00413 parent = rootDir();
00414 dirname = path;
00415 }
00416 else
00417 {
00418 QString left = path.left( pos );
00419 dirname = path.mid( pos + 1 );
00420 parent = findOrCreate( left );
00421 }
00422
00423
00424
00425 KArchiveDirectory * e = new KArchiveDirectory( this, dirname, d->rootDir->permissions(),
00426 d->rootDir->date(), d->rootDir->user(),
00427 d->rootDir->group(), QString::null );
00428 parent->addEntry( e );
00429 return e;
00430 }
00431
00432 void KArchive::setDevice( QIODevice * dev )
00433 {
00434 m_dev = dev;
00435 }
00436
00437 void KArchive::setRootDir( KArchiveDirectory *rootDir )
00438 {
00439 Q_ASSERT( !d->rootDir );
00440 d->rootDir = rootDir;
00441 }
00442
00446 KArchiveEntry::KArchiveEntry( KArchive* t, const QString& name, int access, int date,
00447 const QString& user, const QString& group, const
00448 QString& symlink)
00449 {
00450 m_name = name;
00451 m_access = access;
00452 m_date = date;
00453 m_user = user;
00454 m_group = group;
00455 m_symlink = symlink;
00456 m_archive = t;
00457
00458 }
00459
00460 QDateTime KArchiveEntry::datetime() const
00461 {
00462 QDateTime d;
00463 d.setTime_t( m_date );
00464 return d;
00465 }
00466
00470
00471 KArchiveFile::KArchiveFile( KArchive* t, const QString& name, int access, int date,
00472 const QString& user, const QString& group,
00473 const QString & symlink,
00474 int pos, int size )
00475 : KArchiveEntry( t, name, access, date, user, group, symlink )
00476 {
00477 m_pos = pos;
00478 m_size = size;
00479 }
00480
00481 int KArchiveFile::position() const
00482 {
00483 return m_pos;
00484 }
00485
00486 int KArchiveFile::size() const
00487 {
00488 return m_size;
00489 }
00490
00491 QByteArray KArchiveFile::data() const
00492 {
00493 archive()->device()->at( m_pos );
00494
00495
00496 QByteArray arr( m_size );
00497 if ( m_size )
00498 {
00499 assert( arr.data() );
00500 int n = archive()->device()->readBlock( arr.data(), m_size );
00501 if ( n != m_size )
00502 arr.resize( n );
00503 }
00504 return arr;
00505 }
00506
00507
00508 QIODevice *KArchiveFile::device() const
00509 {
00510 return new KLimitedIODevice( archive()->device(), m_pos, m_size );
00511 }
00512
00513 void KArchiveFile::copyTo(const QString& dest) const
00514 {
00515 QFile f( dest + "/" + name() );
00516 f.open( IO_ReadWrite | IO_Truncate );
00517 f.writeBlock( data() );
00518 f.close();
00519 }
00520
00524
00525
00526 KArchiveDirectory::KArchiveDirectory( KArchive* t, const QString& name, int access,
00527 int date,
00528 const QString& user, const QString& group,
00529 const QString &symlink)
00530 : KArchiveEntry( t, name, access, date, user, group, symlink )
00531 {
00532 m_entries.setAutoDelete( true );
00533 }
00534
00535 QStringList KArchiveDirectory::entries() const
00536 {
00537 QStringList l;
00538
00539 QDictIterator<KArchiveEntry> it( m_entries );
00540 for( ; it.current(); ++it )
00541 l.append( it.currentKey() );
00542
00543 return l;
00544 }
00545
00546 KArchiveEntry* KArchiveDirectory::entry( QString name )
00547
00548
00549 {
00550 int pos = name.find( '/' );
00551 if ( pos == 0 )
00552 {
00553 if (name.length()>1)
00554 {
00555 name = name.mid( 1 );
00556 pos = name.find( '/' );
00557 }
00558 else
00559 return this;
00560 }
00561
00562 if ( pos != -1 && pos == (int)name.length()-1 )
00563 {
00564 name = name.left( pos );
00565 pos = name.find( '/' );
00566 }
00567 if ( pos != -1 )
00568 {
00569 QString left = name.left( pos );
00570 QString right = name.mid( pos + 1 );
00571
00572
00573
00574 KArchiveEntry* e = m_entries[ left ];
00575 if ( !e || !e->isDirectory() )
00576 return 0;
00577 return ((KArchiveDirectory*)e)->entry( right );
00578 }
00579
00580 return m_entries[ name ];
00581 }
00582
00583 const KArchiveEntry* KArchiveDirectory::entry( QString name ) const
00584 {
00585 return ((KArchiveDirectory*)this)->entry( name );
00586 }
00587
00588 void KArchiveDirectory::addEntry( KArchiveEntry* entry )
00589 {
00590 Q_ASSERT( !entry->name().isEmpty() );
00591 if( m_entries[ entry->name() ] ) {
00592 kdWarning() << "KArchiveDirectory::addEntry: directory " << name()
00593 << " has entry " << entry->name() << " already" << endl;
00594 }
00595 m_entries.insert( entry->name(), entry );
00596 }
00597
00598 void KArchiveDirectory::copyTo(const QString& dest, bool recursiveCopy ) const
00599 {
00600 QDir root;
00601
00602 PosSortedPtrList fileList;
00603 QMap<int, QString> fileToDir;
00604
00605 QStringList::Iterator it;
00606
00607
00608 KArchiveDirectory* curDir;
00609 QString curDirName;
00610
00611 QStringList dirEntries;
00612 KArchiveEntry* curEntry;
00613 KArchiveFile* curFile;
00614
00615
00616 QPtrStack<KArchiveDirectory> dirStack;
00617 QValueStack<QString> dirNameStack;
00618
00619 dirStack.push( this );
00620 dirNameStack.push( dest );
00621 do {
00622 curDir = dirStack.pop();
00623 curDirName = dirNameStack.pop();
00624 root.mkdir(curDirName);
00625
00626 dirEntries = curDir->entries();
00627 for ( it = dirEntries.begin(); it != dirEntries.end(); ++it ) {
00628 curEntry = curDir->entry(*it);
00629 if ( curEntry->isFile() ) {
00630 curFile = dynamic_cast<KArchiveFile*>( curEntry );
00631 if (curFile) {
00632 fileList.append( curFile );
00633 fileToDir.insert( curFile->position(), curDirName );
00634 }
00635 }
00636
00637 if ( curEntry->isDirectory() )
00638 if ( recursiveCopy ) {
00639 KArchiveDirectory *ad = dynamic_cast<KArchiveDirectory*>( curEntry );
00640 if (ad) {
00641 dirStack.push( ad );
00642 dirNameStack.push( curDirName + "/" + curEntry->name() );
00643 }
00644 }
00645 }
00646 } while (!dirStack.isEmpty());
00647
00648 fileList.sort();
00649
00650 KArchiveFile* f;
00651 for ( f = fileList.first(); f; f = fileList.next() ) {
00652 int pos = f->position();
00653 f->copyTo( fileToDir[pos] );
00654 }
00655 }
00656
00657 void KArchive::virtual_hook( int id, void* data )
00658 {
00659 switch (id) {
00660 case VIRTUAL_WRITE_DATA: {
00661 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
00662 params->retval = writeData_impl( params->data, params->size );
00663 break;
00664 }
00665 case VIRTUAL_WRITE_SYMLINK: {
00666 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
00667 params->retval = writeSymLink_impl(*params->name,*params->target,
00668 *params->user,*params->group,params->perm,
00669 params->atime,params->mtime,params->ctime);
00670 break;
00671 }
00672 case VIRTUAL_WRITE_DIR: {
00673 WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data);
00674 params->retval = writeDir_impl(*params->name,*params->user,
00675 *params->group,params->perm,
00676 params->atime,params->mtime,params->ctime);
00677 break;
00678 }
00679 case VIRTUAL_WRITE_FILE: {
00680 WriteFileParams *params = reinterpret_cast<WriteFileParams *>(data);
00681 params->retval = writeFile_impl(*params->name,*params->user,
00682 *params->group,params->size,params->perm,
00683 params->atime,params->mtime,params->ctime,
00684 params->data);
00685 break;
00686 }
00687 case VIRTUAL_PREPARE_WRITING: {
00688 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
00689 params->retval = prepareWriting_impl(*params->name,*params->user,
00690 *params->group,params->size,params->perm,
00691 params->atime,params->mtime,params->ctime);
00692 break;
00693 }
00694 default:
00695 ;
00696 }
00697 }
00698
00699 void KArchiveEntry::virtual_hook( int, void* )
00700 { }
00701
00702 void KArchiveFile::virtual_hook( int id, void* data )
00703 { KArchiveEntry::virtual_hook( id, data ); }
00704
00705 void KArchiveDirectory::virtual_hook( int id, void* data )
00706 { KArchiveEntry::virtual_hook( id, data ); }