26 #include <QtCore/QDir>
27 #include <QtCore/QFile>
46 class KTar::KTarPrivate
49 KTarPrivate(
KTar *parent)
61 QByteArray origFileName;
64 bool writeBackTempFile(
const QString & fileName );
65 void fillBuffer(
char * buffer,
const char *
mode,
qint64 size, time_t mtime,
66 char typeflag,
const char * uname,
const char * gname );
67 void writeLonglink(
char *buffer,
const QByteArray &name,
char typeflag,
68 const char *uname,
const char *gname);
69 qint64 readRawHeader(
char *buffer);
70 bool readLonglink(
char *buffer, QByteArray &longlink);
75 :
KArchive( fileName ), d(new KTarPrivate(this))
77 d->mimetype = _mimetype;
81 :
KArchive( dev ), d(new KTarPrivate(this))
89 if (d->mimetype.isEmpty()) {
93 if (mode != QIODevice::WriteOnly && QFile::exists(
fileName())) {
107 if (mime->
is(QString::fromLatin1(
"application/x-compressed-tar")) || mime->
is(QString::fromLatin1(
application_gzip))) {
110 }
else if (mime->
is(QString::fromLatin1(
"application/x-bzip-compressed-tar")) || mime->
is(QString::fromLatin1(
application_bzip))) {
113 }
else if (mime->
is(QString::fromLatin1(
"application/x-lzma-compressed-tar")) || mime->
is(QString::fromLatin1(
application_lzma))) {
116 }
else if (mime->
is(QString::fromLatin1(
"application/x-xz-compressed-tar")) || mime->
is(QString::fromLatin1(
application_xz))) {
122 if (d->mimetype == QLatin1String(
"application/x-tar")) {
124 }
else if (mode == QIODevice::WriteOnly) {
127 if (!d->mimetype.isEmpty()) {
146 Q_ASSERT(!d->tmpFile);
148 d->tmpFile->setPrefix(QLatin1String(
"ktar-"));
149 d->tmpFile->setSuffix(QLatin1String(
".tar"));
169 if ( !
isOpen() || !(
mode() & QIODevice::WriteOnly) )
171 kWarning(7041) <<
"KTar::setOrigFileName: File must be opened for writing first.\n";
177 qint64 KTar::KTarPrivate::readRawHeader(
char *buffer ) {
179 qint64 n = q->device()->read( buffer, 0x200 );
182 if ( n == 0x200 && (buffer[0] != 0 || buffer[0x159] != 0) ) {
184 if (strncmp(buffer + 257,
"ustar", 5)) {
188 for( uint j = 0; j < 0x200; ++j )
192 for( uint j = 0; j < 8 ; j++ )
193 check -= buffer[148 + j];
196 QByteArray s = QByteArray::number( check, 8 );
201 if( strncmp( buffer + 148 + 6 - s.length(), s.data(), s.length() )
202 && strncmp( buffer + 148 + 7 - s.length(), s.data(), s.length() )
203 && strncmp( buffer + 148 + 8 - s.length(), s.data(), s.length() ) ) {
204 kWarning(7041) <<
"KTar: invalid TAR file. Header is:" << QByteArray( buffer+257, 5 )
205 <<
"instead of ustar. Reading from wrong pos in file?"
206 <<
"checksum=" << QByteArray( buffer + 148 + 6 - s.length(), s.length() );
212 if (n == 0x200) n = 0;
217 bool KTar::KTarPrivate::readLonglink(
char *buffer,QByteArray &longlink) {
223 qint64 size = QByteArray( buffer + 0x7c, 12 ).trimmed().toLongLong( 0, 8 );
226 longlink.resize(size);
229 int chunksize = qMin(size, 0x200LL);
230 n = dev->read( longlink.data() + offset, chunksize );
231 if (n == -1)
return false;
236 const int skip = 0x200 - (n % 0x200);
238 if (dev->read(buffer,skip) != skip)
248 qint64 n = readRawHeader(buffer);
249 if (n != 0x200)
return n;
252 if (strcmp(buffer,
"././@LongLink") == 0) {
253 char typeflag = buffer[0x9c];
255 readLonglink(buffer,longlink);
257 case 'L': name = QFile::decodeName(longlink);
break;
258 case 'K': symlink = QFile::decodeName(longlink);
break;
269 name = QFile::decodeName(QByteArray(buffer, 100));
270 if (symlink.isEmpty())
271 symlink = QFile::decodeName(QByteArray(buffer + 0x9d , 100));
281 bool KTar::KTarPrivate::fillTempFile(
const QString & fileName) {
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());
312 if ( file->write(buffer.data(), len) != len ) {
322 Q_ASSERT(file->isOpen());
323 Q_ASSERT(file->openMode() & QIODevice::ReadOnly);
325 kDebug(7041) <<
"no filterdevice found!";
334 if ( !(mode & QIODevice::ReadOnly) )
337 if ( !d->fillTempFile( fileName() ) )
352 char buffer[ 0x200 ];
360 qint64 n = d->readHeader( buffer, name, symlink );
361 if (n < 0)
return false;
365 bool isGlobalHeader =
false;
367 if ( name.endsWith( QLatin1Char(
'/' ) ) )
370 name.truncate( name.length() - 1 );
373 QByteArray
prefix = QByteArray(buffer + 0x159, 155);
374 if (prefix[0] !=
'\0') {
375 name = (QString::fromLatin1(prefix.constData()) + QLatin1Char(
'/') + name);
378 int pos = name.lastIndexOf( QLatin1Char(
'/') );
379 QString nm = ( pos == -1 ) ? name : name.mid( pos + 1 );
384 const char* p = buffer + 0x64;
385 while( *p ==
' ' ) ++p;
386 int access = (int)strtol( p, &dummy, 8 );
389 QString user = QString::fromLocal8Bit( buffer + 0x109 );
390 QString group = QString::fromLocal8Bit( buffer + 0x129 );
395 while( *p ==
' ' ) ++p;
396 int time = (int)strtol( p, &dummy, 8 );
399 char typeflag = buffer[ 0x9c ];
404 if ( typeflag ==
'g' )
405 isGlobalHeader =
true;
407 if ( typeflag ==
'5' )
410 bool isDumpDir =
false;
411 if ( typeflag ==
'D' )
430 QByteArray sizeBuffer( buffer + 0x7c, 12 );
431 qint64 size = sizeBuffer.trimmed().toLongLong( 0, 8 );
444 if ( typeflag ==
'1' )
446 kDebug(7041) <<
"Hard link, setting size to 0 instead of" << size;
451 e =
new KArchiveFile(
this, nm, access, time, user, group, symlink,
456 qint64 rest = size % 0x200;
457 qint64 skip = size + (rest ? 0x200 - rest : 0);
459 if (! dev->seek( dev->pos() + skip ) )
460 kWarning(7041) <<
"skipping" << skip <<
"failed";
468 if (nm == QLatin1String(
".")) {
471 setRootDir( static_cast<KArchiveDirectory *>( e ) );
479 QString path = QDir::cleanPath( name.left( pos ) );
488 d->tarEnd = dev->pos() - n;
500 bool KTar::KTarPrivate::writeBackTempFile(
const QString & fileName )
518 QFile* file = tmpFile;
519 if ( !dev->open(QIODevice::WriteOnly) )
526 static_cast<KFilterDev *
>(dev)->setOrigFileName( origFileName );
529 buffer.resize(8*1024);
531 while ( !file->atEnd()) {
532 len = file->read(buffer.data(), buffer.size());
533 dev->write(buffer.data(),len);
552 if (d->tmpFile && (
mode() & QIODevice::WriteOnly)) {
553 ok = d->writeBackTempFile(
fileName() );
564 int rest = size % 0x200;
565 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
566 d->tarEnd =
device()->pos() + (rest ? 0x200 - rest : 0);
569 char buffer[ 0x201 ];
570 for( uint i = 0; i < 0x200; ++i )
572 qint64 nwritten =
device()->write( buffer, 0x200 - rest );
573 return nwritten == 0x200 - rest;
601 void KTar::KTarPrivate::fillBuffer(
char * buffer,
602 const char * mode,
qint64 size, time_t mtime,
char typeflag,
603 const char * uname,
const char * gname ) {
605 assert( strlen(mode) == 6 );
606 memcpy( buffer+0x64, mode, 6 );
607 buffer[ 0x6a ] =
' ';
608 buffer[ 0x6b ] =
'\0';
611 strcpy( buffer + 0x6c,
" 765 ");
613 strcpy( buffer + 0x74,
" 144 ");
616 QByteArray s = QByteArray::number( size, 8 );
617 s = s.rightJustified( 11,
'0' );
618 memcpy( buffer + 0x7c, s.data(), 11 );
619 buffer[ 0x87 ] =
' ';
622 s = QByteArray::number( static_cast<qulonglong>(mtime), 8 );
623 s = s.rightJustified( 11,
'0' );
624 memcpy( buffer + 0x88, s.data(), 11 );
625 buffer[ 0x93 ] =
' ';
628 buffer[ 0x94 ] = 0x20;
629 buffer[ 0x95 ] = 0x20;
630 buffer[ 0x96 ] = 0x20;
631 buffer[ 0x97 ] = 0x20;
632 buffer[ 0x98 ] = 0x20;
633 buffer[ 0x99 ] = 0x20;
640 buffer[ 0x9a ] =
'\0';
641 buffer[ 0x9b ] =
' ';
644 buffer[ 0x9c ] = typeflag;
647 strcpy( buffer + 0x101,
"ustar");
648 strcpy( buffer + 0x107,
"00" );
651 strcpy( buffer + 0x109, uname );
653 strcpy( buffer + 0x129, gname );
657 for( uint j = 0; j < 0x200; ++j )
659 s = QByteArray::number( check, 8 );
660 s = s.rightJustified( 6,
'0' );
661 memcpy( buffer + 0x94, s.constData(), 6 );
664 void KTar::KTarPrivate::writeLonglink(
char *buffer,
const QByteArray &name,
char typeflag,
665 const char *uname,
const char *gname) {
666 strcpy( buffer,
"././@LongLink" );
667 qint64 namelen = name.length() + 1;
668 fillBuffer( buffer,
" 0", namelen, 0, typeflag, uname, gname );
669 q->device()->write( buffer, 0x200 );
671 while (namelen > 0) {
672 int chunksize = qMin(namelen, 0x200LL);
673 memcpy(buffer, name.data()+offset, chunksize);
675 q->device()->write( buffer, 0x200 );
677 namelen -= chunksize;
684 time_t , time_t mtime, time_t ) {
687 kWarning(7041) <<
"You must open the tar file before writing to it\n";
691 if ( !(
mode() & QIODevice::WriteOnly) )
693 kWarning(7041) <<
"You must open the tar file for writing\n";
719 char buffer[ 0x201 ];
720 memset( buffer, 0, 0x200 );
721 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
722 device()->seek(d->tarEnd);
725 const QByteArray encodedFileName = QFile::encodeName(fileName);
726 const QByteArray uname = user.toLocal8Bit();
727 const QByteArray gname = group.toLocal8Bit();
730 if ( fileName.length() > 99 )
731 d->writeLonglink(buffer,encodedFileName,
'L',uname,gname);
734 strncpy( buffer, encodedFileName, 99 );
737 memset(buffer+0x9d, 0, 0x200 - 0x9d);
739 QByteArray permstr = QByteArray::number( (
unsigned int)perm, 8 );
740 permstr = permstr.rightJustified(6,
'0');
741 d->fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
744 return device()->write( buffer, 0x200 ) == 0x200;
749 time_t , time_t mtime, time_t ) {
752 kWarning(7041) <<
"You must open the tar file before writing to it\n";
756 if ( !(
mode() & QIODevice::WriteOnly) )
758 kWarning(7041) <<
"You must open the tar file for writing\n";
763 QString dirName ( QDir::cleanPath( name ) );
766 if ( !dirName.endsWith( QLatin1Char(
'/' ) ) )
767 dirName += QLatin1Char(
'/' );
769 if ( d->dirList.contains( dirName ) )
772 char buffer[ 0x201 ];
773 memset( buffer, 0, 0x200 );
774 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
775 device()->seek(d->tarEnd);
778 QByteArray encodedDirname = QFile::encodeName(dirName);
779 QByteArray uname = user.toLocal8Bit();
780 QByteArray gname = group.toLocal8Bit();
783 if ( dirName.length() > 99 )
784 d->writeLonglink(buffer,encodedDirname,
'L',uname,gname);
787 strncpy( buffer, encodedDirname, 99 );
790 memset(buffer+0x9d, 0, 0x200 - 0x9d);
792 QByteArray permstr = QByteArray::number( (
unsigned int)perm, 8 );
793 permstr = permstr.rightJustified(6,
' ');
794 d->fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
797 device()->write( buffer, 0x200 );
798 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
799 d->tarEnd =
device()->pos();
801 d->dirList.append( dirName );
807 mode_t perm, time_t , time_t mtime, time_t ) {
810 kWarning(7041) <<
"You must open the tar file before writing to it\n";
814 if ( !(
mode() & QIODevice::WriteOnly) )
816 kWarning(7041) <<
"You must open the tar file for writing\n";
823 char buffer[ 0x201 ];
824 memset( buffer, 0, 0x200 );
825 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
826 device()->seek(d->tarEnd);
829 QByteArray encodedFileName = QFile::encodeName(fileName);
830 QByteArray encodedTarget = QFile::encodeName(target);
831 QByteArray uname = user.toLocal8Bit();
832 QByteArray gname = group.toLocal8Bit();
835 if (target.length() > 99)
836 d->writeLonglink(buffer,encodedTarget,
'K',uname,gname);
837 if ( fileName.length() > 99 )
838 d->writeLonglink(buffer,encodedFileName,
'L',uname,gname);
841 strncpy( buffer, encodedFileName, 99 );
844 strncpy(buffer+0x9d, encodedTarget, 99);
847 memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
849 QByteArray permstr = QByteArray::number( (
unsigned int)perm, 8 );
850 permstr = permstr.rightJustified(6,
' ');
851 d->fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
854 bool retval =
device()->write( buffer, 0x200 ) == 0x200;
855 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
856 d->tarEnd =
device()->pos();