• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDECore

  • kdecore
  • io
ktar.cpp
Go to the documentation of this file.
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>
4
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.
8
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.
13
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.
18*/
19
20#include "ktar.h"
21
22#include <stdlib.h> // strtol
23#include <time.h> // time()
24#include <assert.h>
25
26#include <QtCore/QDir>
27#include <QtCore/QFile>
28#include <kdebug.h>
29#include <kmimetype.h>
30#include <ktemporaryfile.h>
31
32#include <kfilterdev.h>
33#include <kfilterbase.h>
34
38
39// Mime types of known filters
40static const char application_gzip[] = "application/x-gzip";
41static const char application_bzip[] = "application/x-bzip";
42static const char application_lzma[] = "application/x-lzma";
43static const char application_xz[] = "application/x-xz";
44static const char application_zip[] = "application/zip";
45
46class KTar::KTarPrivate
47{
48public:
49 KTarPrivate(KTar *parent)
50 : q(parent),
51 tarEnd( 0 ),
52 tmpFile( 0 )
53 {
54 }
55
56 KTar *q;
57 QStringList dirList;
58 qint64 tarEnd;
59 KTemporaryFile* tmpFile;
60 QString mimetype;
61 QByteArray origFileName;
62
63 bool fillTempFile(const QString & fileName);
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);
71 qint64 readHeader(char *buffer, QString &name, QString &symlink);
72};
73
74KTar::KTar( const QString& fileName, const QString & _mimetype )
75 : KArchive( fileName ), d(new KTarPrivate(this))
76{
77 d->mimetype = _mimetype;
78}
79
80KTar::KTar( QIODevice * dev )
81 : KArchive( dev ), d(new KTarPrivate(this))
82{
83 Q_ASSERT( dev );
84}
85
86// Only called when a filename was given
87bool KTar::createDevice(QIODevice::OpenMode mode)
88{
89 if (d->mimetype.isEmpty()) {
90 // Find out mimetype manually
91
92 KMimeType::Ptr mime;
93 if (mode != QIODevice::WriteOnly && QFile::exists(fileName())) {
94 // Give priority to file contents: if someone renames a .tar.bz2 to .tar.gz,
95 // we can still do the right thing here.
96 mime = KMimeType::findByFileContent(fileName());
97 if (mime == KMimeType::defaultMimeTypePtr()) {
98 // Unable to determine mimetype from contents, get it from file name
99 mime = KMimeType::findByPath(fileName(), 0, true);
100 }
101 } else {
102 mime = KMimeType::findByPath(fileName(), 0, true);
103 }
104
105 //kDebug(7041) << mode << mime->name();
106
107 if (mime->is(QString::fromLatin1("application/x-compressed-tar")) || mime->is(QString::fromLatin1(application_gzip))) {
108 // gzipped tar file (with possibly invalid file name), ask for gzip filter
109 d->mimetype = QString::fromLatin1(application_gzip);
110 } else if (mime->is(QString::fromLatin1("application/x-bzip-compressed-tar")) || mime->is(QString::fromLatin1(application_bzip))) {
111 // bzipped2 tar file (with possibly invalid file name), ask for bz2 filter
112 d->mimetype = QString::fromLatin1(application_bzip);
113 } else if (mime->is(QString::fromLatin1("application/x-lzma-compressed-tar")) || mime->is(QString::fromLatin1(application_lzma))) {
114 // lzma compressed tar file (with possibly invalid file name), ask for xz filter
115 d->mimetype = QString::fromLatin1(application_lzma);
116 } else if (mime->is(QString::fromLatin1("application/x-xz-compressed-tar")) || mime->is(QString::fromLatin1(application_xz))) {
117 // xz compressed tar file (with possibly invalid name), ask for xz filter
118 d->mimetype = QString::fromLatin1(application_xz);
119 }
120 }
121
122 if (d->mimetype == QLatin1String("application/x-tar")) {
123 return KArchive::createDevice(mode);
124 } else if (mode == QIODevice::WriteOnly) {
125 if (!KArchive::createDevice(mode))
126 return false;
127 if (!d->mimetype.isEmpty()) {
128 // Create a compression filter on top of the KSaveFile device that KArchive created.
129 //kDebug(7041) << "creating KFilterDev for" << d->mimetype;
130 QIODevice *filterDev = KFilterDev::device(device(), d->mimetype);
131 Q_ASSERT(filterDev);
132 setDevice(filterDev);
133 }
134 return true;
135 } else {
136 // The compression filters are very slow with random access.
137 // So instead of applying the filter to the device,
138 // the file is completely extracted instead,
139 // and we work on the extracted tar file.
140 // This improves the extraction speed by the tar ioslave dramatically,
141 // if the archive file contains many files.
142 // This is because the tar ioslave extracts one file after the other and normally
143 // has to walk through the decompression filter each time.
144 // Which is in fact nearly as slow as a complete decompression for each file.
145
146 Q_ASSERT(!d->tmpFile);
147 d->tmpFile = new KTemporaryFile();
148 d->tmpFile->setPrefix(QLatin1String("ktar-"));
149 d->tmpFile->setSuffix(QLatin1String(".tar"));
150 d->tmpFile->open();
151 //kDebug(7041) << "creating tempfile:" << d->tmpFile->fileName();
152
153 setDevice(d->tmpFile);
154 return true;
155 }
156}
157
158KTar::~KTar()
159{
160 // mjarrett: Closes to prevent ~KArchive from aborting w/o device
161 if( isOpen() )
162 close();
163
164 delete d->tmpFile;
165 delete d;
166}
167
168void KTar::setOrigFileName( const QByteArray & fileName ) {
169 if ( !isOpen() || !(mode() & QIODevice::WriteOnly) )
170 {
171 kWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n";
172 return;
173 }
174 d->origFileName = fileName;
175}
176
177qint64 KTar::KTarPrivate::readRawHeader( char *buffer ) {
178 // Read header
179 qint64 n = q->device()->read( buffer, 0x200 );
180 // we need to test if there is a prefix value because the file name can be null
181 // and the prefix can have a value and in this case we don't reset n.
182 if ( n == 0x200 && (buffer[0] != 0 || buffer[0x159] != 0) ) {
183 // Make sure this is actually a tar header
184 if (strncmp(buffer + 257, "ustar", 5)) {
185 // The magic isn't there (broken/old tars), but maybe a correct checksum?
186
187 int check = 0;
188 for( uint j = 0; j < 0x200; ++j )
189 check += buffer[j];
190
191 // adjust checksum to count the checksum fields as blanks
192 for( uint j = 0; j < 8 /*size of the checksum field including the \0 and the space*/; j++ )
193 check -= buffer[148 + j];
194 check += 8 * ' ';
195
196 QByteArray s = QByteArray::number( check, 8 ); // octal
197
198 // only compare those of the 6 checksum digits that mean something,
199 // because the other digits are filled with all sorts of different chars by different tars ...
200 // Some tars right-justify the checksum so it could start in one of three places - we have to check each.
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() );
207 return -1;
208 }
209 }/*end if*/
210 } else {
211 // reset to 0 if 0x200 because logical end of archive has been reached
212 if (n == 0x200) n = 0;
213 }/*end if*/
214 return n;
215}
216
217bool KTar::KTarPrivate::readLonglink(char *buffer,QByteArray &longlink) {
218 qint64 n = 0;
219 //kDebug() << "reading longlink from pos " << q->device()->pos();
220 QIODevice *dev = q->device();
221 // read size of longlink from size field in header
222 // size is in bytes including the trailing null (which we ignore)
223 qint64 size = QByteArray( buffer + 0x7c, 12 ).trimmed().toLongLong( 0, 8 /*octal*/ );
224
225 size--; // ignore trailing null
226 longlink.resize(size);
227 qint64 offset = 0;
228 while (size > 0) {
229 int chunksize = qMin(size, 0x200LL);
230 n = dev->read( longlink.data() + offset, chunksize );
231 if (n == -1) return false;
232 size -= chunksize;
233 offset += 0x200;
234 }/*wend*/
235 // jump over the rest
236 const int skip = 0x200 - (n % 0x200);
237 if (skip <= 0x200) {
238 if (dev->read(buffer,skip) != skip)
239 return false;
240 }
241 return true;
242}
243
244qint64 KTar::KTarPrivate::readHeader( char *buffer, QString &name, QString &symlink ) {
245 name.truncate(0);
246 symlink.truncate(0);
247 while (true) {
248 qint64 n = readRawHeader(buffer);
249 if (n != 0x200) return n;
250
251 // is it a longlink?
252 if (strcmp(buffer,"././@LongLink") == 0) {
253 char typeflag = buffer[0x9c];
254 QByteArray longlink;
255 readLonglink(buffer,longlink);
256 switch (typeflag) {
257 case 'L': name = QFile::decodeName(longlink); break;
258 case 'K': symlink = QFile::decodeName(longlink); break;
259 }/*end switch*/
260 } else {
261 break;
262 }/*end if*/
263 }/*wend*/
264
265 // if not result of longlink, read names directly from the header
266 if (name.isEmpty())
267 // there are names that are exactly 100 bytes long
268 // and neither longlink nor \0 terminated (bug:101472)
269 name = QFile::decodeName(QByteArray(buffer, 100));
270 if (symlink.isEmpty())
271 symlink = QFile::decodeName(QByteArray(buffer + 0x9d /*?*/, 100));
272
273 return 0x200;
274}
275
276/*
277 * If we have created a temporary file, we have
278 * to decompress the original file now and write
279 * the contents to the temporary file.
280 */
281bool KTar::KTarPrivate::fillTempFile( const QString & fileName) {
282 if ( ! tmpFile )
283 return true;
284
285 //kDebug(7041) << "filling tmpFile of mimetype" << mimetype;
286
287 bool forced = false;
288 if ( QLatin1String(application_gzip) == mimetype || QLatin1String(application_bzip) == mimetype )
289 forced = true;
290
291 QIODevice *filterDev = KFilterDev::deviceForFile( fileName, mimetype, forced );
292
293 if( filterDev ) {
294 QFile* file = tmpFile;
295 Q_ASSERT(file->isOpen());
296 Q_ASSERT(file->openMode() & QIODevice::WriteOnly);
297 file->seek(0);
298 QByteArray buffer;
299 buffer.resize(8*1024);
300 if ( ! filterDev->open( QIODevice::ReadOnly ) )
301 {
302 delete filterDev;
303 return false;
304 }
305 qint64 len = -1;
306 while ( !filterDev->atEnd() && len != 0 ) {
307 len = filterDev->read(buffer.data(),buffer.size());
308 if ( len < 0 ) { // corrupted archive
309 delete filterDev;
310 return false;
311 }
312 if ( file->write(buffer.data(), len) != len ) { // disk full
313 delete filterDev;
314 return false;
315 }
316 }
317 filterDev->close();
318 delete filterDev;
319
320 file->flush();
321 file->seek(0);
322 Q_ASSERT(file->isOpen());
323 Q_ASSERT(file->openMode() & QIODevice::ReadOnly);
324 } else {
325 kDebug(7041) << "no filterdevice found!";
326 }
327
328 //kDebug( 7041 ) << "filling tmpFile finished.";
329 return true;
330}
331
332bool KTar::openArchive( QIODevice::OpenMode mode ) {
333
334 if ( !(mode & QIODevice::ReadOnly) )
335 return true;
336
337 if ( !d->fillTempFile( fileName() ) )
338 return false;
339
340 // We'll use the permission and user/group of d->rootDir
341 // for any directory we emulate (see findOrCreate)
342 //struct stat buf;
343 //stat( fileName(), &buf );
344
345 d->dirList.clear();
346 QIODevice* dev = device();
347
348 if ( !dev )
349 return false;
350
351 // read dir information
352 char buffer[ 0x200 ];
353 bool ende = false;
354 do
355 {
356 QString name;
357 QString symlink;
358
359 // Read header
360 qint64 n = d->readHeader( buffer, name, symlink );
361 if (n < 0) return false;
362 if (n == 0x200)
363 {
364 bool isdir = false;
365
366 if ( name.endsWith( QLatin1Char( '/' ) ) )
367 {
368 isdir = true;
369 name.truncate( name.length() - 1 );
370 }
371
372 QByteArray prefix = QByteArray(buffer + 0x159, 155);
373 if (prefix[0] != '\0') {
374 name = (QString::fromLatin1(prefix.constData()) + QLatin1Char('/') + name);
375 }
376
377 int pos = name.lastIndexOf( QLatin1Char('/') );
378 QString nm = ( pos == -1 ) ? name : name.mid( pos + 1 );
379
380 // read access
381 buffer[ 0x6b ] = 0;
382 char *dummy;
383 const char* p = buffer + 0x64;
384 while( *p == ' ' ) ++p;
385 int access = (int)strtol( p, &dummy, 8 );
386
387 // read user and group
388 QString user = QString::fromLocal8Bit( buffer + 0x109 );
389 QString group = QString::fromLocal8Bit( buffer + 0x129 );
390
391 // read time
392 buffer[ 0x93 ] = 0;
393 p = buffer + 0x88;
394 while( *p == ' ' ) ++p;
395 int time = (int)strtol( p, &dummy, 8 );
396
397 // read type flag
398 char typeflag = buffer[ 0x9c ];
399 // '0' for files, '1' hard link, '2' symlink, '5' for directory
400 // (and 'L' for longlink fileNames, 'K' for longlink symlink targets)
401 // 'D' for GNU tar extension DUMPDIR, 'x' for Extended header referring
402 // to the next file in the archive and 'g' for Global extended header
403
404 if ( typeflag == '5' )
405 isdir = true;
406
407 bool isDumpDir = false;
408 if ( typeflag == 'D' )
409 {
410 isdir = false;
411 isDumpDir = true;
412 }
413 //kDebug(7041) << nm << "isdir=" << isdir << "pos=" << dev->pos() << "typeflag=" << typeflag << " islink=" << ( typeflag == '1' || typeflag == '2' );
414
415 if (typeflag == 'x' || typeflag == 'g') { // pax extended header, or pax global extended header
416 // Skip it for now. TODO: implement reading of extended header, as per http://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html
417 (void)dev->read( buffer, 0x200 );
418 continue;
419 }
420
421 if (isdir)
422 access |= S_IFDIR; // f*cking broken tar files
423
424 KArchiveEntry* e;
425 if ( isdir )
426 {
427 //kDebug(7041) << "directory" << nm;
428 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
429 }
430 else
431 {
432 // read size
433 QByteArray sizeBuffer( buffer + 0x7c, 12 );
434 qint64 size = sizeBuffer.trimmed().toLongLong( 0, 8 /*octal*/ );
435 //kDebug(7041) << "sizeBuffer='" << sizeBuffer << "' -> size=" << size;
436
437 // for isDumpDir we will skip the additional info about that dirs contents
438 if ( isDumpDir )
439 {
440 //kDebug(7041) << nm << "isDumpDir";
441 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
442 }
443 else
444 {
445
446 // Let's hack around hard links. Our classes don't support that, so make them symlinks
447 if ( typeflag == '1' )
448 {
449 kDebug(7041) << "Hard link, setting size to 0 instead of" << size;
450 size = 0; // no contents
451 }
452
453 //kDebug(7041) << "file" << nm << "size=" << size;
454 e = new KArchiveFile( this, nm, access, time, user, group, symlink,
455 dev->pos(), size );
456 }
457
458 // Skip contents + align bytes
459 qint64 rest = size % 0x200;
460 qint64 skip = size + (rest ? 0x200 - rest : 0);
461 //kDebug(7041) << "pos()=" << dev->pos() << "rest=" << rest << "skipping" << skip;
462 if (! dev->seek( dev->pos() + skip ) )
463 kWarning(7041) << "skipping" << skip << "failed";
464 }
465
466 if ( pos == -1 )
467 {
468 if (nm == QLatin1String(".")) { // special case
469 Q_ASSERT( isdir );
470 if (isdir) {
471 setRootDir( static_cast<KArchiveDirectory *>( e ) );
472 }
473 } else {
474 rootDir()->addEntry( e );
475 }
476 }
477 else
478 {
479 // In some tar files we can find dir/./file => call cleanPath
480 QString path = QDir::cleanPath( name.left( pos ) );
481 // Ensure container directory exists, create otherwise
482 KArchiveDirectory * d = findOrCreate( path );
483 d->addEntry( e );
484 }
485 }
486 else
487 {
488 //qDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]);
489 d->tarEnd = dev->pos() - n; // Remember end of archive
490 ende = true;
491 }
492 } while( !ende );
493 return true;
494}
495
496/*
497 * Writes back the changes of the temporary file
498 * to the original file.
499 * Must only be called if in write mode, not in read mode
500 */
501bool KTar::KTarPrivate::writeBackTempFile( const QString & fileName )
502{
503 if ( !tmpFile )
504 return true;
505
506 //kDebug(7041) << "Write temporary file to compressed file" << fileName << mimetype;
507
508 bool forced = false;
509 if (QLatin1String(application_gzip) == mimetype || QLatin1String(application_bzip) == mimetype ||
510 QLatin1String(application_lzma) == mimetype || QLatin1String(application_xz) == mimetype)
511 forced = true;
512
513 // #### TODO this should use KSaveFile to avoid problems on disk full
514 // (KArchive uses KSaveFile by default, but the temp-uncompressed-file trick
515 // circumvents that).
516
517 QIODevice *dev = KFilterDev::deviceForFile( fileName, mimetype, forced );
518 if( dev ) {
519 QFile* file = tmpFile;
520 if ( !dev->open(QIODevice::WriteOnly) )
521 {
522 file->close();
523 delete dev;
524 return false;
525 }
526 if ( forced )
527 static_cast<KFilterDev *>(dev)->setOrigFileName( origFileName );
528 file->seek(0);
529 QByteArray buffer;
530 buffer.resize(8*1024);
531 qint64 len;
532 while ( !file->atEnd()) {
533 len = file->read(buffer.data(), buffer.size());
534 dev->write(buffer.data(),len); // TODO error checking
535 }
536 file->close();
537 dev->close();
538 delete dev;
539 }
540
541 //kDebug(7041) << "Write temporary file to compressed file done.";
542 return true;
543}
544
545bool KTar::closeArchive() {
546 d->dirList.clear();
547
548 bool ok = true;
549
550 // If we are in readwrite mode and had created
551 // a temporary tar file, we have to write
552 // back the changes to the original file
553 if (d->tmpFile && (mode() & QIODevice::WriteOnly)) {
554 ok = d->writeBackTempFile( fileName() );
555 delete d->tmpFile;
556 d->tmpFile = 0;
557 setDevice(0);
558 }
559
560 return ok;
561}
562
563bool KTar::doFinishWriting( qint64 size ) {
564 // Write alignment
565 int rest = size % 0x200;
566 if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
567 d->tarEnd = device()->pos() + (rest ? 0x200 - rest : 0); // Record our new end of archive
568 if ( rest )
569 {
570 char buffer[ 0x201 ];
571 for( uint i = 0; i < 0x200; ++i )
572 buffer[i] = 0;
573 qint64 nwritten = device()->write( buffer, 0x200 - rest );
574 return nwritten == 0x200 - rest;
575 }
576 return true;
577}
578
579/*** Some help from the tar sources
580struct posix_header
581{ byte offset
582 char name[100]; * 0 * 0x0
583 char mode[8]; * 100 * 0x64
584 char uid[8]; * 108 * 0x6c
585 char gid[8]; * 116 * 0x74
586 char size[12]; * 124 * 0x7c
587 char mtime[12]; * 136 * 0x88
588 char chksum[8]; * 148 * 0x94
589 char typeflag; * 156 * 0x9c
590 char linkname[100]; * 157 * 0x9d
591 char magic[6]; * 257 * 0x101
592 char version[2]; * 263 * 0x107
593 char uname[32]; * 265 * 0x109
594 char gname[32]; * 297 * 0x129
595 char devmajor[8]; * 329 * 0x149
596 char devminor[8]; * 337 * ...
597 char prefix[155]; * 345 *
598 * 500 *
599};
600*/
601
602void KTar::KTarPrivate::fillBuffer( char * buffer,
603 const char * mode, qint64 size, time_t mtime, char typeflag,
604 const char * uname, const char * gname ) {
605 // mode (as in stpos())
606 assert( strlen(mode) == 6 );
607 memcpy( buffer+0x64, mode, 6 );
608 buffer[ 0x6a ] = ' ';
609 buffer[ 0x6b ] = '\0';
610
611 // dummy uid
612 strcpy( buffer + 0x6c, " 765 ");
613 // dummy gid
614 strcpy( buffer + 0x74, " 144 ");
615
616 // size
617 QByteArray s = QByteArray::number( size, 8 ); // octal
618 s = s.rightJustified( 11, '0' );
619 memcpy( buffer + 0x7c, s.data(), 11 );
620 buffer[ 0x87 ] = ' '; // space-terminate (no null after)
621
622 // modification time
623 s = QByteArray::number( static_cast<qulonglong>(mtime), 8 ); // octal
624 s = s.rightJustified( 11, '0' );
625 memcpy( buffer + 0x88, s.data(), 11 );
626 buffer[ 0x93 ] = ' '; // space-terminate (no null after) -- well current tar writes a null byte
627
628 // spaces, replaced by the check sum later
629 buffer[ 0x94 ] = 0x20;
630 buffer[ 0x95 ] = 0x20;
631 buffer[ 0x96 ] = 0x20;
632 buffer[ 0x97 ] = 0x20;
633 buffer[ 0x98 ] = 0x20;
634 buffer[ 0x99 ] = 0x20;
635
636 /* From the tar sources :
637 Fill in the checksum field. It's formatted differently from the
638 other fields: it has [6] digits, a null, then a space -- rather than
639 digits, a space, then a null. */
640
641 buffer[ 0x9a ] = '\0';
642 buffer[ 0x9b ] = ' ';
643
644 // type flag (dir, file, link)
645 buffer[ 0x9c ] = typeflag;
646
647 // magic + version
648 strcpy( buffer + 0x101, "ustar");
649 strcpy( buffer + 0x107, "00" );
650
651 // user
652 strcpy( buffer + 0x109, uname );
653 // group
654 strcpy( buffer + 0x129, gname );
655
656 // Header check sum
657 int check = 32;
658 for( uint j = 0; j < 0x200; ++j )
659 check += buffer[j];
660 s = QByteArray::number( check, 8 ); // octal
661 s = s.rightJustified( 6, '0' );
662 memcpy( buffer + 0x94, s.constData(), 6 );
663}
664
665void 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 ); // TODO error checking
671 qint64 offset = 0;
672 while (namelen > 0) {
673 int chunksize = qMin(namelen, 0x200LL);
674 memcpy(buffer, name.data()+offset, chunksize);
675 // write long name
676 q->device()->write( buffer, 0x200 ); // TODO error checking
677 // not even needed to reclear the buffer, tar doesn't do it
678 namelen -= chunksize;
679 offset += 0x200;
680 }/*wend*/
681}
682
683bool KTar::doPrepareWriting(const QString &name, const QString &user,
684 const QString &group, qint64 size, mode_t perm,
685 time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
686 if ( !isOpen() )
687 {
688 kWarning(7041) << "You must open the tar file before writing to it\n";
689 return false;
690 }
691
692 if ( !(mode() & QIODevice::WriteOnly) )
693 {
694 kWarning(7041) << "You must open the tar file for writing\n";
695 return false;
696 }
697
698 // In some tar files we can find dir/./file => call cleanPath
699 QString fileName ( QDir::cleanPath( name ) );
700
701 /*
702 // Create toplevel dirs
703 // Commented out by David since it's not necessary, and if anybody thinks it is,
704 // he needs to implement a findOrCreate equivalent in writeDir.
705 // But as KTar and the "tar" program both handle tar files without
706 // dir entries, there's really no need for that
707 QString tmp ( fileName );
708 int i = tmp.lastIndexOf( '/' );
709 if ( i != -1 )
710 {
711 QString d = tmp.left( i + 1 ); // contains trailing slash
712 if ( !m_dirList.contains( d ) )
713 {
714 tmp = tmp.mid( i + 1 );
715 writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs
716 }
717 }
718 */
719
720 char buffer[ 0x201 ];
721 memset( buffer, 0, 0x200 );
722 if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
723 device()->seek(d->tarEnd); // Go to end of archive as might have moved with a read
724
725 // provide converted stuff we need later on
726 const QByteArray encodedFileName = QFile::encodeName(fileName);
727 const QByteArray uname = user.toLocal8Bit();
728 const QByteArray gname = group.toLocal8Bit();
729
730 // If more than 100 chars, we need to use the LongLink trick
731 if ( fileName.length() > 99 )
732 d->writeLonglink(buffer,encodedFileName,'L',uname,gname);
733
734 // Write (potentially truncated) name
735 strncpy( buffer, encodedFileName, 99 );
736 buffer[99] = 0;
737 // zero out the rest (except for what gets filled anyways)
738 memset(buffer+0x9d, 0, 0x200 - 0x9d);
739
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);
743
744 // Write header
745 return device()->write( buffer, 0x200 ) == 0x200;
746}
747
748bool KTar::doWriteDir(const QString &name, const QString &user,
749 const QString &group, mode_t perm,
750 time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
751 if ( !isOpen() )
752 {
753 kWarning(7041) << "You must open the tar file before writing to it\n";
754 return false;
755 }
756
757 if ( !(mode() & QIODevice::WriteOnly) )
758 {
759 kWarning(7041) << "You must open the tar file for writing\n";
760 return false;
761 }
762
763 // In some tar files we can find dir/./ => call cleanPath
764 QString dirName ( QDir::cleanPath( name ) );
765
766 // Need trailing '/'
767 if ( !dirName.endsWith( QLatin1Char( '/' ) ) )
768 dirName += QLatin1Char( '/' );
769
770 if ( d->dirList.contains( dirName ) )
771 return true; // already there
772
773 char buffer[ 0x201 ];
774 memset( buffer, 0, 0x200 );
775 if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
776 device()->seek(d->tarEnd); // Go to end of archive as might have moved with a read
777
778 // provide converted stuff we need lateron
779 QByteArray encodedDirname = QFile::encodeName(dirName);
780 QByteArray uname = user.toLocal8Bit();
781 QByteArray gname = group.toLocal8Bit();
782
783 // If more than 100 chars, we need to use the LongLink trick
784 if ( dirName.length() > 99 )
785 d->writeLonglink(buffer,encodedDirname,'L',uname,gname);
786
787 // Write (potentially truncated) name
788 strncpy( buffer, encodedDirname, 99 );
789 buffer[99] = 0;
790 // zero out the rest (except for what gets filled anyways)
791 memset(buffer+0x9d, 0, 0x200 - 0x9d);
792
793 QByteArray permstr = QByteArray::number( (unsigned int)perm, 8 );
794 permstr = permstr.rightJustified(6, ' ');
795 d->fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
796
797 // Write header
798 device()->write( buffer, 0x200 );
799 if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
800 d->tarEnd = device()->pos();
801
802 d->dirList.append( dirName ); // contains trailing slash
803 return true; // TODO if wanted, better error control
804}
805
806bool KTar::doWriteSymLink(const QString &name, const QString &target,
807 const QString &user, const QString &group,
808 mode_t perm, time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
809 if ( !isOpen() )
810 {
811 kWarning(7041) << "You must open the tar file before writing to it\n";
812 return false;
813 }
814
815 if ( !(mode() & QIODevice::WriteOnly) )
816 {
817 kWarning(7041) << "You must open the tar file for writing\n";
818 return false;
819 }
820
821 // In some tar files we can find dir/./file => call cleanPath
822 QString fileName ( QDir::cleanPath( name ) );
823
824 char buffer[ 0x201 ];
825 memset( buffer, 0, 0x200 );
826 if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
827 device()->seek(d->tarEnd); // Go to end of archive as might have moved with a read
828
829 // provide converted stuff we need lateron
830 QByteArray encodedFileName = QFile::encodeName(fileName);
831 QByteArray encodedTarget = QFile::encodeName(target);
832 QByteArray uname = user.toLocal8Bit();
833 QByteArray gname = group.toLocal8Bit();
834
835 // If more than 100 chars, we need to use the LongLink trick
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);
840
841 // Write (potentially truncated) name
842 strncpy( buffer, encodedFileName, 99 );
843 buffer[99] = 0;
844 // Write (potentially truncated) symlink target
845 strncpy(buffer+0x9d, encodedTarget, 99);
846 buffer[0x9d+99] = 0;
847 // zero out the rest
848 memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
849
850 QByteArray permstr = QByteArray::number( (unsigned int)perm, 8 );
851 permstr = permstr.rightJustified(6, ' ');
852 d->fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
853
854 // Write header
855 bool retval = device()->write( buffer, 0x200 ) == 0x200;
856 if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
857 d->tarEnd = device()->pos();
858 return retval;
859}
860
861void KTar::virtual_hook( int id, void* data ) {
862 KArchive::virtual_hook( id, data );
863}
KArchiveDirectory
Represents a directory entry in a KArchive.
Definition: karchive.h:543
KArchiveDirectory::addEntry
void addEntry(KArchiveEntry *)
Definition: karchive.cpp:757
KArchiveEntry
A base class for entries in an KArchive.
Definition: karchive.h:370
KArchiveFile
Represents a file entry in a KArchive.
Definition: karchive.h:458
KArchive
KArchive is a base class for reading and writing archives.
Definition: karchive.h:44
KArchive::device
QIODevice * device() const
The underlying device.
Definition: karchive.cpp:475
KArchive::createDevice
virtual bool createDevice(QIODevice::OpenMode mode)
Can be reimplemented in order to change the creation of the device (when using the fileName construct...
Definition: karchive.cpp:131
KArchive::close
virtual bool close()
Closes the archive.
Definition: karchive.cpp:164
KArchive::rootDir
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
Definition: karchive.cpp:391
KArchive::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: karchive.cpp:871
KArchive::findOrCreate
KArchiveDirectory * findOrCreate(const QString &path)
Ensures that path exists, create otherwise.
Definition: karchive.cpp:406
KArchive::mode
QIODevice::OpenMode mode() const
Returns the mode in which the archive was opened.
Definition: karchive.cpp:470
KArchive::fileName
QString fileName() const
The name of the archive file, as passed to the constructor that takes a fileName, or an empty string ...
Definition: karchive.cpp:485
KArchive::setRootDir
void setRootDir(KArchiveDirectory *rootDir)
Derived classes call setRootDir from openArchive, to set the root directory after parsing an existing...
Definition: karchive.cpp:464
KArchive::isOpen
bool isOpen() const
Checks whether the archive is open.
Definition: karchive.cpp:480
KArchive::setDevice
void setDevice(QIODevice *dev)
Can be called by derived classes in order to set the underlying device.
Definition: karchive.cpp:456
KFilterDev
A class for reading and writing compressed data onto a device (e.g.
Definition: kfilterdev.h:37
KFilterDev::seek
virtual bool seek(qint64)
That one can be quite slow, when going back.
Definition: kfilterdev.cpp:142
KFilterDev::deviceForFile
static QIODevice * deviceForFile(const QString &fileName, const QString &mimetype=QString(), bool forceFilter=false)
Reimplemented to return true.
Definition: kfilterdev.cpp:64
KFilterDev::device
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...
Definition: kfilterdev.cpp:84
KMimeType::is
bool is(const QString &mimeTypeName) const
Do not use name()=="somename" anymore, to check for a given mimetype.
Definition: kmimetype.cpp:556
KMimeType::findByFileContent
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...
Definition: kmimetype.cpp:375
KMimeType::findByPath
static Ptr findByPath(const QString &path, mode_t mode=0, bool fast_mode=false, int *accuracy=0)
Finds a KMimeType with the given url.
Definition: kmimetype.cpp:329
KMimeType::defaultMimeTypePtr
static KMimeType::Ptr defaultMimeTypePtr()
Returns the default mimetype.
Definition: kmimetype.cpp:43
KSharedPtr< KMimeType >
KTar
A class for reading / writing (optionally compressed) tar archives.
Definition: ktar.h:33
KTar::doFinishWriting
virtual bool doFinishWriting(qint64 size)
Reimplemented from KArchive.
Definition: ktar.cpp:563
KTar::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: ktar.cpp:861
KTar::KTar
KTar(const QString &filename, const QString &mimetype=QString())
Creates an instance that operates on the given filename using the compression filter associated to gi...
Definition: ktar.cpp:74
KTar::doPrepareWriting
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.
Definition: ktar.cpp:683
KTar::doWriteSymLink
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.
Definition: ktar.cpp:806
KTar::doWriteDir
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.
Definition: ktar.cpp:748
KTar::createDevice
virtual bool createDevice(QIODevice::OpenMode mode)
Can be reimplemented in order to change the creation of the device (when using the fileName construct...
Definition: ktar.cpp:87
KTar::setOrigFileName
void setOrigFileName(const QByteArray &fileName)
Special function for setting the "original file name" in the gzip header, when writing a tar....
Definition: ktar.cpp:168
KTar::openArchive
virtual bool openArchive(QIODevice::OpenMode mode)
Opens the archive for reading.
Definition: ktar.cpp:332
KTar::~KTar
virtual ~KTar()
If the tar ball is still opened, then it will be closed automatically by the destructor.
Definition: ktar.cpp:158
KTar::closeArchive
virtual bool closeArchive()
Closes the archive.
Definition: ktar.cpp:545
KTemporaryFile
A QTemporaryFile that will save in the KDE temp directory.
Definition: ktemporaryfile.h:93
QIODevice
QStringList
QString
qint64
kDebug
#define kDebug
Definition: kdebug.h:316
kWarning
#define kWarning
Definition: kdebug.h:322
kdebug.h
kfilterbase.h
kfilterdev.h
kmimetype.h
prefix
QString prefix()
Definition: kstandarddirs_win.cpp:28
application_bzip
static const char application_bzip[]
Definition: ktar.cpp:41
application_zip
static const char application_zip[]
Definition: ktar.cpp:44
application_xz
static const char application_xz[]
Definition: ktar.cpp:43
application_lzma
static const char application_lzma[]
Definition: ktar.cpp:42
application_gzip
static const char application_gzip[]
Definition: ktar.cpp:40
ktar.h
ktemporaryfile.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDECore

Skip menu "KDECore"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal