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
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;
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
00114
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
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 }
00152
00153 uint size = fileInfo.
size();
00154
00155
00156
00157
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
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
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
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,¶ms);
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 ,
00247 time_t , time_t , time_t ) {
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,¶ms);
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
00283
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,¶ms);
00310
return params.retval;
00311 }
00312
00313
bool KArchive::writeDir_impl(
const QString &name,
const QString &user,
00314
const QString &group, mode_t ,
00315 time_t , time_t , time_t ) {
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 = ⌖
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,¶ms);
00334
return params.retval;
00335 }
00336
00337
bool KArchive::writeSymLink_impl(
const QString &,
const QString &,
00338
const QString &,
const QString &,
00339 mode_t , time_t , time_t ,
00340 time_t ) {
00341
kdWarning(7040) <<
"writeSymLink not implemented in this class." <<
endl
00342 <<
"No fallback available." <<
endl;
00343
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, ¶ms );
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
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 ); }