• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdeio/tdeio
 

tdeio/tdeio

  • tdeio
  • tdeio
ktar.cpp
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 <stdio.h>
21 #include <stdlib.h> // strtol
22 #include <time.h> // time()
23 /*#include <unistd.h>
24 #include <grp.h>
25 #include <pwd.h>*/
26 #include <assert.h>
27 
28 #include <tqcstring.h>
29 #include <tqdir.h>
30 #include <tqfile.h>
31 #include <kdebug.h>
32 #include <kmimetype.h>
33 #include <tdetempfile.h>
34 
35 #include <kfilterdev.h>
36 #include <kfilterbase.h>
37 
38 #include "ktar.h"
39 #include <tdestandarddirs.h>
40 
44 
45 class KTar::KTarPrivate
46 {
47 public:
48  KTarPrivate() : tarEnd( 0 ), tmpFile( 0 ) {}
49  TQStringList dirList;
50  int tarEnd;
51  KTempFile* tmpFile;
52  TQString mimetype;
53  TQCString origFileName;
54 
55  bool fillTempFile(const TQString & filename);
56  bool writeBackTempFile( const TQString & filename );
57 };
58 
59 KTar::KTar( const TQString& filename, const TQString & _mimetype )
60  : KArchive( 0 )
61 {
62  m_filename = filename;
63  d = new KTarPrivate;
64  TQString mimetype( _mimetype );
65  bool forced = true;
66  if ( mimetype.isEmpty() ) // Find out mimetype manually
67  {
68  if ( TQFile::exists( filename ) )
69  mimetype = KMimeType::findByFileContent( filename )->name();
70  else
71  mimetype = KMimeType::findByPath( filename, 0, true )->name();
72  kdDebug(7041) << "KTar::KTar mimetype = " << mimetype << endl;
73 
74  // Don't move to prepareDevice - the other constructor theoretically allows ANY filter
75  if ( mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around
76  mimetype == "application/x-webarchive" )
77  {
78  // that's a gzipped tar file, so ask for gzip filter
79  mimetype = "application/x-gzip";
80  }
81  else if ( mimetype == "application/x-tbz" ) // that's a bzipped2 tar file, so ask for bz2 filter
82  {
83  mimetype = "application/x-bzip2";
84  }
85  else
86  {
87  // Something else. Check if it's not really gzip though (e.g. for KOffice docs)
88  TQFile file( filename );
89  if ( file.open( IO_ReadOnly ) )
90  {
91  unsigned char firstByte = file.getch();
92  unsigned char secondByte = file.getch();
93  unsigned char thirdByte = file.getch();
94  if ( firstByte == 0037 && secondByte == 0213 )
95  mimetype = "application/x-gzip";
96  else if ( firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h' )
97  mimetype = "application/x-bzip2";
98  else if ( firstByte == 'P' && secondByte == 'K' && thirdByte == 3 )
99  {
100  unsigned char fourthByte = file.getch();
101  if ( fourthByte == 4 )
102  mimetype = "application/x-zip";
103  }
104  else if ( firstByte == 0xfd && secondByte == '7' && thirdByte == 'z' )
105  {
106  unsigned char fourthByte = file.getch();
107  unsigned char fifthByte = file.getch();
108  unsigned char sixthByte = file.getch();
109  if ( fourthByte == 'X' && fifthByte == 'Z' && sixthByte == 0x00 )
110  mimetype = "application/x-xz";
111  }
112  else if ( firstByte == 0x5d && secondByte == 0x00 && thirdByte == 0x00 )
113  {
114  unsigned char fourthByte = file.getch();
115  if ( fourthByte == 0x80 )
116  mimetype = "application/x-lzma";
117  }
118  else if ( firstByte == 'L' && secondByte == 'Z' && thirdByte == 'I' )
119  {
120  unsigned char fourthByte = file.getch();
121  unsigned char fifthByte = file.getch();
122  if ( fourthByte == 'P' && fifthByte == 0x01 )
123  mimetype = "application/x-lzip";
124  }
125  }
126  file.close();
127  }
128  forced = false;
129  }
130  d->mimetype = mimetype;
131 
132  prepareDevice( filename, mimetype, forced );
133 }
134 
135 void KTar::prepareDevice( const TQString & filename,
136  const TQString & mimetype, bool /*forced*/ )
137 {
138  if( "application/x-tar" == mimetype )
139  setDevice( new TQFile( filename ) );
140  else
141  {
142  // The compression filters are very slow with random access.
143  // So instead of applying the filter to the device,
144  // the file is completly extracted instead,
145  // and we work on the extracted tar file.
146  // This improves the extraction speed by the tar ioslave dramatically,
147  // if the archive file contains many files.
148  // This is because the tar ioslave extracts one file after the other and normally
149  // has to walk through the decompression filter each time.
150  // Which is in fact nearly as slow as a complete decompression for each file.
151  d->tmpFile = new KTempFile(locateLocal("tmp", "ktar-"),".tar");
152  kdDebug( 7041 ) << "KTar::prepareDevice creating TempFile: " << d->tmpFile->name() << endl;
153  d->tmpFile->setAutoDelete(true);
154 
155  // KTempFile opens the file automatically,
156  // the device must be closed, however, for KArchive.setDevice()
157  TQFile* file = d->tmpFile->file();
158  file->close();
159  setDevice(file);
160  }
161 }
162 
163 KTar::KTar( TQIODevice * dev )
164  : KArchive( dev )
165 {
166  Q_ASSERT( dev );
167  d = new KTarPrivate;
168 }
169 
170 KTar::~KTar()
171 {
172  // mjarrett: Closes to prevent ~KArchive from aborting w/o device
173  if( isOpened() )
174  close();
175 
176  if (d->tmpFile)
177  delete d->tmpFile; // will delete the device
178  else if ( !m_filename.isEmpty() )
179  delete device(); // we created it ourselves
180 
181 
182  delete d;
183 }
184 
185 void KTar::setOrigFileName( const TQCString & fileName )
186 {
187  if ( !isOpened() || !(mode() & IO_WriteOnly) )
188  {
189  kdWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n";
190  return;
191  }
192  d->origFileName = fileName;
193 }
194 
195 TQ_LONG KTar::readRawHeader(char *buffer) {
196  // Read header
197  TQ_LONG n = device()->readBlock( buffer, 0x200 );
198  if ( n == 0x200 && buffer[0] != 0 ) {
199  // Make sure this is actually a tar header
200  if (strncmp(buffer + 257, "ustar", 5)) {
201  // The magic isn't there (broken/old tars), but maybe a correct checksum?
202  TQCString s;
203 
204  int check = 0;
205  for( uint j = 0; j < 0x200; ++j )
206  check += buffer[j];
207 
208  // adjust checksum to count the checksum fields as blanks
209  for( uint j = 0; j < 8 /*size of the checksum field including the \0 and the space*/; j++ )
210  check -= buffer[148 + j];
211  check += 8 * ' ';
212 
213  s.sprintf("%o", check );
214 
215  // only compare those of the 6 checksum digits that mean something,
216  // because the other digits are filled with all sorts of different chars by different tars ...
217  // Some tars right-justify the checksum so it could start in one of three places - we have to check each.
218  if( strncmp( buffer + 148 + 6 - s.length(), s.data(), s.length() )
219  && strncmp( buffer + 148 + 7 - s.length(), s.data(), s.length() )
220  && strncmp( buffer + 148 + 8 - s.length(), s.data(), s.length() ) ) {
221  kdWarning(7041) << "KTar: invalid TAR file. Header is: " << TQCString( buffer+257, 5 ) << endl;
222  return -1;
223  }
224  }/*end if*/
225  } else {
226  // reset to 0 if 0x200 because logical end of archive has been reached
227  if (n == 0x200) n = 0;
228  }/*end if*/
229  return n;
230 }
231 
232 bool KTar::readLonglink(char *buffer,TQCString &longlink) {
233  TQ_LONG n = 0;
234  TQIODevice *dev = device();
235  // read size of longlink from size field in header
236  // size is in bytes including the trailing null (which we ignore)
237  buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
238  char *dummy;
239  const char* p = buffer + 0x7c;
240  while( *p == ' ' ) ++p;
241  int size = (int)strtol( p, &dummy, 8 );
242 
243  longlink.resize(size);
244  size--; // ignore trailing null
245  dummy = longlink.data();
246  int offset = 0;
247  while (size > 0) {
248  int chunksize = TQMIN(size, 0x200);
249  n = dev->readBlock( dummy + offset, chunksize );
250  if (n == -1) return false;
251  size -= chunksize;
252  offset += 0x200;
253  }/*wend*/
254  // jump over the rest
255  int skip = 0x200 - (n % 0x200);
256  if (skip < 0x200) {
257  if (dev->readBlock(buffer,skip) != skip) return false;
258  }
259  return true;
260 }
261 
262 TQ_LONG KTar::readHeader(char *buffer,TQString &name,TQString &symlink) {
263  name.truncate(0);
264  symlink.truncate(0);
265  while (true) {
266  TQ_LONG n = readRawHeader(buffer);
267  if (n != 0x200) return n;
268 
269  // is it a longlink?
270  if (strcmp(buffer,"././@LongLink") == 0) {
271  char typeflag = buffer[0x9c];
272  TQCString longlink;
273  readLonglink(buffer,longlink);
274  switch (typeflag) {
275  case 'L': name = TQFile::decodeName(longlink); break;
276  case 'K': symlink = TQFile::decodeName(longlink); break;
277  }/*end switch*/
278  } else {
279  break;
280  }/*end if*/
281  }/*wend*/
282 
283  // if not result of longlink, read names directly from the header
284  if (name.isEmpty())
285  // there are names that are exactly 100 bytes long
286  // and neither longlink nor \0 terminated (bug:101472)
287  name = TQFile::decodeName(TQCString(buffer, 101));
288  if (symlink.isEmpty())
289  symlink = TQFile::decodeName(TQCString(buffer + 0x9d, 101));
290 
291  return 0x200;
292 }
293 
294 /*
295  * If we have created a temporary file, we have
296  * to decompress the original file now and write
297  * the contents to the temporary file.
298  */
299 bool KTar::KTarPrivate::fillTempFile( const TQString & filename) {
300  if ( ! tmpFile )
301  return true;
302 
303  kdDebug( 7041 ) <<
304  "KTar::openArchive: filling tmpFile of mimetype '" << mimetype <<
305  "' ... " << endl;
306 
307  bool forced = false;
308  if( "application/x-gzip" == mimetype
309  || "application/x-bzip2" == mimetype
310  || "application/x-lzma" == mimetype
311  || "application/x-xz" == mimetype
312  || "application/x-lzip" == mimetype)
313  forced = true;
314 
315  TQIODevice *filterDev = KFilterDev::deviceForFile( filename, mimetype, forced );
316 
317  if( filterDev ) {
318  TQFile* file = tmpFile->file();
319  file->close();
320  if ( ! file->open( IO_WriteOnly ) )
321  {
322  delete filterDev;
323  return false;
324  }
325  TQByteArray buffer(8*1024);
326  if ( ! filterDev->open( IO_ReadOnly ) )
327  {
328  delete filterDev;
329  return false;
330  }
331  TQ_LONG len = -1;
332  while ( !filterDev->atEnd() && len != 0) {
333  len = filterDev->readBlock(buffer.data(),buffer.size());
334  if ( len < 0 ) { // corrupted archive
335  delete filterDev;
336  return false;
337  }
338  file->writeBlock(buffer.data(),len);
339  }
340  filterDev->close();
341  delete filterDev;
342 
343  file->close();
344  if ( ! file->open( IO_ReadOnly ) )
345  return false;
346  }
347  else
348  kdDebug( 7041 ) << "KTar::openArchive: no filterdevice found!" << endl;
349 
350  kdDebug( 7041 ) << "KTar::openArchive: filling tmpFile finished." << endl;
351  return true;
352 }
353 
354 bool KTar::openArchive( int mode )
355 {
356  kdDebug( 7041 ) << "KTar::openArchive" << endl;
357  if ( !(mode & IO_ReadOnly) )
358  return true;
359 
360  if ( !d->fillTempFile( m_filename ) )
361  return false;
362 
363  // We'll use the permission and user/group of d->rootDir
364  // for any directory we emulate (see findOrCreate)
365  //struct stat buf;
366  //stat( m_filename, &buf );
367 
368  d->dirList.clear();
369  TQIODevice* dev = device();
370 
371  if ( !dev )
372  return false;
373 
374  // read dir infos
375  char buffer[ 0x200 ];
376  bool ende = false;
377  do
378  {
379  TQString name;
380  TQString symlink;
381 
382  // Read header
383  TQ_LONG n = readHeader(buffer,name,symlink);
384  if (n < 0) return false;
385  if (n == 0x200)
386  {
387  bool isdir = false;
388  TQString nm;
389 
390  if ( name.right(1) == "/" )
391  {
392  isdir = true;
393  name = name.left( name.length() - 1 );
394  }
395 
396  int pos = name.findRev( '/' );
397  if ( pos == -1 )
398  nm = name;
399  else
400  nm = name.mid( pos + 1 );
401 
402  // read access
403  buffer[ 0x6b ] = 0;
404  char *dummy;
405  const char* p = buffer + 0x64;
406  while( *p == ' ' ) ++p;
407  int access = (int)strtol( p, &dummy, 8 );
408 
409  // read user and group
410  TQString user( buffer + 0x109 );
411  TQString group( buffer + 0x129 );
412 
413  // read time
414  buffer[ 0x93 ] = 0;
415  p = buffer + 0x88;
416  while( *p == ' ' ) ++p;
417  int time = (int)strtol( p, &dummy, 8 );
418 
419  // read type flag
420  char typeflag = buffer[ 0x9c ];
421  // '0' for files, '1' hard link, '2' symlink, '5' for directory
422  // (and 'L' for longlink filenames, 'K' for longlink symlink targets)
423  // and 'D' for GNU tar extension DUMPDIR
424  if ( typeflag == '5' )
425  isdir = true;
426 
427  bool isDumpDir = false;
428  if ( typeflag == 'D' )
429  {
430  isdir = false;
431  isDumpDir = true;
432  }
433  //bool islink = ( typeflag == '1' || typeflag == '2' );
434  //kdDebug(7041) << "typeflag=" << typeflag << " islink=" << islink << endl;
435 
436  if (isdir)
437  access |= S_IFDIR; // f*cking broken tar files
438 
439  KArchiveEntry* e;
440  if ( isdir )
441  {
442  //kdDebug(7041) << "KTar::openArchive directory " << nm << endl;
443  e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
444  }
445  else
446  {
447  // read size
448  buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
449  char *dummy;
450  const char* p = buffer + 0x7c;
451  while( *p == ' ' ) ++p;
452  int size = (int)strtol( p, &dummy, 8 );
453 
454  // for isDumpDir we will skip the additional info about that dirs contents
455  if ( isDumpDir )
456  {
457  //kdDebug(7041) << "KTar::openArchive " << nm << " isDumpDir" << endl;
458  e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
459  }
460  else
461  {
462 
463  // Let's hack around hard links. Our classes don't support that, so make them symlinks
464  if ( typeflag == '1' )
465  {
466  kdDebug(7041) << "HARD LINK, setting size to 0 instead of " << size << endl;
467  size = 0; // no contents
468  }
469 
470  //kdDebug(7041) << "KTar::openArchive file " << nm << " size=" << size << endl;
471  e = new KArchiveFile( this, nm, access, time, user, group, symlink,
472  dev->at(), size );
473  }
474 
475  // Skip contents + align bytes
476  int rest = size % 0x200;
477  int skip = size + (rest ? 0x200 - rest : 0);
478  //kdDebug(7041) << "KTar::openArchive, at()=" << dev->at() << " rest=" << rest << " skipping " << skip << endl;
479  if (! dev->at( dev->at() + skip ) )
480  kdWarning(7041) << "KTar::openArchive skipping " << skip << " failed" << endl;
481  }
482 
483  if ( pos == -1 )
484  {
485  if ( nm == "." ) // special case
486  {
487  Q_ASSERT( isdir );
488  if ( isdir )
489  setRootDir( static_cast<KArchiveDirectory *>( e ) );
490  }
491  else
492  rootDir()->addEntry( e );
493  }
494  else
495  {
496  // In some tar files we can find dir/./file => call cleanDirPath
497  TQString path = TQDir::cleanDirPath( name.left( pos ) );
498  // Ensure container directory exists, create otherwise
499  KArchiveDirectory * d = findOrCreate( path );
500  d->addEntry( e );
501  }
502  }
503  else
504  {
505  //tqDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]);
506  d->tarEnd = dev->at() - n; // Remember end of archive
507  ende = true;
508  }
509  } while( !ende );
510  return true;
511 }
512 
513 /*
514  * Writes back the changes of the temporary file
515  * to the original file.
516  * Must only be called if in IO_WriteOnly mode
517  */
518 bool KTar::KTarPrivate::writeBackTempFile( const TQString & filename ) {
519  if ( ! tmpFile )
520  return true;
521 
522  kdDebug(7041) << "Write temporary file to compressed file" << endl;
523  kdDebug(7041) << filename << " " << mimetype << endl;
524 
525  bool forced = false;
526  if( "application/x-gzip" == mimetype
527  || "application/x-bzip2" == mimetype
528  || "application/x-lzma" == mimetype
529  || "application/x-xz" == mimetype
530  || "application/x-lzip" == mimetype)
531  forced = true;
532 
533  TQIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced );
534  if( dev ) {
535  TQFile* file = tmpFile->file();
536  file->close();
537  if ( ! file->open(IO_ReadOnly) || ! dev->open(IO_WriteOnly) )
538  {
539  file->close();
540  delete dev;
541  return false;
542  }
543  if ( forced )
544  static_cast<KFilterDev *>(dev)->setOrigFileName( origFileName );
545  TQByteArray buffer(8*1024);
546  TQ_LONG len;
547  while ( ! file->atEnd()) {
548  len = file->readBlock(buffer.data(),buffer.size());
549  dev->writeBlock(buffer.data(),len);
550  }
551  file->close();
552  dev->close();
553  delete dev;
554  }
555 
556  kdDebug(7041) << "Write temporary file to compressed file done." << endl;
557  return true;
558 }
559 
560 bool KTar::closeArchive()
561 {
562  d->dirList.clear();
563 
564  // If we are in write mode and had created
565  // a temporary tar file, we have to write
566  // back the changes to the original file
567  if( mode() == IO_WriteOnly)
568  return d->writeBackTempFile( m_filename );
569 
570  return true;
571 }
572 
573 bool KTar::writeDir( const TQString& name, const TQString& user, const TQString& group )
574 {
575  mode_t perm = 040755;
576  time_t the_time = time(0);
577  return writeDir(name,user,group,perm,the_time,the_time,the_time);
578 #if 0
579  if ( !isOpened() )
580  {
581  kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
582  return false;
583  }
584 
585  if ( !(mode() & IO_WriteOnly) )
586  {
587  kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
588  return false;
589  }
590 
591  // In some tar files we can find dir/./ => call cleanDirPath
592  TQString dirName ( TQDir::cleanDirPath( name ) );
593 
594  // Need trailing '/'
595  if ( dirName.right(1) != "/" )
596  dirName += "/";
597 
598  if ( d->dirList.contains( dirName ) )
599  return true; // already there
600 
601  char buffer[ 0x201 ];
602  memset( buffer, 0, 0x200 );
603  if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
604 
605  // If more than 100 chars, we need to use the LongLink trick
606  if ( dirName.length() > 99 )
607  {
608  strcpy( buffer, "././@LongLink" );
609  fillBuffer( buffer, " 0", dirName.length()+1, 'L', user.local8Bit(), group.local8Bit() );
610  device()->writeBlock( buffer, 0x200 );
611  strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
612  buffer[0x200] = 0;
613  // write long name
614  device()->writeBlock( buffer, 0x200 );
615  // not even needed to reclear the buffer, tar doesn't do it
616  }
617  else
618  {
619  // Write name
620  strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
621  buffer[0x200] = 0;
622  }
623 
624  fillBuffer( buffer, " 40755", 0, 0x35, user.local8Bit(), group.local8Bit());
625 
626  // Write header
627  device()->writeBlock( buffer, 0x200 );
628  if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
629 
630  d->dirList.append( dirName ); // contains trailing slash
631  return true; // TODO if wanted, better error control
632 #endif
633 }
634 
635 bool KTar::prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size )
636 {
637  mode_t dflt_perm = 0100644;
638  time_t the_time = time(0);
639  return prepareWriting(name,user,group,size,dflt_perm,
640  the_time,the_time,the_time);
641 }
642 
643 bool KTar::doneWriting( uint size )
644 {
645  // Write alignment
646  int rest = size % 0x200;
647  if ( mode() & IO_ReadWrite )
648  d->tarEnd = device()->at() + (rest ? 0x200 - rest : 0); // Record our new end of archive
649  if ( rest )
650  {
651  char buffer[ 0x201 ];
652  for( uint i = 0; i < 0x200; ++i )
653  buffer[i] = 0;
654  TQ_LONG nwritten = device()->writeBlock( buffer, 0x200 - rest );
655  return nwritten == 0x200 - rest;
656  }
657  return true;
658 }
659 
660 /*** Some help from the tar sources
661 struct posix_header
662 { byte offset
663  char name[100]; * 0 * 0x0
664  char mode[8]; * 100 * 0x64
665  char uid[8]; * 108 * 0x6c
666  char gid[8]; * 116 * 0x74
667  char size[12]; * 124 * 0x7c
668  char mtime[12]; * 136 * 0x88
669  char chksum[8]; * 148 * 0x94
670  char typeflag; * 156 * 0x9c
671  char linkname[100]; * 157 * 0x9d
672  char magic[6]; * 257 * 0x101
673  char version[2]; * 263 * 0x107
674  char uname[32]; * 265 * 0x109
675  char gname[32]; * 297 * 0x129
676  char devmajor[8]; * 329 * 0x149
677  char devminor[8]; * 337 * ...
678  char prefix[155]; * 345 *
679  * 500 *
680 };
681 */
682 
683 void KTar::fillBuffer( char * buffer,
684  const char * mode, int size, time_t mtime, char typeflag,
685  const char * uname, const char * gname )
686 {
687  // mode (as in stat())
688  assert( strlen(mode) == 6 );
689  strcpy( buffer+0x64, mode );
690  buffer[ 0x6a ] = ' ';
691  buffer[ 0x6b ] = '\0';
692 
693  // dummy uid
694  strcpy( buffer + 0x6c, " 765 ");
695  // dummy gid
696  strcpy( buffer + 0x74, " 144 ");
697 
698  // size
699  TQCString s;
700  s.sprintf("%o", size); // OCT
701  s = s.rightJustify( 11, ' ' );
702  strcpy( buffer + 0x7c, s.data() );
703  buffer[ 0x87 ] = ' '; // space-terminate (no null after)
704 
705  // modification time
706  s.sprintf("%lo", static_cast<unsigned long>(mtime) ); // OCT
707  s = s.rightJustify( 11, ' ' );
708  strcpy( buffer + 0x88, s.data() );
709  buffer[ 0x93 ] = ' '; // space-terminate (no null after)
710 
711  // spaces, replaced by the check sum later
712  buffer[ 0x94 ] = 0x20;
713  buffer[ 0x95 ] = 0x20;
714  buffer[ 0x96 ] = 0x20;
715  buffer[ 0x97 ] = 0x20;
716  buffer[ 0x98 ] = 0x20;
717  buffer[ 0x99 ] = 0x20;
718 
719  /* From the tar sources :
720  Fill in the checksum field. It's formatted differently from the
721  other fields: it has [6] digits, a null, then a space -- rather than
722  digits, a space, then a null. */
723 
724  buffer[ 0x9a ] = '\0';
725  buffer[ 0x9b ] = ' ';
726 
727  // type flag (dir, file, link)
728  buffer[ 0x9c ] = typeflag;
729 
730  // magic + version
731  strcpy( buffer + 0x101, "ustar");
732  strcpy( buffer + 0x107, "00" );
733 
734  // user
735  strcpy( buffer + 0x109, uname );
736  // group
737  strcpy( buffer + 0x129, gname );
738 
739  // Header check sum
740  int check = 32;
741  for( uint j = 0; j < 0x200; ++j )
742  check += buffer[j];
743  s.sprintf("%o", check ); // OCT
744  s = s.rightJustify( 7, ' ' );
745  strcpy( buffer + 0x94, s.data() );
746 }
747 
748 void KTar::writeLonglink(char *buffer, const TQCString &name, char typeflag,
749  const char *uname, const char *gname) {
750  strcpy( buffer, "././@LongLink" );
751  int namelen = name.length() + 1;
752  fillBuffer( buffer, " 0", namelen, 0, typeflag, uname, gname );
753  device()->writeBlock( buffer, 0x200 );
754  int offset = 0;
755  while (namelen > 0) {
756  int chunksize = TQMIN(namelen, 0x200);
757  memcpy(buffer, name.data()+offset, chunksize);
758  // write long name
759  device()->writeBlock( buffer, 0x200 );
760  // not even needed to reclear the buffer, tar doesn't do it
761  namelen -= chunksize;
762  offset += 0x200;
763  }/*wend*/
764 }
765 
766 bool KTar::prepareWriting(const TQString& name, const TQString& user,
767  const TQString& group, uint size, mode_t perm,
768  time_t atime, time_t mtime, time_t ctime) {
769  return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
770 }
771 
772 bool KTar::prepareWriting_impl(const TQString &name, const TQString &user,
773  const TQString &group, uint size, mode_t perm,
774  time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
775  if ( !isOpened() )
776  {
777  kdWarning(7041) << "KTar::prepareWriting: You must open the tar file before writing to it\n";
778  return false;
779  }
780 
781  if ( !(mode() & IO_WriteOnly) )
782  {
783  kdWarning(7041) << "KTar::prepareWriting: You must open the tar file for writing\n";
784  return false;
785  }
786 
787  // In some tar files we can find dir/./file => call cleanDirPath
788  TQString fileName ( TQDir::cleanDirPath( name ) );
789 
790  /*
791  // Create toplevel dirs
792  // Commented out by David since it's not necessary, and if anybody thinks it is,
793  // he needs to implement a findOrCreate equivalent in writeDir.
794  // But as KTar and the "tar" program both handle tar files without
795  // dir entries, there's really no need for that
796  TQString tmp ( fileName );
797  int i = tmp.findRev( '/' );
798  if ( i != -1 )
799  {
800  TQString d = tmp.left( i + 1 ); // contains trailing slash
801  if ( !m_dirList.contains( d ) )
802  {
803  tmp = tmp.mid( i + 1 );
804  writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs
805  }
806  }
807  */
808 
809  char buffer[ 0x201 ];
810  memset( buffer, 0, 0x200 );
811  if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
812 
813  // provide converted stuff we need lateron
814  TQCString encodedFilename = TQFile::encodeName(fileName);
815  TQCString uname = user.local8Bit();
816  TQCString gname = group.local8Bit();
817 
818  // If more than 100 chars, we need to use the LongLink trick
819  if ( fileName.length() > 99 )
820  writeLonglink(buffer,encodedFilename,'L',uname,gname);
821 
822  // Write (potentially truncated) name
823  strncpy( buffer, encodedFilename, 99 );
824  buffer[99] = 0;
825  // zero out the rest (except for what gets filled anyways)
826  memset(buffer+0x9d, 0, 0x200 - 0x9d);
827 
828  TQCString permstr;
829  permstr.sprintf("%o",perm);
830  permstr = permstr.rightJustify(6, ' ');
831  fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
832 
833  // Write header
834  return device()->writeBlock( buffer, 0x200 ) == 0x200;
835 }
836 
837 bool KTar::writeDir(const TQString& name, const TQString& user,
838  const TQString& group, mode_t perm,
839  time_t atime, time_t mtime, time_t ctime) {
840  return KArchive::writeDir(name,user,group,perm,atime,mtime,ctime);
841 }
842 
843 bool KTar::writeDir_impl(const TQString &name, const TQString &user,
844  const TQString &group, mode_t perm,
845  time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
846  if ( !isOpened() )
847  {
848  kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
849  return false;
850  }
851 
852  if ( !(mode() & IO_WriteOnly) )
853  {
854  kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
855  return false;
856  }
857 
858  // In some tar files we can find dir/./ => call cleanDirPath
859  TQString dirName ( TQDir::cleanDirPath( name ) );
860 
861  // Need trailing '/'
862  if ( dirName.right(1) != "/" )
863  dirName += "/";
864 
865  if ( d->dirList.contains( dirName ) )
866  return true; // already there
867 
868  char buffer[ 0x201 ];
869  memset( buffer, 0, 0x200 );
870  if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
871 
872  // provide converted stuff we need lateron
873  TQCString encodedDirname = TQFile::encodeName(dirName);
874  TQCString uname = user.local8Bit();
875  TQCString gname = group.local8Bit();
876 
877  // If more than 100 chars, we need to use the LongLink trick
878  if ( dirName.length() > 99 )
879  writeLonglink(buffer,encodedDirname,'L',uname,gname);
880 
881  // Write (potentially truncated) name
882  strncpy( buffer, encodedDirname, 99 );
883  buffer[99] = 0;
884  // zero out the rest (except for what gets filled anyways)
885  memset(buffer+0x9d, 0, 0x200 - 0x9d);
886 
887  TQCString permstr;
888  permstr.sprintf("%o",perm);
889  permstr = permstr.rightJustify(6, ' ');
890  fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
891 
892  // Write header
893  device()->writeBlock( buffer, 0x200 );
894  if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
895 
896  d->dirList.append( dirName ); // contains trailing slash
897  return true; // TODO if wanted, better error control
898 }
899 
900 bool KTar::writeSymLink(const TQString &name, const TQString &target,
901  const TQString &user, const TQString &group,
902  mode_t perm, time_t atime, time_t mtime, time_t ctime) {
903  return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
904 }
905 
906 bool KTar::writeSymLink_impl(const TQString &name, const TQString &target,
907  const TQString &user, const TQString &group,
908  mode_t perm, time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
909  if ( !isOpened() )
910  {
911  kdWarning(7041) << "KTar::writeSymLink: You must open the tar file before writing to it\n";
912  return false;
913  }
914 
915  if ( !(mode() & IO_WriteOnly) )
916  {
917  kdWarning(7041) << "KTar::writeSymLink: You must open the tar file for writing\n";
918  return false;
919  }
920 
921  device()->flush();
922 
923  // In some tar files we can find dir/./file => call cleanDirPath
924  TQString fileName ( TQDir::cleanDirPath( name ) );
925 
926  char buffer[ 0x201 ];
927  memset( buffer, 0, 0x200 );
928  if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
929 
930  // provide converted stuff we need lateron
931  TQCString encodedFilename = TQFile::encodeName(fileName);
932  TQCString encodedTarget = TQFile::encodeName(target);
933  TQCString uname = user.local8Bit();
934  TQCString gname = group.local8Bit();
935 
936  // If more than 100 chars, we need to use the LongLink trick
937  if (target.length() > 99)
938  writeLonglink(buffer,encodedTarget,'K',uname,gname);
939  if ( fileName.length() > 99 )
940  writeLonglink(buffer,encodedFilename,'L',uname,gname);
941 
942  // Write (potentially truncated) name
943  strncpy( buffer, encodedFilename, 99 );
944  buffer[99] = 0;
945  // Write (potentially truncated) symlink target
946  strncpy(buffer+0x9d, encodedTarget, 99);
947  buffer[0x9d+99] = 0;
948  // zero out the rest
949  memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
950 
951  TQCString permstr;
952  permstr.sprintf("%o",perm);
953  permstr = permstr.rightJustify(6, ' ');
954  fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
955 
956  // Write header
957  bool retval = device()->writeBlock( buffer, 0x200 ) == 0x200;
958  if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
959  return retval;
960 }
961 
962 void KTar::virtual_hook( int id, void* data ) {
963  switch (id) {
964  case VIRTUAL_WRITE_SYMLINK: {
965  WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
966  params->retval = writeSymLink_impl(*params->name,*params->target,
967  *params->user,*params->group,params->perm,
968  params->atime,params->mtime,params->ctime);
969  break;
970  }
971  case VIRTUAL_WRITE_DIR: {
972  WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data);
973  params->retval = writeDir_impl(*params->name,*params->user,
974  *params->group,params->perm,
975  params->atime,params->mtime,params->ctime);
976  break;
977  }
978  case VIRTUAL_PREPARE_WRITING: {
979  PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
980  params->retval = prepareWriting_impl(*params->name,*params->user,
981  *params->group,params->size,params->perm,
982  params->atime,params->mtime,params->ctime);
983  break;
984  }
985  default:
986  KArchive::virtual_hook( id, data );
987  }/*end switch*/
988 }
989 
KArchiveDirectory
Represents a directory entry in a KArchive.
Definition: karchive.h:574
KArchiveEntry
A base class for entries in an KArchive.
Definition: karchive.h:396
KArchiveFile
Represents a file entry in a KArchive.
Definition: karchive.h:491
KArchive
KArchive is a base class for reading and writing archives.
Definition: karchive.h:43
KArchive::writeDir
virtual bool writeDir(const TQString &name, const TQString &user, const TQString &group)=0
If an archive is opened for writing then you can add new directories using this function.
KArchive::rootDir
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
Definition: karchive.cpp:368
KArchive::findOrCreate
KArchiveDirectory * findOrCreate(const TQString &path)
Ensures that path exists, create otherwise.
Definition: karchive.cpp:383
KArchive::close
virtual void close()
Closes the archive.
Definition: karchive.cpp:108
KArchive::mode
int mode() const
Returns the mode in which the archive was opened.
Definition: karchive.h:90
KArchive::isOpened
bool isOpened() const
Checks whether the archive is open.
Definition: karchive.h:83
KArchive::device
TQIODevice * device() const
The underlying device.
Definition: karchive.h:96
KArchive::writeSymLink
bool writeSymLink(const TQString &name, const TQString &target, const TQString &user, const TQString &group, mode_t perm, time_t atime, time_t mtime, time_t ctime)
Writes a symbolic link to the archive if the archive must be opened for writing.
Definition: karchive.cpp:327
KArchive::prepareWriting
virtual bool prepareWriting(const TQString &name, const TQString &user, const TQString &group, uint size)=0
Here's another way of writing a file into an archive: Call prepareWriting, then call writeData() as m...
KFilterDev
A class for reading and writing compressed data onto a device (e.g.
Definition: kfilterdev.h:37
KFilterDev::deviceForFile
static TQIODevice * deviceForFile(const TQString &fileName, const TQString &mimetype=TQString::null, bool forceFilter=false)
Creates an i/o device that is able to read from fileName, whether it's compressed or not.
Definition: kfilterdev.cpp:81
KMimeType::findByPath
static Ptr findByPath(const TQString &path, mode_t mode=0, bool fast_mode=false)
Finds a KMimeType with the given _url.
Definition: kmimetype.cpp:308
KMimeType::findByFileContent
static Ptr findByFileContent(const TQString &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:323
KTar::setOrigFileName
void setOrigFileName(const TQCString &fileName)
Special function for setting the "original file name" in the gzip header, when writing a tar....
Definition: ktar.cpp:185
KTar::fileName
TQString fileName()
The name of the tar file, as passed to the constructor Null if you used the TQIODevice constructor.
Definition: ktar.h:77
KTar::doneWriting
virtual bool doneWriting(uint size)
Call doneWriting after writing the data.
Definition: ktar.cpp:643
KTar::openArchive
virtual bool openArchive(int mode)
Opens the archive for reading.
Definition: ktar.cpp:354
KTar::~KTar
virtual ~KTar()
If the tar ball is still opened, then it will be closed automatically by the destructor.
Definition: ktar.cpp:170
KTar::closeArchive
virtual bool closeArchive()
Closes the archive.
Definition: ktar.cpp:560
KTar::KTar
KTar(const TQString &filename, const TQString &mimetype=TQString::null)
Creates an instance that operates on the given filename using the compression filter associated to gi...
Definition: ktar.cpp:59
KTar::prepareWriting
virtual bool prepareWriting(const TQString &name, const TQString &user, const TQString &group, uint size)
Here's another way of writing a file into an archive: Call prepareWriting, then call writeData() as m...
Definition: ktar.cpp:635
KTar::writeDir
virtual bool writeDir(const TQString &name, const TQString &user, const TQString &group)
If an archive is opened for writing then you can add new directories using this function.
Definition: ktar.cpp:573
TDEIO::symlink
TDEIO_EXPORT SimpleJob * symlink(const TQString &target, const KURL &dest, bool overwrite, bool showProgressInfo=true)
Create or move a symlink.
Definition: job.cpp:808
TDEIO::mimetype
TDEIO_EXPORT MimetypeJob * mimetype(const KURL &url, bool showProgressInfo=true)
Find mimetype for one file or directory.
Definition: job.cpp:1573

tdeio/tdeio

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

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdeio/tdeio by doxygen 1.9.1
This website is maintained by Timothy Pearson.