kio Library API Documentation

karchive.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 David Faure <faure@kde.org> 00003 Copyright (C) 2003 Leo Savernik <l.savernik@aon.at> 00004 00005 Moved from ktar.cpp by Roberto Teixeira <maragato@kde.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License version 2 as published by the Free Software Foundation. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00019 Boston, MA 02111-1307, USA. 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; // Fail w/o segfaulting if the device is no good 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 // moved by holger to allow kzip to write the zip central dir 00114 // to the file in closeArchive() 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 // rootDir isn't const so that parsing-on-demand is possible 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 }/*end if*/ 00152 00153 uint size = fileInfo.size(); 00154 00155 // the file must be opened before prepareWriting is called, otherwise 00156 // if the opening fails, no content will follow the already written 00157 // header and the tar file is effectively f*cked up 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 // Read and write data in chunks to minimize memory usage 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 // kdDebug() << "storing " << fileName << endl; 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 // We omit sockets 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,&params); 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 /*perm*/, 00247 time_t /*atime*/, time_t /*mtime*/, time_t /*ctime*/ ) { 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,&params); 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 // Write data 00283 // Note: if data is 0L, don't call writeBlock, it would terminate the KFilterDev 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,&params); 00310 return params.retval; 00311 } 00312 00313 bool KArchive::writeDir_impl(const QString &name, const QString &user, 00314 const QString &group, mode_t /*perm*/, 00315 time_t /*atime*/, time_t /*mtime*/, time_t /*ctime*/ ) { 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 = &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,&params); 00334 return params.retval; 00335 } 00336 00337 bool KArchive::writeSymLink_impl(const QString &/*name*/,const QString &/*target*/, 00338 const QString &/*user*/, const QString &/*group*/, 00339 mode_t /*perm*/, time_t /*atime*/, time_t /*mtime*/, 00340 time_t /*ctime*/) { 00341 kdWarning(7040) << "writeSymLink not implemented in this class." << endl 00342 << "No fallback available." << endl; 00343 // FIXME: better return true here for compatibility with KDE < 3.2 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, &params ); 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 //kdDebug() << "Making root dir " << endl; 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 //kdDebug() << "KArchive::findOrCreate " << path << endl; 00379 if ( path.isEmpty() || path == "/" || path == "." ) // root dir => found 00380 { 00381 //kdDebug() << "KArchive::findOrCreate returning rootdir" << endl; 00382 return rootDir(); 00383 } 00384 // Important note : for tar files containing absolute paths 00385 // (i.e. beginning with "/"), this means the leading "/" will 00386 // be removed (no KDirectory for it), which is exactly the way 00387 // the "tar" program works (though it displays a warning about it) 00388 // See also KArchiveDirectory::entry(). 00389 00390 // Already created ? => found 00391 KArchiveEntry* ent = rootDir()->entry( path ); 00392 if ( ent && ent->isDirectory() ) 00393 { 00394 //kdDebug() << "KArchive::findOrCreate found it" << endl; 00395 return (KArchiveDirectory *) ent; 00396 } 00397 00398 // Otherwise go up and try again 00399 int pos = path.findRev( '/' ); 00400 KArchiveDirectory * parent; 00401 QString dirname; 00402 if ( pos == -1 ) // no more slash => create in root dir 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 ); // recursive call... until we find an existing dir. 00412 } 00413 00414 //kdDebug() << "KTar : found parent " << parent->name() << " adding " << dirname << " to ensure " << path << endl; 00415 // Found -> add the missing piece 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; // now a directory to <path> exists 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 ); // Call setRootDir only once during parsing please ;) 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 // Read content 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 // ** This should be a virtual method, and this code should be in ktar.cpp 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 // not "const QString & name" since we want a local copy 00539 // (to remove leading slash if any) 00540 { 00541 int pos = name.find( '/' ); 00542 if ( pos == 0 ) // ouch absolute path (see also KArchive::findOrCreate) 00543 { 00544 if (name.length()>1) 00545 { 00546 name = name.mid( 1 ); // remove leading slash 00547 pos = name.find( '/' ); // look again 00548 } 00549 else // "/" 00550 return this; 00551 } 00552 // trailing slash ? -> remove 00553 if ( pos != -1 && pos == (int)name.length()-1 ) 00554 { 00555 name = name.left( pos ); 00556 pos = name.find( '/' ); // look again 00557 } 00558 if ( pos != -1 ) 00559 { 00560 QString left = name.left( pos ); 00561 QString right = name.mid( pos + 1 ); 00562 00563 //kdDebug() << "KArchiveDirectory::entry left=" << left << " right=" << right << endl; 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 // placeholders for iterated items 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 ); // init stack at current directory 00607 dirNameStack.push( dest ); // ... with given path 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(); // sort on m_pos, so we have a linear access 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 /*BASE::virtual_hook( id, data )*/; 00683 }/*end switch*/ 00684 } 00685 00686 void KArchiveEntry::virtual_hook( int, void* ) 00687 { /*BASE::virtual_hook( id, data );*/ } 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 ); }
KDE Logo
This file is part of the documentation for kio Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 10 18:55:25 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003