UltraScan III
us_tar.cpp
Go to the documentation of this file.
1 /* This is a highly customized version of GNU tar. It has
2  * been converted to Qt and C++.
3  * It only includes the extract and create functions and NO options.
4  *
5  * Since this file is derived from a GPLed application, this file is
6  * also licensed under the GPL.
7  *
8  * Bruce Dubbs
9  * Univerity of Texas Health Science Center
10  */
11 
12 #include "us_tar.h"
13 
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 
17 #ifdef Q_WS_X11
18 #include <unistd.h>
19 #endif
20 
21 #ifdef WIN32
22 # include <io.h>
23 # include <sys/utime.h>
24 # include <sys/timeb.h>
25 # include <BaseTsd.h>
26 # include <winsock2.h>
27 # define open _open
28 # define read _read
29 # define write _write
30 # define close _close
31 # define fstat _fstat
32 # define stat _stat
33 # define utime _utime
34 # define utimbuf _utimbuf
35 # define umask _umask
36 # define ssize_t SSIZE_T
37 #else
38 # include <utime.h>
39 # include <pwd.h>
40 # include <grp.h>
41 # include <sys/time.h>
42 #endif
43 
44 #define LONGFILE "././@LongLink"
45 
46 #include <time.h>
47 
48 #include <vector>
49 using namespace std;
50 
52 {
53 }
54 
55 int US_Tar::create( const QString& archive, const QString& directory,
56  QStringList* list )
57 {
58  // Just put the directory in a list and create the file that way.
59  QStringList dir;
60  dir << directory;
61  return create( archive, dir, list );
62 }
63 
64 int US_Tar::create( const QString& archive, const QStringList& files,
65  QStringList* list )
66 {
67  // To create the file, we do the following:
68  // 1. Open the archive filefor writing. This will overwrite any existing
69  // tar file by the same name
70  // 2. For each file in the list
71  // a. If the file is a directory, continue for each file in the directory
72  // b. Write the header to the archive
73  // c. Copy the file to the archive
74  // 3. Write two null headers (512 bytes)
75 
76  QStringList all;
77 
78  if ( list ) list->clear();
79 
80  for ( int i = 0; i < files.size(); i++ )
81  {
82  QString current = files[ i ];
83  QFileInfo f( current );
84 
85  if ( ! f.exists() ) return TAR_NOTFOUND;
86 
87  if ( f.isDir() )
88  {
89  // Remove any trailing slash
90  if ( current.endsWith( "/" ) )
91  {
92  current = current.left( current.length() - 1 );
93  }
94 
95  all << current;
96  process_dir( current, all );
97  continue;
98  }
99 
100  if ( f.isHidden() ) // No hidden files
101  {
102  continue;
103  }
104 
105  // Append regular files to the list
106  if ( f.isFile() )
107  {
108  all << current;
109  }
110 
111  // Block and character devices, pipes, and symbolic links should be ignored
112  }
113 
114  if ( list ) *list = all;
115  // Process all files
116 
117 #ifndef O_BINARY
118 # define O_BINARY 0
119 #endif
120 
121  ofd = open( archive.toLatin1(), O_WRONLY | O_CREAT | O_BINARY, 0644 );
122 
123  if ( ofd < 0 ) return TAR_CANNOTCREATE;
124 
125  QStringList::iterator it = all.begin();
126  blocks_written = 0;
127 
128  try
129  {
130 
131  while ( it != all.end() )
132  {
133  write_file ( *it++ );
134  }
135 
136  archive_end();
137  }
138  catch ( int error )
139  {
140  close( ifd );
141  close( ofd );
142  unlink( archive.toLatin1() );
143  return error;
144  }
145 
146  close( ofd );
147  return TAR_OK;
148 }
149 
150 void US_Tar::process_dir( const QString& path, QStringList& all )
151 {
152  QDir dir( path );
153  QStringList files = dir.entryList( QDir::Files | QDir::NoSymLinks );
154  QStringList::Iterator it = files.begin();
155 
156  // Add the files
157  while( it != files.end() )
158  {
159  all << path + "/" + *it;
160  it++;
161  }
162 
163  QStringList dirs = dir.entryList( QDir::Dirs );
164  it = dirs.begin();
165 
166  // Recurse into subdirectories
167  while ( it != dirs.end() )
168  {
169  if ( *it != "." && *it != ".." )
170  {
171  all << path + "/" + *it + "/";
172  process_dir( path + "/" + *it, all );
173  }
174 
175  it++;
176  }
177 }
178 
179 /* The bits in mode: */
180 #define TSUID 04000
181 #define TSGID 02000
182 #define TSVTX 01000
183 #define TUREAD 00400
184 #define TUWRITE 00200
185 #define TUEXEC 00100
186 #define TGREAD 00040
187 #define TGWRITE 00020
188 #define TGEXEC 00010
189 #define TOREAD 00004
190 #define TOWRITE 00002
191 #define TOEXEC 00001
192 
193 void US_Tar::write_file( const QString& file )
194 {
195  // Create and output the header
196  QFileInfo f( file );
197 
198  struct stat stats;
199  int ret = stat( file.toLatin1(), &stats );
200  if ( ret < 0 ) throw TAR_CANTSTAT;
201 
202  memset( (void*) tar_header.h, 0, sizeof( tar_header ) );
203 
204  // Populate the header
205  if ( file.length() > (int)sizeof( tar_header.header.name ) - 1 )
206  write_long_filename( file );
207 
208  strcpy( tar_header.header.name, file.toLatin1() );
209 
210  int perms = TSUID | TSGID | TSVTX |
211  TUREAD | TUWRITE | TUEXEC |
212  TGREAD | TGWRITE | TGEXEC |
213  TOREAD | TOWRITE | TOEXEC ;
214 
215  sprintf ( tar_header.header.mode, "%07o", stats.st_mode & perms );
216  sprintf ( tar_header.header.uid, "%07o", stats.st_uid );
217  sprintf ( tar_header.header.gid, "%07o", stats.st_gid );
218  sprintf ( tar_header.header.size, "%011o", (unsigned int) stats.st_size );
219  sprintf ( tar_header.header.mtime, "%011o", (unsigned int) stats.st_mtime );
220 
221  // Fill with blanks befor checksumming
222  memcpy( &tar_header.header.chksum, " ", sizeof tar_header.header.chksum );
223 
224  //char typeflag; /* 156 */
225 
226  if ( f.isDir() ) tar_header.header.typeflag = '5';
227  else if ( f.isFile() ) tar_header.header.typeflag = '0';
228  else throw TAR_INTERNAL;
229 
230  //char linkname[100]; /* 157 */
231  //char magic[6]; /* 257 */
232  //char version[2]; /* 263 */
233 
234  memcpy( tar_header.header.magic, "ustar ", 6 );
235  memcpy( tar_header.header.version, " ", 2 );
236 
237 #ifndef WIN32
238  // uid and gid are always zero on WIN32 systems
239  //char uname[32]; /* 265 */
240  struct passwd* pwbuf = getpwuid( stats.st_uid );
241  if ( pwbuf ) sprintf ( tar_header.header.uname, "%s", pwbuf->pw_name );
242 
243  //char gname[32]; /* 297 */
244  struct group* grpbuf = getgrgid( stats.st_gid );
245  if ( grpbuf ) sprintf ( tar_header.header.gname, "%s", grpbuf->gr_name );
246 #endif
247  /* Fill in the checksum field. It's formatted differently from the
248  * other fields: it has [6] digits, a null, then a space -- rather than
249  * digits, then a null. */
250 
251  //char chksum[8];
252  int sum = 0;
253  char* p = (char*) &tar_header;
254 
255  for ( int i = sizeof tar_header; i-- != 0; )
256  {
257  sum += 0xFF & *p++;
258  }
259  sprintf ( tar_header.header.chksum, "%06o", sum );
260 
261  // Copy the header to the buffer
262  memcpy( (void*) ( buffer + blocks_written * BLOCK_SIZE ),
263  (void*) tar_header.h,
264  sizeof tar_header );
265 
266  // Write the buffer if it is full
267  blocks_written++;
268  if ( blocks_written == BLOCKING_FACTOR ) flush_buffer();
269 
270  // Output the file
271  if ( ! f.isDir() )
272  {
273  ifd = open( file.toLatin1(), O_RDONLY | O_BINARY );
274  if ( ifd < 0 ) throw TAR_READERROR;
275 
276  ssize_t input;
277  int full_blocks = stats.st_size / BLOCK_SIZE;
278  int space = BLOCKING_FACTOR - blocks_written;
279 
280  if ( full_blocks > 0 && space >= full_blocks )
281  {
282  input = read( ifd, buffer + blocks_written * BLOCK_SIZE,
283  full_blocks * BLOCK_SIZE );
284  if ( input != full_blocks * BLOCK_SIZE ) throw TAR_READERROR;
285  blocks_written += full_blocks;
286  if ( blocks_written == BLOCKING_FACTOR ) flush_buffer();
287  }
288  else if ( full_blocks > 0 )
289  {
290  input = read( ifd, buffer + blocks_written * BLOCK_SIZE,
291  space * BLOCK_SIZE );
292  blocks_written += space;
293  full_blocks -= space;
294  flush_buffer();
295 
296  // Read/write in BLOCKING_FACTOR blocks at a time
297  while ( full_blocks >= BLOCKING_FACTOR )
298  {
299  space = BLOCKING_FACTOR;
300  input = read( ifd, buffer, space * BLOCK_SIZE );
301  if ( input != space * BLOCK_SIZE ) throw TAR_READERROR;
302  blocks_written += space;
303  full_blocks -= space;
304  flush_buffer();
305  }
306 
307  // Write out remaining full blocks
308  input = read( ifd, buffer, full_blocks * BLOCK_SIZE );
309  if ( input != full_blocks * BLOCK_SIZE ) throw TAR_READERROR;
310  blocks_written += full_blocks;
311 
312  // We don't need ot flush here because we always have
313  // less than BLOCKING_FACTOR blocks
314  }
315 
316  // Now finish up any partial block
317  input = read( ifd, buffer + blocks_written * BLOCK_SIZE, BLOCK_SIZE );
318  if ( input < 0 ) throw TAR_READERROR;
319 
320  if ( input > 0 )
321  {
322  // Zero out anyting left
323  memset( buffer + blocks_written * BLOCK_SIZE + input, 0,
324  BLOCK_SIZE - input );
325  blocks_written++;
326  if ( blocks_written == BLOCKING_FACTOR ) flush_buffer();
327  }
328 
329  close( ifd );
330  }
331 }
332 
333 void US_Tar::write_long_filename( const QString& filename )
334 {
335  // If there is a long filename, write a special header, followed by the
336  // necessary number of blocks that are needed for the filename
337 
338  // Add 1 for null byte teminating filename
339  unsigned int length = (unsigned int) filename.length() + 1;
340 
341  //char name[100]; /* 0 */
342  strcpy( tar_header.header.name, LONGFILE );
343 
344  //char mode[8]; /* 100 */
345  //char uid[8]; /* 108 */
346  //char gid[8]; /* 116 */
347  //char size[12]; /* 124 */
348  //char mtime[12]; /* 136 */
349 
350  sprintf ( tar_header.header.mode, "%07o", 0 );
351  sprintf ( tar_header.header.uid, "%07o", 0 );
352  sprintf ( tar_header.header.gid, "%07o", 0 );
353  sprintf ( tar_header.header.size, "%011o", length );
354  sprintf ( tar_header.header.mtime, "%011o", 0 );
355 
356  // Fill with blanks befor checksumming
357  memcpy( &tar_header.header.chksum, " ", sizeof tar_header.header.chksum );
358 
359  //char typeflag; /* 156 */
360 
361  tar_header.header.typeflag = 'L';
362 
363  //char linkname[100]; /* 157 */
364  //char magic[6]; /* 257 */
365  //char version[2]; /* 263 */
366 
367  memcpy( tar_header.header.magic, "ustar ", 6 );
368  memcpy( tar_header.header.version, " ", 2 );
369 
370  //char uname[32]; /* 265 */
371  //char gname[32]; /* 297 */
372  strcpy ( tar_header.header.uname, "root" );
373  strcpy ( tar_header.header.gname, "root" );
374 
375  /* Fill in the checksum field. It's formatted differently from the
376  * other fields: it has [6] digits, a null, then a space -- rather than
377  * digits, then a null. */
378 
379  //char chksum[8];
380  int sum = 0;
381  char* p = (char*) &tar_header;
382 
383  for ( int i = sizeof tar_header; i-- != 0; )
384  {
385  sum += 0xFF & *p++;
386  }
387 
388  sprintf ( tar_header.header.chksum, "%06o", sum );
389 
390  // Copy the header to the buffer
391  memcpy( (void*) ( buffer + blocks_written * BLOCK_SIZE ),
392  (void*) tar_header.h,
393  sizeof tar_header );
394 
395  // Write the buffer if it is full
396  blocks_written++;
397  if ( blocks_written == BLOCKING_FACTOR ) flush_buffer();
398 
399  // Now write the filename in one of more blocks
400  int full_blocks = length / BLOCK_SIZE;
401 
402  for ( int i = 0; i < full_blocks; i++ )
403  {
404  memset( (void*) tar_header.h, 0, sizeof tar_header );
405 
406  memcpy( (void*) tar_header.h,
407  (void*) filename.mid( i * BLOCK_SIZE, BLOCK_SIZE ).constData(),
408  BLOCK_SIZE );
409 
410  // Copy the header to the buffer
411  memcpy( (void*) ( buffer + blocks_written * BLOCK_SIZE ),
412  (void*) tar_header.h,
413  sizeof tar_header );
414 
415  // Write the buffer if it is full
416  blocks_written++;
417  if ( blocks_written == BLOCKING_FACTOR ) flush_buffer();
418  }
419 
420  // Copy what is left
421  if ( ( length ) % BLOCK_SIZE )
422  {
423  memset( (void*) tar_header.h, 0, sizeof tar_header );
424 
425  memcpy( (void*) tar_header.h,
426  (void*) filename.mid( full_blocks * BLOCK_SIZE ).constData(),
427  length % BLOCK_SIZE );
428 
429  // Copy the header to the buffer
430  memcpy( (void*) ( buffer + blocks_written * BLOCK_SIZE ),
431  (void*) tar_header.h,
432  sizeof tar_header );
433 
434  // Write the buffer if it is full
435  blocks_written++;
436  if ( blocks_written == BLOCKING_FACTOR ) flush_buffer();
437  }
438 
439  // Finally write the first 100 bytes of the original filename
440  memset( (void*) tar_header.h, 0, sizeof( tar_header ) );
441  memcpy( (void*) tar_header.h, filename.toLatin1(), 100 );
442 }
443 
445 {
446  // Write a null block
447  void* location = (void*) ( buffer + blocks_written * BLOCK_SIZE );
448  memset( location, 0, BLOCK_SIZE );
449  blocks_written++;
450 
451  if ( blocks_written == BLOCKING_FACTOR ) flush_buffer();
452 
453  // Write a second null block and finish
454  location = (void*) ( buffer + blocks_written * BLOCK_SIZE );
455  memset( location, 0, BLOCK_SIZE );
456  blocks_written++;
457  flush_buffer();
458 }
459 
460 
462 {
463  if ( blocks_written == 0 ) return;
464 
465  if ( blocks_written < BLOCKING_FACTOR )
466  {
467  size_t size = ( BLOCKING_FACTOR - blocks_written ) * BLOCK_SIZE;
468  void* location = (void*) ( buffer + blocks_written * BLOCK_SIZE );
469  memset( location, 0, size );
470  }
471 
472  int ret = write( ofd, buffer, sizeof buffer );
473 
474  if ( ret != sizeof buffer ) throw TAR_WRITEERROR;
475 
476  blocks_written = 0;
477 }
478 
480 int US_Tar::extract( const QString& archive, QStringList* list )
481 {
482  /* 1. Open the archve
483  * 2. while header is not null
484  * a. read header
485  * i. validate chksum
486  * b. if file
487  * i. copy from archive to file
488  * ii. fix permissions, owner, group, size, and date
489  * c. if directory
490  * i. mkdir
491  * ii. fix owner, group
492  * 3. fix directory times
493  */
494 
495  ofd = -1; // Initialize output file to closed
496  ifd = open( archive.toLatin1(), O_RDONLY | O_BINARY );
497  if ( ifd < 0 ) return TAR_NOTFOUND;
498 
499  if ( list ) list->clear();
500 
501  blocks_read = 0;
502  QStringList files;
503 
504  QStringList dirs;
505  vector<int> times;
506 
507  try
508  {
509  while ( true )
510  {
511  // Read header
512  read_block();
513 
514  // Validate checksum
515  bool zero = validate_header();
516 
517  // The archive ends with two zero blocks
518  if ( zero )
519  {
520  read_block();
521  bool second_zero = validate_header();
522 
523  if ( second_zero )
524  {
525  break;
526  }
527  else
528  {
529  throw TAR_ARCHIVEERROR;
530  }
531  }
532 
533  // Now get the data from the header
534 
535  QString filename;
536 
537  if ( tar_header.header.typeflag == 'L' )
538  filename = get_long_filename();
539  else
540  filename = tar_header.header.name;
541 
542  QString uname = tar_header.header.uname;
543  QString gname = tar_header.header.gname;
544 
545  unsigned int mode;
546 #ifndef WIN32
547  uid_t uid;
548  gid_t gid;
549 #endif
550  unsigned int fsize;
551  unsigned int mtime;
552 
553  sscanf( tar_header.header.mode, "%7o", &mode );
554 #ifndef WIN32
555  sscanf( tar_header.header.uid, "%7o", &uid );
556  sscanf( tar_header.header.gid, "%7o", &gid );
557 #endif
558  sscanf( tar_header.header.size, "%11o", &fsize );
559  sscanf( tar_header.header.mtime, "%11o", &mtime );
560 
561  bool directory;
562  switch ( tar_header.header.typeflag )
563  {
564  case '5':
565  directory = true;
566  mode |= 0111; // Make sure directory is executable
567  break;
568 
569  case '0':
570  directory = false;
571  break;
572 
573  default:
574  throw TAR_ARCHIVEERROR;
575  }
576 
577  files << filename; // Save the file name for error processing
578  if ( list ) list->append( filename ); // Return list if requested
579 
580  if ( directory )
581  {
582  QDir f( "." );
583  if ( ! f.exists( filename ) )
584  {
585  bool success = f.mkdir( filename );
586  if ( ! success ) throw TAR_MKDIRFAILED;
587  }
588 
589  // Save directory name and mode to restore at the end
590  dirs.append( filename );
591  times.push_back( mtime );
592  }
593  else // It's a file. Create it.
594  {
595  int flags = O_WRONLY | O_CREAT | O_BINARY | O_TRUNC;
596  ofd = open( filename.toLatin1(), flags, 0644 );
597  if ( ofd < 0 ) throw TAR_WRITEERROR;
598 
599  // Copy from archive to file
600 
601  unsigned int bytes_to_write = fsize;
602  int skip = BLOCK_SIZE - fsize % BLOCK_SIZE;
603 
604  if ( skip == BLOCK_SIZE ) skip = 0; // If file size is exact multple of blocks
605 
606  int size;
607 
608  while ( bytes_to_write > sizeof buffer )
609  {
610  size = read( ifd, buffer, sizeof buffer );
611  if ( size != sizeof buffer ) throw TAR_READERROR;
612 
613  size = write( ofd, buffer, sizeof buffer );
614  if ( size != sizeof buffer ) throw TAR_WRITEERROR;
615 
616  bytes_to_write -= sizeof buffer;
617  }
618 
619  size = read( ifd, buffer, bytes_to_write );
620  if ( size != (int) bytes_to_write ) throw TAR_READERROR;
621 
622  size = write( ofd, buffer, bytes_to_write );
623  if ( size != (int) bytes_to_write ) throw TAR_WRITEERROR;
624 
625  // Skip to start of next block
626  lseek( ifd, skip, SEEK_CUR );
627 
628  // Clost output file
629  close( ofd );
630  ofd = -1; // Mark as closed
631  }
632 
633  // Fix permissions, owner, group, and date
634  // We do the utime before the chmod because some versions of utime are
635  // broken and trash the modes of the file.
636 
637  // Get current time
638  struct timeval tv;
639 #ifndef WIN32
640  gettimeofday( &tv, NULL );
641 #else
642  struct _timeb timebuffer;
643  _ftime(&timebuffer);
644  tv.tv_sec = timebuffer.time;
645  tv.tv_usec = timebuffer.millitm * 1000;
646 #endif
647  // Set the times on the file
648  struct utimbuf time;
649  time.actime = tv.tv_sec; // now
650  time.modtime = mtime;
651  utime( filename.toLatin1(), &time );
652 
653 #ifndef WIN32
654  // Update permissions
655  chmod( filename.toLatin1(), mode );
656 
657  // Update owner/group
658  if ( geteuid() != 0 ) uid = (uid_t) -1;
659  int choerr = chown( filename.toAscii(), uid, gid );
660  if ( choerr != 0 ) uid = (uid_t) -1;
661 #endif
662  } // while ( true )
663  }
664  catch ( int error )
665  {
666  close( ifd );
667  if ( ofd > 0 ) close( ofd );
668 
669  // Cycle through files and delete everything created
670  for ( size_t i = files.size() -1 ; i <= 0; i++ )
671  {
672  unlink( files[i].toLatin1() );
673  }
674 
675  return error;
676  }
677 
678  close( ifd );
679 
680  // Fix directory times
681  for ( int i = 0; i < dirs.size(); i++ )
682  {
683  struct utimbuf time;
684  time.actime = times[ i ];
685  time.modtime = times[ i ];
686 
687  utime( dirs[ i ].toLatin1(), &time );
688  }
689 
690  return TAR_OK;
691 }
692 
694 int US_Tar::list( const QString& archive, QStringList& files )
695 {
696  ifd = open( archive.toLatin1(), O_RDONLY | O_BINARY );
697  if ( ifd < 0 ) return TAR_NOTFOUND;
698 
699  blocks_read = 0;
700 
701  try
702  {
703  while ( true )
704  {
705  // Read header
706  read_block();
707  bool zero = validate_header();
708 
709  // The archive ends with two zero blocks
710  if ( zero )
711  {
712  read_block();
713  bool second_zero = validate_header();
714 
715  if ( second_zero )
716  {
717  break;
718  }
719  else
720  {
721  throw TAR_ARCHIVEERROR;
722  }
723  }
724 
725  // Now get the data from the header
726  QString filename;
727 
728  if ( tar_header.header.typeflag == 'L' )
729  filename = get_long_filename();
730  else
731  filename = tar_header.header.name;
732 
733  QString uname = tar_header.header.uname;
734  QString gname = tar_header.header.gname;
735 
736  unsigned int mode;
737  unsigned int fsize;
738  unsigned int mtime;
739 
740  sscanf( tar_header.header.mode, "%7o", &mode );
741  sscanf( tar_header.header.size, "%11o", &fsize );
742  sscanf( tar_header.header.mtime, "%11o", &mtime );
743 
744  bool directory;
745  switch ( tar_header.header.typeflag )
746  {
747  case '5':
748  directory = true;
749  break;
750 
751  case '0':
752  directory = false;
753  break;
754 
755  default:
756  throw TAR_ARCHIVEERROR;
757  }
758 
759  QString s;
760 
761  // perms user/group size date time filename
762  files << format_permissions( mode, directory ) + " " +
763  uname + "/" + gname + " " +
764  s.sprintf( "%10d", fsize ) + " " +
765  format_datetime( mtime ) + " " +
766  filename;
767 
768  if ( ! directory )
769  {
770  // Skip file data
771  unsigned int fsize;
772  sscanf( tar_header.header.size, "%11o", &fsize );
773 
774  int skip = BLOCK_SIZE - fsize % BLOCK_SIZE;
775 
776  // If file size is exact multple of blocks
777  if ( skip == BLOCK_SIZE ) skip = 0;
778 
779  // Skip to start of next block
780  lseek( ifd, fsize + skip, SEEK_CUR );
781  }
782  } // while ( true )
783  }
784  catch ( int error )
785  {
786  close( ifd );
787  return error;
788  }
789 
790  close( ifd );
791  return TAR_OK;
792 }
793 
794 QString US_Tar::format_permissions( const unsigned int mode, const bool dir )
795 {
796  QString s = "----------";
797  if ( dir ) s = s.replace( 0, 1, "d" );
798  if ( mode & 0400 ) s = s.replace( 1, 1, "r" );
799  if ( mode & 0200 ) s = s.replace( 2, 1, "w" );
800  if ( mode & 0100 ) s = s.replace( 3, 1, "x" );
801  if ( mode & 0040 ) s = s.replace( 4, 1, "r" );
802  if ( mode & 0020 ) s = s.replace( 5, 1, "w" );
803  if ( mode & 0010 ) s = s.replace( 6, 1, "x" );
804  if ( mode & 0004 ) s = s.replace( 7, 1, "r" );
805  if ( mode & 0002 ) s = s.replace( 8, 1, "w" );
806  if ( mode & 0001 ) s = s.replace( 9, 1, "x" );
807 
808  return s;
809 }
810 
811 QString US_Tar::format_datetime( const unsigned int mtime )
812 {
813  time_t time = mtime;
814  struct tm* t = localtime( &time );
815 
816  QString s;
817  s = s.sprintf( "%04d-%02d-%02d %02d:%02d",
818  t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min );
819  return s;
820 }
821 
822 // The return boolean here is if header is zero
824 {
825  // Validate checksum
826  unsigned char* p = tar_header.h;
827  int unsigned_sum = 0;
828  unsigned int i;
829 
830  for ( i = sizeof tar_header; i-- != 0; )
831  {
832  unsigned_sum += (unsigned char) (*p++);
833  }
834 
835  // Signal that the end of archive has arived. Verify with another
836  // empty block
837 
838  if ( unsigned_sum == 0 ) return true;
839 
840  // Adjust checksum to count the "chksum" field as blanks.
841  for ( i = sizeof tar_header.header.chksum; i-- != 0; )
842  {
843  unsigned_sum -= (unsigned char) tar_header.header.chksum[i];
844  }
845 
846  unsigned_sum += ' ' * sizeof tar_header.header.chksum;
847 
848  // Get the checksum from the header and compare to calculated sum
849  unsigned int parsed_sum;
850  sscanf( tar_header.header.chksum, "%6o", &parsed_sum );
851 
852  if ( parsed_sum != (unsigned int ) unsigned_sum )
853  {
854  throw TAR_ARCHIVEERROR;
855  }
856 
857  // And check the magic string
858  QString magic = tar_header.header.magic;
859  if ( magic != "ustar " )
860  {
861  throw TAR_ARCHIVEERROR;
862  }
863 
864  return false; // Header ok and not zero
865 }
866 
868 {
869  QString filename;
870 
871  unsigned int length;
872  sscanf( tar_header.header.size, "%11o", &length );
873 
874  // Skip to next header
875 
876  int final_block = ( length % BLOCK_SIZE ) ? 1 : 0;
877  int blocks = length / BLOCK_SIZE + final_block;
878 
879  for ( int i = 0; i < blocks; i++ )
880  {
881  read_block();
882  filename.append( (char*) tar_header.h );
883  }
884 
885  read_block();
886  return filename;
887 }
888 
889 void US_Tar::read_block( void )
890 {
891  ssize_t size = read( ifd, tar_header.h, BLOCK_SIZE );
892  if ( size != BLOCK_SIZE ) throw TAR_READERROR;
893 }
894 
896 QString US_Tar::explain( const int error )
897 {
898  QString explanation;
899  switch ( error )
900  {
901  case TAR_OK:
902  explanation = "The (un)tar operation was successful.";
903  break;
904 
905  case TAR_CANNOTCREATE:
906  explanation = "Could not create the output file." ;
907  break;
908 
909  case TAR_NOTFOUND:
910  explanation = "Could not find the input file." ;
911  break;
912 
913  case TAR_CANTSTAT:
914  explanation = "Could not determine the input file status." ;
915  break;
916 
917  case TAR_FILENAMETOOLONG:
918  explanation = "A file name was too long." ;
919  break;
920 
921  case TAR_INTERNAL:
922  explanation = "The internal file type was not a file or a directory." ;
923  break;
924 
925  case TAR_READERROR:
926  explanation = "Could not read input file." ;
927  break;
928 
929  case TAR_WRITEERROR:
930  explanation = "Could not write an output file." ;
931  break;
932 
933  case TAR_ARCHIVEERROR:
934  explanation = "The archive was not formatted properly." ;
935  break;
936 
937  case TAR_MKDIRFAILED:
938  explanation = "Could not create a directory." ;
939  break;
940 
941  default:
942  explanation = "Unknown return code: " + error;
943  }
944 
945  return explanation;
946 }