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

KDECore

  • sources
  • kde-4.12
  • kdelibs
  • 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
40 static const char application_gzip[] = "application/x-gzip";
41 static const char application_bzip[] = "application/x-bzip";
42 static const char application_lzma[] = "application/x-lzma";
43 static const char application_xz[] = "application/x-xz";
44 static const char application_zip[] = "application/zip";
45 
46 class KTar::KTarPrivate
47 {
48 public:
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 
74 KTar::KTar( const QString& fileName, const QString & _mimetype )
75  : KArchive( fileName ), d(new KTarPrivate(this))
76 {
77  d->mimetype = _mimetype;
78 }
79 
80 KTar::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
87 bool 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 
158 KTar::~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 
168 void 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 
177 qint64 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 
217 bool 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 
244 qint64 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  */
281 bool 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 
332 bool 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  */
501 bool 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 
545 bool 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 
563 bool 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
580 struct 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 
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 ) {
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 
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 ); // 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 
683 bool 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 
748 bool 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 
806 bool 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 
861 void KTar::virtual_hook( int id, void* data ) {
862  KArchive::virtual_hook( id, data );
863 }
KFilterDev::deviceForFile
static QIODevice * deviceForFile(const QString &fileName, const QString &mimetype=QString(), bool forceFilter=false)
Reimplemented to return true.
Definition: kfilterdev.cpp:64
KSharedPtr< KMimeType >
qint64
application_xz
static const char application_xz[]
Definition: ktar.cpp:43
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
KArchive::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: karchive.cpp:860
kdebug.h
KArchive::rootDir
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
Definition: karchive.cpp:391
kmimetype.h
kfilterdev.h
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
KMacroExpander::group
Definition: kmacroexpander_unix.cpp:34
KArchive
KArchive is a base class for reading and writing archives.
Definition: karchive.h:43
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
KTar::~KTar
virtual ~KTar()
If the tar ball is still opened, then it will be closed automatically by the destructor.
Definition: ktar.cpp:158
QString
KTemporaryFile
A QTemporaryFile that will save in the KDE temp directory.
Definition: ktemporaryfile.h:92
KMimeType::defaultMimeTypePtr
static KMimeType::Ptr defaultMimeTypePtr()
Returns the default mimetype.
Definition: kmimetype.cpp:43
KArchive::close
virtual bool close()
Closes the archive.
Definition: karchive.cpp:164
KArchive::mode
QIODevice::OpenMode mode() const
Returns the mode in which the archive was opened.
Definition: karchive.cpp:470
prefix
QString prefix()
Definition: kstandarddirs_win.cpp:28
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
kfilterbase.h
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
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
KFilterDev
A class for reading and writing compressed data onto a device (e.g.
Definition: kfilterdev.h:36
KTar
A class for reading / writing (optionally compressed) tar archives.
Definition: ktar.h:32
QStringList
application_gzip
static const char application_gzip[]
Definition: ktar.cpp:40
KArchiveEntry
A base class for entries in an KArchive.
Definition: karchive.h:369
application_zip
static const char application_zip[]
Definition: ktar.cpp:44
application_bzip
static const char application_bzip[]
Definition: ktar.cpp:41
KTar::closeArchive
virtual bool closeArchive()
Closes the archive.
Definition: ktar.cpp:545
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
KArchive::isOpen
bool isOpen() const
Checks whether the archive is open.
Definition: karchive.cpp:480
kWarning
#define kWarning
Definition: kdebug.h:322
KArchiveDirectory::addEntry
void addEntry(KArchiveEntry *)
Definition: karchive.cpp:757
ktar.h
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
ktemporaryfile.h
KArchive::findOrCreate
KArchiveDirectory * findOrCreate(const QString &path)
Ensures that path exists, create otherwise.
Definition: karchive.cpp:406
KArchiveDirectory
Represents a directory entry in a KArchive.
Definition: karchive.h:542
KTar::openArchive
virtual bool openArchive(QIODevice::OpenMode mode)
Opens the archive for reading.
Definition: ktar.cpp:332
KFilterDev::seek
virtual bool seek(qint64)
That one can be quite slow, when going back.
Definition: kfilterdev.cpp:142
application_lzma
static const char application_lzma[]
Definition: ktar.cpp:42
KDE::access
int access(const QString &path, int mode)
Definition: kde_file_win.cpp:123
KArchiveFile
Represents a file entry in a KArchive.
Definition: karchive.h:457
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
kDebug
#define kDebug
Definition: kdebug.h:316
KTar::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: ktar.cpp:861
KTar::doFinishWriting
virtual bool doFinishWriting(qint64 size)
Reimplemented from KArchive.
Definition: ktar.cpp:563
KArchive::setDevice
void setDevice(QIODevice *dev)
Can be called by derived classes in order to set the underlying device.
Definition: karchive.cpp:456
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
QIODevice
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::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
KMimeType::is
bool is(const QString &mimeTypeName) const
Do not use name()=="somename" anymore, to check for a given mimetype.
Definition: kmimetype.cpp:556
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:47:09 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



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