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;
366 if ( name.endsWith( QLatin1Char(
'/' ) ) )
369 name.truncate( name.length() - 1 );
372 QByteArray
prefix = QByteArray(buffer + 0x159, 155);
373 if (prefix[0] !=
'\0') {
374 name = (QString::fromLatin1(prefix.constData()) + QLatin1Char(
'/') + name);
377 int pos = name.lastIndexOf( QLatin1Char(
'/') );
378 QString nm = ( pos == -1 ) ? name : name.mid( pos + 1 );
383 const char* p = buffer + 0x64;
384 while( *p ==
' ' ) ++p;
385 int access = (int)strtol( p, &dummy, 8 );
388 QString user = QString::fromLocal8Bit( buffer + 0x109 );
389 QString group = QString::fromLocal8Bit( buffer + 0x129 );
394 while( *p ==
' ' ) ++p;
395 int time = (int)strtol( p, &dummy, 8 );
398 char typeflag = buffer[ 0x9c ];
404 if ( typeflag ==
'5' )
407 bool isDumpDir =
false;
408 if ( typeflag ==
'D' )
415 if (typeflag ==
'x' || typeflag ==
'g') {
417 (void)dev->read( buffer, 0x200 );
433 QByteArray sizeBuffer( buffer + 0x7c, 12 );
434 qint64 size = sizeBuffer.trimmed().toLongLong( 0, 8 );
447 if ( typeflag ==
'1' )
449 kDebug(7041) <<
"Hard link, setting size to 0 instead of" << size;
454 e =
new KArchiveFile(
this, nm, access, time, user, group, symlink,
459 qint64 rest = size % 0x200;
460 qint64 skip = size + (rest ? 0x200 - rest : 0);
462 if (! dev->seek( dev->pos() + skip ) )
463 kWarning(7041) <<
"skipping" << skip <<
"failed";
468 if (nm == QLatin1String(
".")) {
471 setRootDir( static_cast<KArchiveDirectory *>( e ) );
480 QString path = QDir::cleanPath( name.left( pos ) );
489 d->tarEnd = dev->pos() - n;
501 bool KTar::KTarPrivate::writeBackTempFile(
const QString & fileName )
519 QFile* file = tmpFile;
520 if ( !dev->open(QIODevice::WriteOnly) )
527 static_cast<KFilterDev *
>(dev)->setOrigFileName( origFileName );
530 buffer.resize(8*1024);
532 while ( !file->atEnd()) {
533 len = file->read(buffer.data(), buffer.size());
534 dev->write(buffer.data(),len);
553 if (d->tmpFile && (
mode() & QIODevice::WriteOnly)) {
554 ok = d->writeBackTempFile(
fileName() );
565 int rest = size % 0x200;
566 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
567 d->tarEnd =
device()->pos() + (rest ? 0x200 - rest : 0);
570 char buffer[ 0x201 ];
571 for( uint i = 0; i < 0x200; ++i )
573 qint64 nwritten =
device()->write( buffer, 0x200 - rest );
574 return nwritten == 0x200 - rest;
602 void KTar::KTarPrivate::fillBuffer(
char * buffer,
603 const char * mode,
qint64 size, time_t mtime,
char typeflag,
604 const char * uname,
const char * gname ) {
606 assert( strlen(mode) == 6 );
607 memcpy( buffer+0x64, mode, 6 );
608 buffer[ 0x6a ] =
' ';
609 buffer[ 0x6b ] =
'\0';
612 strcpy( buffer + 0x6c,
" 765 ");
614 strcpy( buffer + 0x74,
" 144 ");
617 QByteArray s = QByteArray::number( size, 8 );
618 s = s.rightJustified( 11,
'0' );
619 memcpy( buffer + 0x7c, s.data(), 11 );
620 buffer[ 0x87 ] =
' ';
623 s = QByteArray::number( static_cast<qulonglong>(mtime), 8 );
624 s = s.rightJustified( 11,
'0' );
625 memcpy( buffer + 0x88, s.data(), 11 );
626 buffer[ 0x93 ] =
' ';
629 buffer[ 0x94 ] = 0x20;
630 buffer[ 0x95 ] = 0x20;
631 buffer[ 0x96 ] = 0x20;
632 buffer[ 0x97 ] = 0x20;
633 buffer[ 0x98 ] = 0x20;
634 buffer[ 0x99 ] = 0x20;
641 buffer[ 0x9a ] =
'\0';
642 buffer[ 0x9b ] =
' ';
645 buffer[ 0x9c ] = typeflag;
648 strcpy( buffer + 0x101,
"ustar");
649 strcpy( buffer + 0x107,
"00" );
652 strcpy( buffer + 0x109, uname );
654 strcpy( buffer + 0x129, gname );
658 for( uint j = 0; j < 0x200; ++j )
660 s = QByteArray::number( check, 8 );
661 s = s.rightJustified( 6,
'0' );
662 memcpy( buffer + 0x94, s.constData(), 6 );
665 void KTar::KTarPrivate::writeLonglink(
char *buffer,
const QByteArray &name,
char typeflag,
666 const char *uname,
const char *gname) {
667 strcpy( buffer,
"././@LongLink" );
668 qint64 namelen = name.length() + 1;
669 fillBuffer( buffer,
" 0", namelen, 0, typeflag, uname, gname );
670 q->device()->write( buffer, 0x200 );
672 while (namelen > 0) {
673 int chunksize = qMin(namelen, 0x200LL);
674 memcpy(buffer, name.data()+offset, chunksize);
676 q->device()->write( buffer, 0x200 );
678 namelen -= chunksize;
685 time_t , time_t mtime, time_t ) {
688 kWarning(7041) <<
"You must open the tar file before writing to it\n";
692 if ( !(
mode() & QIODevice::WriteOnly) )
694 kWarning(7041) <<
"You must open the tar file for writing\n";
720 char buffer[ 0x201 ];
721 memset( buffer, 0, 0x200 );
722 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
723 device()->seek(d->tarEnd);
726 const QByteArray encodedFileName = QFile::encodeName(fileName);
727 const QByteArray uname = user.toLocal8Bit();
728 const QByteArray gname = group.toLocal8Bit();
731 if ( fileName.length() > 99 )
732 d->writeLonglink(buffer,encodedFileName,
'L',uname,gname);
735 strncpy( buffer, encodedFileName, 99 );
738 memset(buffer+0x9d, 0, 0x200 - 0x9d);
740 QByteArray permstr = QByteArray::number( (
unsigned int)perm, 8 );
741 permstr = permstr.rightJustified(6,
'0');
742 d->fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
745 return device()->write( buffer, 0x200 ) == 0x200;
750 time_t , time_t mtime, time_t ) {
753 kWarning(7041) <<
"You must open the tar file before writing to it\n";
757 if ( !(
mode() & QIODevice::WriteOnly) )
759 kWarning(7041) <<
"You must open the tar file for writing\n";
764 QString dirName ( QDir::cleanPath( name ) );
767 if ( !dirName.endsWith( QLatin1Char(
'/' ) ) )
768 dirName += QLatin1Char(
'/' );
770 if ( d->dirList.contains( dirName ) )
773 char buffer[ 0x201 ];
774 memset( buffer, 0, 0x200 );
775 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
776 device()->seek(d->tarEnd);
779 QByteArray encodedDirname = QFile::encodeName(dirName);
780 QByteArray uname = user.toLocal8Bit();
781 QByteArray gname = group.toLocal8Bit();
784 if ( dirName.length() > 99 )
785 d->writeLonglink(buffer,encodedDirname,
'L',uname,gname);
788 strncpy( buffer, encodedDirname, 99 );
791 memset(buffer+0x9d, 0, 0x200 - 0x9d);
793 QByteArray permstr = QByteArray::number( (
unsigned int)perm, 8 );
794 permstr = permstr.rightJustified(6,
' ');
795 d->fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
798 device()->write( buffer, 0x200 );
799 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
800 d->tarEnd =
device()->pos();
802 d->dirList.append( dirName );
808 mode_t perm, time_t , time_t mtime, time_t ) {
811 kWarning(7041) <<
"You must open the tar file before writing to it\n";
815 if ( !(
mode() & QIODevice::WriteOnly) )
817 kWarning(7041) <<
"You must open the tar file for writing\n";
824 char buffer[ 0x201 ];
825 memset( buffer, 0, 0x200 );
826 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
827 device()->seek(d->tarEnd);
830 QByteArray encodedFileName = QFile::encodeName(fileName);
831 QByteArray encodedTarget = QFile::encodeName(target);
832 QByteArray uname = user.toLocal8Bit();
833 QByteArray gname = group.toLocal8Bit();
836 if (target.length() > 99)
837 d->writeLonglink(buffer,encodedTarget,
'K',uname,gname);
838 if ( fileName.length() > 99 )
839 d->writeLonglink(buffer,encodedFileName,
'L',uname,gname);
842 strncpy( buffer, encodedFileName, 99 );
845 strncpy(buffer+0x9d, encodedTarget, 99);
848 memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
850 QByteArray permstr = QByteArray::number( (
unsigned int)perm, 8 );
851 permstr = permstr.rightJustified(6,
' ');
852 d->fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
855 bool retval =
device()->write( buffer, 0x200 ) == 0x200;
856 if ( (
mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
857 d->tarEnd =
device()->pos();
static QIODevice * deviceForFile(const QString &fileName, const QString &mimetype=QString(), bool forceFilter=false)
Reimplemented to return true.
static const char application_xz[]
KTar(const QString &filename, const QString &mimetype=QString())
Creates an instance that operates on the given filename using the compression filter associated to gi...
virtual void virtual_hook(int id, void *data)
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
QIODevice * device() const
The underlying device.
virtual bool createDevice(QIODevice::OpenMode mode)
Can be reimplemented in order to change the creation of the device (when using the fileName construct...
KArchive is a base class for reading and writing archives.
static Ptr findByPath(const QString &path, mode_t mode=0, bool fast_mode=false, int *accuracy=0)
Finds a KMimeType with the given url.
virtual ~KTar()
If the tar ball is still opened, then it will be closed automatically by the destructor.
A QTemporaryFile that will save in the KDE temp directory.
static KMimeType::Ptr defaultMimeTypePtr()
Returns the default mimetype.
virtual bool close()
Closes the archive.
QIODevice::OpenMode mode() const
Returns the mode in which the archive was opened.
virtual bool createDevice(QIODevice::OpenMode mode)
Can be reimplemented in order to change the creation of the device (when using the fileName construct...
virtual bool doWriteDir(const QString &name, const QString &user, const QString &group, mode_t perm, time_t atime, time_t mtime, time_t ctime)
Reimplemented from KArchive.
QString fileName() const
The name of the archive file, as passed to the constructor that takes a fileName, or an empty string ...
A class for reading and writing compressed data onto a device (e.g.
A class for reading / writing (optionally compressed) tar archives.
static const char application_gzip[]
A base class for entries in an KArchive.
static const char application_zip[]
static const char application_bzip[]
virtual bool closeArchive()
Closes the archive.
void setOrigFileName(const QByteArray &fileName)
Special function for setting the "original file name" in the gzip header, when writing a tar...
bool isOpen() const
Checks whether the archive is open.
void addEntry(KArchiveEntry *)
static QIODevice * device(QIODevice *inDevice, const QString &mimetype, bool autoDeleteInDevice=true)
Creates an i/o device that is able to read from the QIODevice inDevice, whether the data is compresse...
KArchiveDirectory * findOrCreate(const QString &path)
Ensures that path exists, create otherwise.
Represents a directory entry in a KArchive.
virtual bool openArchive(QIODevice::OpenMode mode)
Opens the archive for reading.
virtual bool seek(qint64)
That one can be quite slow, when going back.
static const char application_lzma[]
int access(const QString &path, int mode)
Represents a file entry in a KArchive.
void setRootDir(KArchiveDirectory *rootDir)
Derived classes call setRootDir from openArchive, to set the root directory after parsing an existing...
virtual void virtual_hook(int id, void *data)
virtual bool doFinishWriting(qint64 size)
Reimplemented from KArchive.
void setDevice(QIODevice *dev)
Can be called by derived classes in order to set the underlying device.
static Ptr findByFileContent(const QString &fileName, int *accuracy=0)
Tries to find out the MIME type of a file by looking for certain magic numbers and characteristic str...
virtual bool doWriteSymLink(const QString &name, const QString &target, const QString &user, const QString &group, mode_t perm, time_t atime, time_t mtime, time_t ctime)
Reimplemented from KArchive.
virtual bool doPrepareWriting(const QString &name, const QString &user, const QString &group, qint64 size, mode_t perm, time_t atime, time_t mtime, time_t ctime)
Reimplemented from KArchive.
bool is(const QString &mimeTypeName) const
Do not use name()=="somename" anymore, to check for a given mimetype.