1 /* This file is part of the KDE libraries
2 Copyright (C) 2000 David Faure <faure@kde.org>
3 Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
22 #include <stdlib.h> // strtol
23 #include <time.h> // time()
26 #include <QtCore/QDir>
27 #include <QtCore/QFile>
29 #include <kmimetype.h>
30 #include <ktemporaryfile.h>
32 #include <kfilterdev.h>
33 #include <kfilterbase.h>
35 #include <kstandarddirs.h>
37 ////////////////////////////////////////////////////////////////////////
38 /////////////////////////// KTar ///////////////////////////////////
39 ////////////////////////////////////////////////////////////////////////
41 class KTar::KTarPrivate
44 KTarPrivate(KTar
*parent
)
54 KTemporaryFile
* tmpFile
;
56 QByteArray origFileName
;
58 bool fillTempFile(const QString
& fileName
);
59 bool writeBackTempFile( const QString
& fileName
);
60 void fillBuffer( char * buffer
, const char * mode
, qint64 size
, time_t mtime
,
61 char typeflag
, const char * uname
, const char * gname
);
62 void writeLonglink(char *buffer
, const QByteArray
&name
, char typeflag
,
63 const char *uname
, const char *gname
);
64 qint64
readRawHeader(char *buffer
);
65 bool readLonglink(char *buffer
, QByteArray
&longlink
);
66 qint64
readHeader(char *buffer
, QString
&name
, QString
&symlink
);
69 KTar::KTar( const QString
& fileName
, const QString
& _mimetype
)
70 : KArchive( fileName
), d(new KTarPrivate(this))
72 d
->mimetype
= _mimetype
;
75 KTar::KTar( QIODevice
* dev
)
76 : KArchive( dev
),d(new KTarPrivate(this))
81 // Only called when the a filename was given
82 bool KTar::createDevice( QIODevice::OpenMode mode
)
85 if ( d
->mimetype
.isEmpty() ) // Find out mimetype manually
87 if ( mode
!= QIODevice::WriteOnly
&& QFile::exists( fileName() ) )
88 d
->mimetype
= KMimeType::findByFileContent( fileName() )->name();
90 d
->mimetype
= KMimeType::findByPath( fileName(), 0, true )->name();
91 kDebug(7041) << "mimetype=" << d
->mimetype
;
93 if ( d
->mimetype
== "application/x-compressed-tar"
94 || d
->mimetype
== "application/x-tgz" // old deprecated name
95 || d
->mimetype
== "application/x-targz" // old deprecated name
96 || d
->mimetype
== "application/x-webarchive" )
98 // that's a gzipped tar file, so ask for gzip filter
99 d
->mimetype
= "application/x-gzip";
101 else if ( d
->mimetype
== "application/x-bzip-compressed-tar" ) // that's a bzipped2 tar file, so ask for bz2 filter
103 d
->mimetype
= "application/x-bzip";
107 // Something else. Check if it's not really gzip though (e.g. for old-style KOffice files)
108 QFile
file( fileName() );
109 if ( file
.open( QIODevice::ReadOnly
) )
111 char firstByte
, secondByte
, thirdByte
;
112 if ( file
.getChar( &firstByte
) &&
113 file
.getChar( &secondByte
) &&
114 file
.getChar( &thirdByte
) ) {
115 if ( firstByte
== 0037 && static_cast<uchar
>(secondByte
) == static_cast<uchar
>(0213) )
116 d
->mimetype
= "application/x-gzip";
117 else if ( firstByte
== 'B' && secondByte
== 'Z' && thirdByte
== 'h' )
118 d
->mimetype
= "application/x-bzip";
119 else if ( firstByte
== 'P' && secondByte
== 'K' && thirdByte
== 3 )
122 if ( file
.getChar(&fourthByte
) && fourthByte
== 4 )
123 d
->mimetype
= "application/zip";
131 if( d
->mimetype
== "application/x-tar" )
133 return KArchive::createDevice( mode
);
137 // The compression filters are very slow with random access.
138 // So instead of applying the filter to the device,
139 // the file is completely extracted instead,
140 // and we work on the extracted tar file.
141 // This improves the extraction speed by the tar ioslave dramatically,
142 // if the archive file contains many files.
143 // This is because the tar ioslave extracts one file after the other and normally
144 // has to walk through the decompression filter each time.
145 // Which is in fact nearly as slow as a complete decompression for each file.
147 Q_ASSERT(!d
->tmpFile
);
148 d
->tmpFile
= new KTemporaryFile();
149 d
->tmpFile
->setPrefix("ktar-");
150 d
->tmpFile
->setSuffix(".tar");
152 kDebug( 7041 ) << "creating tempfile:" << d
->tmpFile
->fileName();
154 setDevice( d
->tmpFile
);
161 // mjarrett: Closes to prevent ~KArchive from aborting w/o device
169 void KTar::setOrigFileName( const QByteArray
& fileName
) {
170 if ( !isOpen() || !(mode() & QIODevice::WriteOnly
) )
172 kWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n";
175 d
->origFileName
= fileName
;
178 qint64
KTar::KTarPrivate::readRawHeader( char *buffer
) {
180 qint64 n
= q
->device()->read( buffer
, 0x200 );
181 if ( n
== 0x200 && buffer
[0] != 0 ) {
182 // Make sure this is actually a tar header
183 if (strncmp(buffer
+ 257, "ustar", 5)) {
184 // The magic isn't there (broken/old tars), but maybe a correct checksum?
187 for( uint j
= 0; j
< 0x200; ++j
)
190 // adjust checksum to count the checksum fields as blanks
191 for( uint j
= 0; j
< 8 /*size of the checksum field including the \0 and the space*/; j
++ )
192 check
-= buffer
[148 + j
];
195 QByteArray s
= QByteArray::number( check
, 8 ); // octal
197 // only compare those of the 6 checksum digits that mean something,
198 // because the other digits are filled with all sorts of different chars by different tars ...
199 // Some tars right-justify the checksum so it could start in one of three places - we have to check each.
200 if( strncmp( buffer
+ 148 + 6 - s
.length(), s
.data(), s
.length() )
201 && strncmp( buffer
+ 148 + 7 - s
.length(), s
.data(), s
.length() )
202 && strncmp( buffer
+ 148 + 8 - s
.length(), s
.data(), s
.length() ) ) {
203 kWarning(7041) << "KTar: invalid TAR file. Header is:" << QByteArray( buffer
+257, 5 )
204 << "instead of ustar. Reading from wrong pos in file?"
205 << "checksum=" << QByteArray( buffer
+ 148 + 6 - s
.length(), s
.length() );
210 // reset to 0 if 0x200 because logical end of archive has been reached
211 if (n
== 0x200) n
= 0;
216 bool KTar::KTarPrivate::readLonglink(char *buffer
,QByteArray
&longlink
) {
218 //kDebug() << "reading longlink from pos " << device()->pos();
219 QIODevice
*dev
= q
->device();
220 // read size of longlink from size field in header
221 // size is in bytes including the trailing null (which we ignore)
222 qint64 size
= QByteArray( buffer
+ 0x7c, 12 ).trimmed().toLongLong( 0, 8 /*octal*/ );
224 size
--; // ignore trailing null
225 longlink
.resize(size
);
228 int chunksize
= qMin(size
, 0x200LL
);
229 n
= dev
->read( longlink
.data() + offset
, chunksize
);
230 if (n
== -1) return false;
234 // jump over the rest
235 const int skip
= 0x200 - (n
% 0x200);
237 if (dev
->read(buffer
,skip
) != skip
)
243 qint64
KTar::KTarPrivate::readHeader( char *buffer
, QString
&name
, QString
&symlink
) {
247 qint64 n
= readRawHeader(buffer
);
248 if (n
!= 0x200) return n
;
251 if (strcmp(buffer
,"././@LongLink") == 0) {
252 char typeflag
= buffer
[0x9c];
254 readLonglink(buffer
,longlink
);
256 case 'L': name
= QFile::decodeName(longlink
); break;
257 case 'K': symlink
= QFile::decodeName(longlink
); break;
264 // if not result of longlink, read names directly from the header
266 // there are names that are exactly 100 bytes long
267 // and neither longlink nor \0 terminated (bug:101472)
268 name
= QFile::decodeName(QByteArray(buffer
, 100));
269 if (symlink
.isEmpty())
270 symlink
= QFile::decodeName(QByteArray(buffer
+ 0x9d /*?*/, 100));
276 * If we have created a temporary file, we have
277 * to decompress the original file now and write
278 * the contents to the temporary file.
280 bool KTar::KTarPrivate::fillTempFile( const QString
& fileName
) {
284 kDebug( 7041 ) << "filling tmpFile of mimetype" << mimetype
;
287 if( "application/x-gzip" == mimetype
288 || "application/x-bzip" == mimetype
)
291 QIODevice
*filterDev
= KFilterDev::deviceForFile( fileName
, mimetype
, forced
);
294 QFile
* file
= tmpFile
;
295 Q_ASSERT(file
->isOpen());
296 Q_ASSERT(file
->openMode() & QIODevice::WriteOnly
);
299 buffer
.resize(8*1024);
300 if ( ! filterDev
->open( QIODevice::ReadOnly
) )
306 while ( !filterDev
->atEnd() && len
!= 0 ) {
307 len
= filterDev
->read(buffer
.data(),buffer
.size());
308 if ( len
< 0 ) { // corrupted archive
312 if ( file
->write(buffer
.data(), len
) != len
) { // disk full
322 Q_ASSERT(file
->isOpen());
323 Q_ASSERT(file
->openMode() & QIODevice::ReadOnly
);
326 kDebug( 7041 ) << "no filterdevice found!";
328 //kDebug( 7041 ) << "filling tmpFile finished.";
332 bool KTar::openArchive( QIODevice::OpenMode mode
) {
334 if ( !(mode
& QIODevice::ReadOnly
) )
337 if ( !d
->fillTempFile( fileName() ) )
340 // We'll use the permission and user/group of d->rootDir
341 // for any directory we emulate (see findOrCreate)
343 //stat( fileName(), &buf );
346 QIODevice
* dev
= device();
352 char buffer
[ 0x200 ];
360 qint64 n
= d
->readHeader( buffer
, name
, symlink
);
361 if (n
< 0) return false;
366 if ( name
.endsWith( QLatin1Char( '/' ) ) )
369 name
.truncate( name
.length() - 1 );
372 int pos
= name
.lastIndexOf( '/' );
373 QString nm
= ( pos
== -1 ) ? name
: name
.mid( pos
+ 1 );
378 const char* p
= buffer
+ 0x64;
379 while( *p
== ' ' ) ++p
;
380 int access
= (int)strtol( p
, &dummy
, 8 );
382 // read user and group
383 QString
user( buffer
+ 0x109 );
384 QString
group( buffer
+ 0x129 );
389 while( *p
== ' ' ) ++p
;
390 int time
= (int)strtol( p
, &dummy
, 8 );
393 char typeflag
= buffer
[ 0x9c ];
394 // '0' for files, '1' hard link, '2' symlink, '5' for directory
395 // (and 'L' for longlink fileNames, 'K' for longlink symlink targets)
396 // and 'D' for GNU tar extension DUMPDIR
397 if ( typeflag
== '5' )
400 bool isDumpDir
= false;
401 if ( typeflag
== 'D' )
406 //kDebug(7041) << "typeflag=" << typeflag << " islink=" << ( typeflag == '1' || typeflag == '2' );
409 access
|= S_IFDIR
; // f*cking broken tar files
414 //kDebug(7041) << "directory" << nm;
415 e
= new KArchiveDirectory( this, nm
, access
, time
, user
, group
, symlink
);
420 QByteArray
sizeBuffer( buffer
+ 0x7c, 12 );
421 qint64 size
= sizeBuffer
.trimmed().toLongLong( 0, 8 /*octal*/ );
422 //kDebug(7041) << "sizeBuffer='" << sizeBuffer << "' -> size=" << size;
424 // for isDumpDir we will skip the additional info about that dirs contents
427 //kDebug(7041) << nm << "isDumpDir";
428 e
= new KArchiveDirectory( this, nm
, access
, time
, user
, group
, symlink
);
433 // Let's hack around hard links. Our classes don't support that, so make them symlinks
434 if ( typeflag
== '1' )
436 kDebug(7041) << "HARD LINK, setting size to 0 instead of " << size
;
437 size
= 0; // no contents
440 //kDebug(7041) << "file" << nm << "size=" << size;
441 e
= new KArchiveFile( this, nm
, access
, time
, user
, group
, symlink
,
445 // Skip contents + align bytes
446 int rest
= size
% 0x200;
447 int skip
= size
+ (rest
? 0x200 - rest
: 0);
448 //kDebug(7041) << "pos()=" << dev->pos() << "rest=" << rest << "skipping" << skip;
449 if (! dev
->seek( dev
->pos() + skip
) )
450 kWarning(7041) << "skipping" << skip
<< "failed";
455 if ( nm
== "." ) // special case
459 setRootDir( static_cast<KArchiveDirectory
*>( e
) );
462 rootDir()->addEntry( e
);
466 // In some tar files we can find dir/./file => call cleanPath
467 QString path
= QDir::cleanPath( name
.left( pos
) );
468 // Ensure container directory exists, create otherwise
469 KArchiveDirectory
* d
= findOrCreate( path
);
475 //qDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]);
476 d
->tarEnd
= dev
->pos() - n
; // Remember end of archive
484 * Writes back the changes of the temporary file
485 * to the original file.
486 * Must only be called if in QIODevice::WriteOnly mode
488 bool KTar::KTarPrivate::writeBackTempFile( const QString
& fileName
) {
492 kDebug(7041) << "Write temporary file to compressed file";
493 kDebug(7041) << fileName
<< " " << mimetype
;
496 if( "application/x-gzip" == mimetype
497 || "application/x-bzip" == mimetype
)
500 // #### TODO this should use KSaveFile to avoid problems on disk full
501 // (KArchive uses KSaveFile by default, but the temp-uncompressed-file trick
502 // circumvents that).
504 QIODevice
*dev
= KFilterDev::deviceForFile( fileName
, mimetype
, forced
);
506 QFile
* file
= tmpFile
;
507 if ( !dev
->open(QIODevice::WriteOnly
) )
514 static_cast<KFilterDev
*>(dev
)->setOrigFileName( origFileName
);
517 buffer
.resize(8*1024);
519 while ( !file
->atEnd()) {
520 len
= file
->read(buffer
.data(), buffer
.size());
521 dev
->write(buffer
.data(),len
); // TODO error checking
528 kDebug(7041) << "Write temporary file to compressed file done.";
532 bool KTar::closeArchive() {
537 // If we are in write mode and had created
538 // a temporary tar file, we have to write
539 // back the changes to the original file
540 if( mode() == QIODevice::WriteOnly
) {
541 ok
= d
->writeBackTempFile( fileName() );
550 bool KTar::doFinishWriting( qint64 size
) {
552 int rest
= size
% 0x200;
553 if ( ( mode() & QIODevice::ReadWrite
) == QIODevice::ReadWrite
)
554 d
->tarEnd
= device()->pos() + (rest
? 0x200 - rest
: 0); // Record our new end of archive
557 char buffer
[ 0x201 ];
558 for( uint i
= 0; i
< 0x200; ++i
)
560 qint64 nwritten
= device()->write( buffer
, 0x200 - rest
);
561 return nwritten
== 0x200 - rest
;
566 /*** Some help from the tar sources
569 char name[100]; * 0 * 0x0
570 char mode[8]; * 100 * 0x64
571 char uid[8]; * 108 * 0x6c
572 char gid[8]; * 116 * 0x74
573 char size[12]; * 124 * 0x7c
574 char mtime[12]; * 136 * 0x88
575 char chksum[8]; * 148 * 0x94
576 char typeflag; * 156 * 0x9c
577 char linkname[100]; * 157 * 0x9d
578 char magic[6]; * 257 * 0x101
579 char version[2]; * 263 * 0x107
580 char uname[32]; * 265 * 0x109
581 char gname[32]; * 297 * 0x129
582 char devmajor[8]; * 329 * 0x149
583 char devminor[8]; * 337 * ...
584 char prefix[155]; * 345 *
589 void KTar::KTarPrivate::fillBuffer( char * buffer
,
590 const char * mode
, qint64 size
, time_t mtime
, char typeflag
,
591 const char * uname
, const char * gname
) {
592 // mode (as in stpos())
593 assert( strlen(mode
) == 6 );
594 memcpy( buffer
+0x64, mode
, 6 );
595 buffer
[ 0x6a ] = ' ';
596 buffer
[ 0x6b ] = '\0';
599 strcpy( buffer
+ 0x6c, " 765 ");
601 strcpy( buffer
+ 0x74, " 144 ");
604 QByteArray s
= QByteArray::number( size
, 8 ); // octal
605 s
= s
.rightJustified( 11, '0' );
606 memcpy( buffer
+ 0x7c, s
.data(), 11 );
607 buffer
[ 0x87 ] = ' '; // space-terminate (no null after)
610 s
= QByteArray::number( static_cast<qulonglong
>(mtime
), 8 ); // octal
611 s
= s
.rightJustified( 11, '0' );
612 memcpy( buffer
+ 0x88, s
.data(), 11 );
613 buffer
[ 0x93 ] = ' '; // space-terminate (no null after) -- well current tar writes a null byte
615 // spaces, replaced by the check sum later
616 buffer
[ 0x94 ] = 0x20;
617 buffer
[ 0x95 ] = 0x20;
618 buffer
[ 0x96 ] = 0x20;
619 buffer
[ 0x97 ] = 0x20;
620 buffer
[ 0x98 ] = 0x20;
621 buffer
[ 0x99 ] = 0x20;
623 /* From the tar sources :
624 Fill in the checksum field. It's formatted differently from the
625 other fields: it has [6] digits, a null, then a space -- rather than
626 digits, a space, then a null. */
628 buffer
[ 0x9a ] = '\0';
629 buffer
[ 0x9b ] = ' ';
631 // type flag (dir, file, link)
632 buffer
[ 0x9c ] = typeflag
;
635 strcpy( buffer
+ 0x101, "ustar");
636 strcpy( buffer
+ 0x107, "00" );
639 strcpy( buffer
+ 0x109, uname
);
641 strcpy( buffer
+ 0x129, gname
);
645 for( uint j
= 0; j
< 0x200; ++j
)
647 s
= QByteArray::number( check
, 8 ); // octal
648 s
= s
.rightJustified( 6, '0' );
649 memcpy( buffer
+ 0x94, s
.constData(), 6 );
652 void KTar::KTarPrivate::writeLonglink(char *buffer
, const QByteArray
&name
, char typeflag
,
653 const char *uname
, const char *gname
) {
654 strcpy( buffer
, "././@LongLink" );
655 qint64 namelen
= name
.length() + 1;
656 fillBuffer( buffer
, " 0", namelen
, 0, typeflag
, uname
, gname
);
657 q
->device()->write( buffer
, 0x200 ); // TODO error checking
659 while (namelen
> 0) {
660 int chunksize
= qMin(namelen
, 0x200LL
);
661 memcpy(buffer
, name
.data()+offset
, chunksize
);
663 q
->device()->write( buffer
, 0x200 ); // TODO error checking
664 // not even needed to reclear the buffer, tar doesn't do it
665 namelen
-= chunksize
;
670 bool KTar::doPrepareWriting(const QString
&name
, const QString
&user
,
671 const QString
&group
, qint64 size
, mode_t perm
,
672 time_t /*atime*/, time_t mtime
, time_t /*ctime*/) {
675 kWarning(7041) << "You must open the tar file before writing to it\n";
679 if ( !(mode() & QIODevice::WriteOnly
) )
681 kWarning(7041) << "You must open the tar file for writing\n";
685 // In some tar files we can find dir/./file => call cleanPath
686 QString
fileName ( QDir::cleanPath( name
) );
689 // Create toplevel dirs
690 // Commented out by David since it's not necessary, and if anybody thinks it is,
691 // he needs to implement a findOrCreate equivalent in writeDir.
692 // But as KTar and the "tar" program both handle tar files without
693 // dir entries, there's really no need for that
694 QString tmp ( fileName );
695 int i = tmp.lastIndexOf( '/' );
698 QString d = tmp.left( i + 1 ); // contains trailing slash
699 if ( !m_dirList.contains( d ) )
701 tmp = tmp.mid( i + 1 );
702 writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs
707 char buffer
[ 0x201 ];
708 memset( buffer
, 0, 0x200 );
709 if ( ( mode() & QIODevice::ReadWrite
) == QIODevice::ReadWrite
)
710 device()->seek(d
->tarEnd
); // Go to end of archive as might have moved with a read
712 // provide converted stuff we need later on
713 const QByteArray encodedFileName
= QFile::encodeName(fileName
);
714 const QByteArray uname
= user
.toLocal8Bit();
715 const QByteArray gname
= group
.toLocal8Bit();
717 // If more than 100 chars, we need to use the LongLink trick
718 if ( fileName
.length() > 99 )
719 d
->writeLonglink(buffer
,encodedFileName
,'L',uname
,gname
);
721 // Write (potentially truncated) name
722 strncpy( buffer
, encodedFileName
, 99 );
724 // zero out the rest (except for what gets filled anyways)
725 memset(buffer
+0x9d, 0, 0x200 - 0x9d);
727 QByteArray permstr
= QByteArray::number( (unsigned int)perm
, 8 );
728 permstr
= permstr
.rightJustified(6, '0');
729 d
->fillBuffer(buffer
, permstr
, size
, mtime
, 0x30, uname
, gname
);
732 return device()->write( buffer
, 0x200 ) == 0x200;
735 bool KTar::doWriteDir(const QString
&name
, const QString
&user
,
736 const QString
&group
, mode_t perm
,
737 time_t /*atime*/, time_t mtime
, time_t /*ctime*/) {
740 kWarning(7041) << "You must open the tar file before writing to it\n";
744 if ( !(mode() & QIODevice::WriteOnly
) )
746 kWarning(7041) << "You must open the tar file for writing\n";
750 // In some tar files we can find dir/./ => call cleanPath
751 QString
dirName ( QDir::cleanPath( name
) );
754 if ( !dirName
.endsWith( QLatin1Char( '/' ) ) )
755 dirName
+= QLatin1Char( '/' );
757 if ( d
->dirList
.contains( dirName
) )
758 return true; // already there
760 char buffer
[ 0x201 ];
761 memset( buffer
, 0, 0x200 );
762 if ( ( mode() & QIODevice::ReadWrite
) == QIODevice::ReadWrite
)
763 device()->seek(d
->tarEnd
); // Go to end of archive as might have moved with a read
765 // provide converted stuff we need lateron
766 QByteArray encodedDirname
= QFile::encodeName(dirName
);
767 QByteArray uname
= user
.toLocal8Bit();
768 QByteArray gname
= group
.toLocal8Bit();
770 // If more than 100 chars, we need to use the LongLink trick
771 if ( dirName
.length() > 99 )
772 d
->writeLonglink(buffer
,encodedDirname
,'L',uname
,gname
);
774 // Write (potentially truncated) name
775 strncpy( buffer
, encodedDirname
, 99 );
777 // zero out the rest (except for what gets filled anyways)
778 memset(buffer
+0x9d, 0, 0x200 - 0x9d);
780 QByteArray permstr
= QByteArray::number( (unsigned int)perm
, 8 );
781 permstr
= permstr
.rightJustified(6, ' ');
782 d
->fillBuffer( buffer
, permstr
, 0, mtime
, 0x35, uname
, gname
);
785 device()->write( buffer
, 0x200 );
786 if ( ( mode() & QIODevice::ReadWrite
) == QIODevice::ReadWrite
)
787 d
->tarEnd
= device()->pos();
789 d
->dirList
.append( dirName
); // contains trailing slash
790 return true; // TODO if wanted, better error control
793 bool KTar::doWriteSymLink(const QString
&name
, const QString
&target
,
794 const QString
&user
, const QString
&group
,
795 mode_t perm
, time_t /*atime*/, time_t mtime
, time_t /*ctime*/) {
798 kWarning(7041) << "You must open the tar file before writing to it\n";
802 if ( !(mode() & QIODevice::WriteOnly
) )
804 kWarning(7041) << "You must open the tar file for writing\n";
808 // In some tar files we can find dir/./file => call cleanPath
809 QString
fileName ( QDir::cleanPath( name
) );
811 char buffer
[ 0x201 ];
812 memset( buffer
, 0, 0x200 );
813 if ( ( mode() & QIODevice::ReadWrite
) == QIODevice::ReadWrite
)
814 device()->seek(d
->tarEnd
); // Go to end of archive as might have moved with a read
816 // provide converted stuff we need lateron
817 QByteArray encodedFileName
= QFile::encodeName(fileName
);
818 QByteArray encodedTarget
= QFile::encodeName(target
);
819 QByteArray uname
= user
.toLocal8Bit();
820 QByteArray gname
= group
.toLocal8Bit();
822 // If more than 100 chars, we need to use the LongLink trick
823 if (target
.length() > 99)
824 d
->writeLonglink(buffer
,encodedTarget
,'K',uname
,gname
);
825 if ( fileName
.length() > 99 )
826 d
->writeLonglink(buffer
,encodedFileName
,'L',uname
,gname
);
828 // Write (potentially truncated) name
829 strncpy( buffer
, encodedFileName
, 99 );
831 // Write (potentially truncated) symlink target
832 strncpy(buffer
+0x9d, encodedTarget
, 99);
835 memset(buffer
+0x9d+100, 0, 0x200 - 100 - 0x9d);
837 QByteArray permstr
= QByteArray::number( (unsigned int)perm
, 8 );
838 permstr
= permstr
.rightJustified(6, ' ');
839 d
->fillBuffer(buffer
, permstr
, 0, mtime
, 0x32, uname
, gname
);
842 bool retval
= device()->write( buffer
, 0x200 ) == 0x200;
843 if ( ( mode() & QIODevice::ReadWrite
) == QIODevice::ReadWrite
)
844 d
->tarEnd
= device()->pos();
848 void KTar::virtual_hook( int id
, void* data
) {
849 KArchive::virtual_hook( id
, data
);