• 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 <kstandarddirs.h>
40
44
45class KTar::KTarPrivate
46{
47public:
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
59KTar::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
135void 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
163KTar::KTar( TQIODevice * dev )
164 : KArchive( dev )
165{
166 Q_ASSERT( dev );
167 d = new KTarPrivate;
168}
169
170KTar::~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
185void 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
195TQ_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
232bool 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
262TQ_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 */
299bool 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
354bool 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 */
518bool 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
560bool 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
573bool 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
635bool 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
643bool 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
661struct 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
683void 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
748void 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
766bool 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
772bool 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
837bool 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
843bool 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
900bool 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
906bool 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
962void 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::device
TQIODevice * device() const
The underlying device.
Definition karchive.h:96
KArchive::isOpened
bool isOpened() const
Checks whether the archive is open.
Definition karchive.h:83
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
  • 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.8
This website is maintained by Timothy Pearson.