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 ); }