OSDN Git Service

Fix MinGW-Bug #2026.
[mingw/mingw-get.git] / src / tarproc.cpp
1 /*
2  * tarproc.cpp
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2009, 2010, 2011, 2012, 2013, MinGW.org Project
8  *
9  *
10  * Implementation of package archive processing methods, for reading
11  * and extracting content from tar archives; provides implementations
12  * for each of the pkgTarArchiveProcessor and pkgTarArchiveInstaller
13  * classes.
14  *
15  *
16  * This is free software.  Permission is granted to copy, modify and
17  * redistribute this software, under the provisions of the GNU General
18  * Public License, Version 3, (or, at your option, any later version),
19  * as published by the Free Software Foundation; see the file COPYING
20  * for licensing details.
21  *
22  * Note, in particular, that this software is provided "as is", in the
23  * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
24  * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
25  * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
26  * MinGW Project, accept liability for any damages, however caused,
27  * arising from the use of this software.
28  *
29  */
30 #include "pkgimpl.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40
41 #if IMPLEMENTATION_LEVEL == PACKAGE_BASE_COMPONENT
42
43 #include "dmh.h"
44 #include "debug.h"
45 #include "mkpath.h"
46
47 #include "pkginfo.h"
48 #include "pkgkeys.h"
49 #include "pkgstat.h"
50
51 #endif /* PACKAGE_BASE_COMPONENT */
52
53 #include "pkgproc.h"
54
55 /*******************
56  *
57  * Class Implementation: pkgArchiveProcessor
58  *
59  */
60 int pkgArchiveProcessor::CreateExtractionDirectory( const char *pathname )
61 {
62   /* Helper method for creation of the directory infrastructure
63    * into which archived file entities are to be extracted.
64    */
65   int status = 0;
66   if( save_on_extract && ((status = mkdir_recursive( pathname, 0755 )) != 0) )
67     dmh_notify( DMH_ERROR, "cannot create directory `%s'\n", pathname );
68   return status;
69 }
70
71 static inline int dmh_notify_extraction_failed( const char *name )
72 {
73   /* Helper function to emit archive "extraction failed" diagnostics.
74    */
75   return dmh_notify( DMH_ERROR, "%s: extraction failed\n", name );
76 }
77
78 static inline int dmh_notify_archive_data_exhausted( const char *context )
79 {
80   /* Helper function to emit "premature end of archive" diagnostics.
81    */
82   return dmh_notify( DMH_ERROR,
83       "unexpected end of archive reading %s record\n", context
84     );
85 }
86
87 static int create_output_stream( const char *name, int mode )
88 {
89   /* Wrapper encapsulating the set_output_stream() function, while
90    * protecting against inadvertently overwriting any unexpectedly
91    * pre-existing file.
92    */
93   int fd = set_output_stream( name, mode );
94   if( (fd == -1) && ((errno == EEXIST) || (errno == EACCES)) )
95   {
96     /* Overwrite prevention was triggered; diagnose.
97      */
98     dmh_notify_extraction_failed( name );
99     if( errno == EEXIST )
100     {
101       /* The exception was triggered by an already existing file;
102        * this likely indicates a conflict between two packages.
103        */
104       dmh_notify( DMH_ERROR,
105           "%s: probable package conflict; existing file not overwritten\n",
106           name
107         );
108     }
109     else
110     { /* Otherwise, the user isn't allowed to write the extracted
111        * file, in the location designated for installation.
112        */
113       dmh_notify( DMH_ERROR,
114           "%s: permission denied; cannot store file\n", name
115         );
116     }
117   }
118   return fd;
119 }
120
121 inline int pkgArchiveProcessor::SetOutputStream( const char *name, int mode )
122 {
123   /* Wrapper method to facilitate the set up of output streams
124    * for writing extracted content to disk, except in the special
125    * case where saving of files has been disabled.
126    */
127   return save_on_extract ? create_output_stream( name, mode ) : -2;
128 }
129
130 int pkgArchiveProcessor::ExtractFile( int fd, const char *pathname, int status )
131 {
132   /* Helper method to finalise extraction of archived file entities;
133    * called by the ProcessDataStream() method of the extractor class,
134    * where "fd" is the file descriptor for the extraction data stream,
135    * "pathname" is the corresponding path where the data is extracted,
136    * and "status" is the result of calling the ProcessEntityData()
137    * method of the extractor class on "fd".
138    */
139   if( fd >= 0 )
140   {
141     /* File stream was written; close it...
142      */
143     close( fd );
144     if( status != 0 )
145     {
146       /* The target file was not successfully and completely
147        * written; discard it, and diagnose failure.
148        */
149       unlink( pathname );
150       dmh_notify_extraction_failed( pathname );
151       switch( status )
152       {
153         case TAR_ARCHIVE_DATA_READ_ERROR:
154           dmh_notify_archive_data_exhausted( "content" );
155           break;
156
157         case TAR_ARCHIVE_DATA_WRITE_ERROR:
158           dmh_notify( DMH_ERROR, "write error extracting file content\n" );
159           break;
160
161         default:
162           dmh_notify( DMH_ERROR, "unexpected fault; status = %d\n", status );
163       }
164     }
165   }
166   /* Finally, we pass either the original status value, or the
167    * failing file descriptor as an effective status, if no file
168    * could be extracted, back to the caller.
169    */
170   return (fd == -1) ? fd : status;
171 }
172
173 /*******************
174  *
175  * Class Implementation: pkgTarArchiveProcessor
176  *
177  */
178 #if IMPLEMENTATION_LEVEL == PACKAGE_BASE_COMPONENT
179 /*
180  * The GUI setup tool will provide a simplified substitute for
181  * this constructor.
182  */
183 pkgTarArchiveProcessor::pkgTarArchiveProcessor( pkgXmlNode *pkg )
184 {
185   /* Constructor to associate a package tar archive with its
186    * nominated sysroot and respective installation directory path,
187    * and prepare it for processing, using an appropriate streaming
188    * decompression filter; (choice of filter is based on archive
189    * file name extension; file names are restricted to the
190    * POSIX Portable Character Set).
191    *
192    * First, we anticipate an invalid initialisation state...
193    */
194   sysroot_len = 0;
195
196   sysroot = NULL;
197   sysroot_path = NULL;
198   installed = NULL;
199   stream = NULL;
200
201   /* The 'pkg' XML database entry must be non-NULL, must
202    * represent a package release, and must specify a canonical
203    * tarname to identify the package...
204    */
205   if( ((origin = pkg) != NULL) && pkg->IsElementOfType( release_key )
206   &&  ((tarname = pkg->GetPropVal( tarname_key, NULL )) != NULL)       )
207   {
208     /* When these pre-conditions are satisfied, we may proceed
209      * to identify and locate the sysroot record with which this
210      * package is to be associated...
211      */
212     pkgSpecs lookup( pkgfile = tarname );
213     if( (sysroot = pkg->GetSysRoot( lookup.GetSubSystemName() )) != NULL )
214     {
215       /* Having located the requisite sysroot record, we may
216        * retrieve its specified installation path prefix...
217        */
218       const char *prefix;
219       if( (prefix = sysroot->GetPropVal( pathname_key, NULL )) != NULL )
220       {
221         /* ...and incorporate it into a formatting template
222          * for use in deriving the full path names for files
223          * which are installed from this package.
224          */
225         const char *template_format = "%F%%/M/%%F";
226         char template_text[mkpath( NULL, template_format, prefix, NULL )];
227         mkpath( template_text, template_format, prefix, NULL );
228         sysroot_len = mkpath( NULL, template_text, "", NULL ) - 1;
229         sysroot_path = strdup( template_text );
230       }
231     }
232     /* Some older packages don't use the canonical tarname
233      * for the archive file name; identify the real file name
234      * associated with such packages...
235      */
236     pkgfile = pkg->ArchiveName();
237
238     /* Finally, initialise the data stream which we will use
239      * for reading the package content.
240      */
241     const char *archive_path_template = pkgArchivePath();
242     char archive_path_name[mkpath( NULL, archive_path_template, pkgfile, NULL )];
243     mkpath( archive_path_name, archive_path_template, pkgfile, NULL );
244     stream = pkgOpenArchiveStream( archive_path_name );
245   }
246 }
247
248 pkgTarArchiveProcessor::~pkgTarArchiveProcessor()
249 {
250   /* Destructor must release the heap memory allocated in
251    * the constructor, (by strdup and pkgManifest), clean up
252    * the decompression filter state, and close the archive
253    * data stream.
254    */
255   free( (void *)(sysroot_path) );
256   delete installed;
257   delete stream;
258 }
259
260 #endif /* PACKAGE_BASE_COMPONENT */
261
262 int pkgTarArchiveProcessor::ProcessLinkedEntity( const char *pathname )
263 {
264   /* FIXME: Win32 links need special handling; for hard links, we
265    * may be able to create them directly, with >= Win2K and NTFS;
266    * for symlinks on *all* Win32 variants, and for hard links on
267    * FAT32 or Win9x, we need to make physical copies of the source
268    * file, at the link target location.
269    *
270    * For now, we simply ignore links.
271    */
272   dmh_printf(
273       "FIXME:ProcessLinkedEntity<stub>:Ignoring link: %s --> %s\n",
274        pathname, header.field.linkname
275     );
276   return 0;
277 }
278
279 static
280 uint64_t compute_octval( const char *p, size_t len )
281 # define octval( FIELD ) compute_octval( FIELD, sizeof( FIELD ) )
282 {
283   /* Helper to convert the ASCII representation of octal values,
284    * (as recorded within tar archive header fields), to their actual
285    * numeric values, ignoring leading or trailing garbage.
286    */
287   uint64_t value = 0LL;
288
289   while( (len > 0) && ((*p < '0') || (*p > '7')) )
290   {
291     /* Step over leading garbage.
292      */
293     ++p; --len;
294   }
295   while( (len > 0) && (*p >= '0') && (*p < '8') )
296   {
297     /* Accumulate octal digits; (each represents exactly three
298      * bits in the accumulated value), until we either exhaust
299      * the width of the field, or we encounter trailing junk.
300      */
301     value = (value << 3) + *p++ - '0'; --len;
302   }
303   return value;
304 }
305
306 int pkgTarArchiveProcessor::GetArchiveEntry()
307 {
308   /* Read header for next available entry in the tar archive;
309    * check for end-of-archive mark, (all zero header); verify
310    * checksum for active entry.
311    */
312   char *buf = header.aggregate;
313   size_t count = stream->Read( buf, sizeof( header ) );
314
315   if( count < sizeof( header ) )
316   {
317     /* Failed to read a complete header; diagnose and return error code.
318      */
319     dmh_notify_archive_data_exhausted( "header" );
320     return TAR_ARCHIVE_DATA_READ_ERROR;
321   }
322
323   while( count-- )
324     /*
325      * Outer loop checks for an all zero header...
326      */
327     if( *buf++ != '\0' )
328     {
329       /* Any non-zero byte transfers control to an inner loop,
330        * to rescan the entire header, accumulating its checksum...
331        */
332       uint64_t sum = 0;
333       for( buf = header.aggregate, count = sizeof( header ); count--; ++buf )
334       {
335         if( (buf < header.field.chksum) || (buf >= header.field.typeflag) )
336           /*
337            * ...counting the actual binary value of each byte,
338            * in all but the checksum field itself...
339            */
340           sum += *buf;
341         else
342           /* ...while treating each byte within the checksum field as
343            * having an effective value equivalent to ASCII <space>.
344            */
345           sum += 0x20;
346       }
347       /* After computing the checksum for a non-zero header,
348        * verify it against the value recorded in the checksum field;
349        * return +1 for a successful match...
350        */
351       if( sum == octval( header.field.chksum ) )
352         return 1;
353
354       /* ...otherwise diagnose checksum validation failure, and
355        * return the fault status.
356        */
357       dmh_notify( DMH_ERROR, "checksum validation failed\n" );
358       return TAR_ARCHIVE_FORMAT_ERROR;
359     }
360
361   /* If we get to here, then the inner loop was never entered;
362    * the outer loop has completed, confirming an all zero header;
363    * return zero, to indicate end of archive.
364    */
365   return 0;
366 }
367
368 int pkgTarArchiveProcessor::Process()
369 {
370   /* Generic method for reading tar archives, and extracting their
371    * content; loops over each archive entry in turn...
372    */
373   int status;
374   while( (status = GetArchiveEntry()) > 0 )
375   {
376     char *prefix = *header.field.prefix ? header.field.prefix : NULL;
377     char *name = header.field.name;
378
379     /* Handle the GNU long name header format. 
380      * If the pathname overflows the name field, GNU tar creates a special
381      * entry type, where the data contains the full pathname for the
382      * following entry.
383      */
384     char *longname = NULL;
385     if( *header.field.typeflag == TAR_ENTITY_TYPE_GNU_LONGNAME )
386     {
387       /* Extract the full pathname from the data of this entry.
388        */
389       if( (longname = EntityDataAsString()) == NULL )
390       {
391         dmh_notify( DMH_ERROR, "Unable to read a long name entry\n" );
392         return TAR_ARCHIVE_FORMAT_ERROR;
393       }
394
395       /* Read the entry for which this long name is intended.
396        */
397       if( GetArchiveEntry() <= 0 )
398       {
399         dmh_notify( DMH_ERROR, "Expected a new entry after a long name entry\n" );
400         return TAR_ARCHIVE_FORMAT_ERROR;
401       }
402
403       /* Use the previously determined long name as the pathname for this entry.
404        */
405       prefix = NULL;
406       name = longname;
407     }
408
409     /* Found an archive entry; map it to an equivalent file system
410      * path name, within the designated sysroot hierarchy.
411      */
412     char pathname[mkpath( NULL, sysroot_path, name, prefix )];
413     mkpath( pathname, sysroot_path, name, prefix );
414
415     free( longname );
416
417     /* Direct further processing to the appropriate handler; (this
418      * is specific to the archive entry classification)...
419      */
420     switch( *header.field.typeflag )
421     {
422       int status;
423       
424       case TAR_ENTITY_TYPE_DIRECTORY:
425         /*
426          * We may need to take some action in respect of directories;
427          * e.g. we may need to create a directory, or even a sequence
428          * of directories, to establish a location within the sysroot
429          * hierarchy...
430          */
431          { /* Note: Microsoft's implementation of stat() appears to choke
432             * on directory path names with trailing slashes; thus, before
433             * we invoke the directory processing routine, (which may need
434             * to call stat(), to check if the specified directory already
435             * exists), we remove any such trailing slashes.
436             */
437            char *p = pathname + sizeof( pathname ) - 1;
438            while( (p > pathname) && ((*--p == '/') || (*p == '\\')) )
439              *p = '\0';
440          }
441
442         /* We are now ready to process the directory path name entry...
443          */
444         status = ProcessDirectory( pathname );
445         break;
446
447       case TAR_ENTITY_TYPE_LINK:
448       case TAR_ENTITY_TYPE_SYMLINK:
449         /*
450          * Links ultimately represent file system entities in
451          * our sysroot hierarchy, but we need special processing
452          * to handle them correctly...
453          *
454          */
455         status = ProcessLinkedEntity( pathname );
456         break;
457
458       case TAR_ENTITY_TYPE_FILE:
459       case TAR_ENTITY_TYPE_ALTFILE:
460         /*
461          * These represent regular files; the file content is
462          * embedded within the archive stream, so we need to be
463          * prepared to read or copy it, as appropriate...
464          *
465          */
466         ProcessDataStream( pathname );
467         break;
468
469       default:
470         /* FIXME: we make no provision for handling any other
471          * type of archive entry; we should provide some more
472          * robust error handling, but for now we simply emit
473          * a diagnostic, and return an error condition code...
474          *
475          */
476         dmh_notify( DMH_ERROR,
477             "unexpected archive entry classification: type %d\n",
478             (int)(*header.field.typeflag)
479           );
480         return -1;
481     }
482   }
483   /* If we didn't bail out before getting to here, then the archive
484    * was processed successfully; return the success code.
485    */
486   return 0;
487 }
488
489 int pkgTarArchiveProcessor::ProcessEntityData( int fd )
490 {
491   /* Generic method for reading past the data associated with
492    * a specific header within a tar archive; if given a negative
493    * value for `fd', it will simply skip over the data, otherwise
494    * `fd' is assumed to represent a descriptor for an opened file
495    * stream, to which the data will be copied (extracted).
496    */
497    int status = 0;
498
499   /* Initialise a counter for the length of the data content, and
500    * specify the default size for the transfer buffer in which to
501    * process it; make the initial size of the transfer buffer 16
502    * times the header size.
503    */
504   uint64_t bytes_to_copy = octval( header.field.size );
505   size_t block_size = sizeof( header ) << 4;
506
507   /* While we still have unread data, and no processing error...
508    */
509   while( (bytes_to_copy > 0) && (status == 0) )
510   {
511     /* Adjust the requested size for the transfer buffer, shrinking
512      * it by 50% at each step, until it is smaller than the remaining
513      * data length, but never smaller than the header record length.
514      */
515     while( (bytes_to_copy < block_size) && (block_size > sizeof( header )) )
516       block_size >>= 1;
517
518     /* Allocate a transfer buffer of the requested size, and populate
519      * it, by reading data from the archive; (since the transfer buffer
520      * is never smaller than the header length, this will also capture
521      * any additional padding bytes, which may be required to keep the
522      * data length equal to an exact multiple of the header length).
523      */
524     char buffer[block_size];
525     if( stream->Read( buffer, block_size ) < (int)(block_size) )
526       /*
527        * Failure to fully populate the transfer buffer, (i.e. a short
528        * read), indicates a corrupt archive; bail out immediately.
529        */
530       return TAR_ARCHIVE_DATA_READ_ERROR;
531
532     /* When the number of actual data bytes expected is fewer than the
533      * total number of bytes in the transfer buffer...
534      */
535     if( bytes_to_copy < block_size )
536       /*
537        * ...then we have reached the end of the data for the current
538        * archived entity; adjust the block size to reflect the number
539        * of actual data bytes present in the transfer buffer...
540        */
541       block_size = bytes_to_copy;
542
543     /* With the number of actual data bytes present now accurately
544      * reflected by the block size, we save that data to the stream
545      * specified for archive extraction, (if any).
546      */
547     if( (fd >= 0) && (write( fd, buffer, block_size ) != (int)(block_size)) )
548       /*
549        * An extraction error occurred; set the status code to
550        * indicate failure.
551        */
552       status = TAR_ARCHIVE_DATA_WRITE_ERROR;
553
554     /* Adjust the count of remaining unprocessed data bytes, and begin
555      * a new processing cycle, to capture any which may be present.
556      */
557     bytes_to_copy -= block_size;
558   }
559
560   /* Finally, when all data for the current archive entry has been
561    * processed, we return to the caller with an appropriate completion
562    * status code.
563    */
564   return status;
565 }
566
567 char *pkgTarArchiveProcessor::EntityDataAsString()
568 {
569   /* Read the data associated with a specific header within a tar archive
570    * and return it as a string.  The return value is stored in memory which
571    * is allocated by malloc; it should be freed when no longer required.
572    *
573    * It is assumed that the return data can be accommodated within available
574    * heap memory.  Since the length isn't returned, we assume that the string
575    * is NUL-terminated, and that it contains no embedded NULs.
576    *
577    * In the event of any error, NULL is returned.
578    */
579   char *data;
580   uint64_t bytes_to_copy = octval( header.field.size );
581   
582   /* Round the buffer size to the smallest multiple of the record size.
583    */
584   bytes_to_copy += sizeof( header ) - 1;
585   bytes_to_copy -= bytes_to_copy % sizeof( header );
586
587   /* Allocate the data buffer.
588    */
589   data = (char*)(malloc( bytes_to_copy ));
590   if( !data )
591     return NULL;
592   
593   /* Read the data into the buffer.
594    */
595   size_t count = stream->Read( data, bytes_to_copy );
596   if( count < bytes_to_copy )
597   {
598     /* Failure to fully populate the transfer buffer, (i.e. a short
599      * read), indicates a corrupt archive.
600      */
601     free( data );
602     return NULL;
603   }
604   return data;
605 }
606
607 /*******************
608  *
609  * Class Implementation: pkgTarArchiveExtractor
610  *
611  */
612 #include <utime.h>
613
614 EXTERN_C int have_api( const char *, const char * = NULL );
615
616 static inline int have_utime64_api( void )
617 {
618   /* Local helper function to check and record the availability of
619    * the _utime64() API function, within the particular version of
620    * MSVCRT.DLL which is installed on the host platform.
621    */
622   enum { API_UNSUPPORTED = 0, API_SUPPORTED, API_UNTESTED };
623
624   /* On first call, we don't know; initialise accordingly.
625    */
626   static int status = (int)(API_UNTESTED);
627
628   return (status == (int)(API_UNTESTED))
629     /*
630      * Must be first time of calling; check, record, and return
631      * the appropriate availability status.
632      */
633     ? status = have_api( "_utime64" )
634     /*
635      * On second and subsequent calls, we've already checked, so
636      * we know the availability status; simply return it.
637      */
638     : status;
639 }
640
641 static int commit_saved_entity( const char *pathname, __time64_t mtime )
642 {
643   /* Helper to set the access and modification times for a file,
644    * after extraction from an archive, to match the specified "mtime";
645    * (typically "mtime" is as recorded within the archive).
646    */
647   if( have_utime64_api() )
648   {
649     /* When the _utime64() API function is available...
650      */
651     struct __utimbuf64 timestamp;
652
653     /* ...we prefer to use it...
654      */
655     timestamp.actime = timestamp.modtime = mtime;
656     return _utime64( pathname, &timestamp );
657   }
658   else
659   {
660     /* ...otherwise, we assume that this is a legacy system,
661      * and the utime() function is based on 32-bit time_t...
662      */
663     struct __utimbuf32 timestamp;
664
665     /* ...so fall back to using that.
666      */
667     timestamp.actime = timestamp.modtime = mtime;
668     return utime( pathname, (utimbuf *)(&timestamp) );
669   }
670 }
671
672 pkgTarArchiveExtractor::pkgTarArchiveExtractor( const char *fn, const char *dir )
673 {
674   /* A simplified variation on the installer theme; this extracts
675    * the tar archive named by "fn" into any arbitrarily chosen path,
676    * specified by "dir", without creating an installation record.
677    *
678    * The extractor uses a specialised constructor; however, we
679    * begin by initialising as for the general case.
680    */
681   sysroot_len = 0;
682
683   sysroot = NULL;
684   sysroot_path = NULL;
685   installed = NULL;
686   stream = NULL;
687
688   /* When an explicit extraction path name is specified...
689    */
690   if( dir != NULL )
691   {
692     /* ...then set up the template which the extractor will use
693      * to generate path names for each extracted file entity...
694      */
695     const char *template_format = "%F%%/M/%%F";
696     char template_text[mkpath( NULL, template_format, dir, NULL )];
697     mkpath( template_text, template_format, dir, NULL );
698
699     /* ...suborning the sysroot_len and sysroot_path properties
700      * to pass it to the extraction methods.
701      */
702     sysroot_len = mkpath( NULL, template_text, "", NULL ) - 1;
703     sysroot_path = strdup( template_text );
704   }
705   /* Finally, open the specified archive using the appropriate
706    * stream type, and invoke the extraction Process() method.
707    */
708   stream = pkgOpenArchiveStream( fn );
709   Process();
710 }
711
712 int pkgTarArchiveExtractor::ProcessDirectory( const char *pathname )
713 {
714   /* We are obliged to provide an implementation for this method,
715    * since the base class declares it as abstract; in this instance,
716    * delegation to a real base class method suffices.
717    */
718   return CreateExtractionDirectory( pathname );
719 }
720
721 int pkgTarArchiveExtractor::ProcessDataStream( const char *pathname )
722 {
723   /* Also declared as abstract in the base class, in this case
724    * we must set up the output stream, and initiate entity data
725    * processing on behalf of the base class ExtractFile() method..
726    */
727   int status;
728   int fd = SetOutputStream( pathname, octval( header.field.mode ) );
729   if( (status = ExtractFile( fd, pathname, ProcessEntityData( fd ))) == 0 )
730     if( save_on_extract )
731       /*
732        * ...and commit the file after successful extraction...
733        */
734       commit_saved_entity( pathname, octval( header.field.mtime ) );
735
736   /* ...ultimately returning the extraction status code.
737    */
738   return status;
739 }
740
741 #if IMPLEMENTATION_LEVEL == PACKAGE_BASE_COMPONENT
742
743 /*******************
744  *
745  * Class Implementation: pkgTarArchiveInstaller
746  *
747  */
748 pkgTarArchiveInstaller::
749 pkgTarArchiveInstaller( pkgXmlNode *pkg ):pkgTarArchiveProcessor( pkg )
750 {
751   /* Constructor: having successfully set up the pkgTarArchiveProcessor
752    * base class, we attach a pkgManifest to track the installation.
753    */
754   if( (tarname != NULL) && (sysroot != NULL) && stream->IsReady() )
755     installed = new pkgManifest( package_key, tarname );
756 }
757
758 int pkgTarArchiveInstaller::Process()
759 {
760   /* Specialisation of the base class Process() method.
761    */
762   int status;
763   /* First, process the archive as for the base class...
764    */
765   if( (status = pkgTarArchiveProcessor::Process()) == 0 )
766   {
767     /* ...then, on successful completion...
768      *
769      * Update the package installation manifest, to record
770      * the installation in the current sysroot...
771      */
772     installed->BindSysRoot( sysroot, package_key );
773     pkgRegister( sysroot, origin, tarname, pkgfile );
774   }
775   return status;
776 }
777
778 int pkgTarArchiveInstaller::ProcessDirectory( const char *pathname )
779 {
780   /* Create the directory infrastructure required to support
781    * a specific package installation.
782    */
783   int status = 0;
784   if( DEBUG_REQUEST( DEBUG_SUPPRESS_INSTALLATION ) )
785   {
786     /* Debugging stub...
787      * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
788      */
789     dmh_printf(
790         "FIXME:ProcessDirectory<stub>:not executing: mkdir -p %s\n",
791          pathname
792       );
793     if( DEBUG_REQUEST( DEBUG_UPDATE_INVENTORY ) )
794       /*
795        * Although no installation directory has actually been created,
796        * update the inventory to simulate the effect of doing so.
797        */
798       installed->AddEntry( dirname_key, pathname + sysroot_len );
799   }
800   else
801   {
802     if( (status = CreateExtractionDirectory( pathname )) == 0 )
803       /*
804        * Either the specified directory already exists,
805        * or we just successfully created it; attach a reference
806        * in the installation manifest for the current package.
807        */
808       installed->AddEntry( dirname_key, pathname + sysroot_len );
809   }
810   return status;
811 }
812
813 int pkgTarArchiveInstaller::ProcessDataStream( const char *pathname )
814 {
815   /* Extract file data from the archive, and copy it to the
816    * associated target file stream, if any.
817    */
818   pkgSpinWait::Report( "Extracting %s", pathname + sysroot_len );
819   if( DEBUG_REQUEST( DEBUG_SUPPRESS_INSTALLATION ) )
820   {
821     /* Debugging stub...
822      * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
823      */
824     dmh_printf(
825         "FIXME:ProcessDataStream<stub>:not extracting: %s\n",
826         pathname
827       );
828     if( DEBUG_REQUEST( DEBUG_UPDATE_INVENTORY ) )
829       /*
830        * Although no file has actually been installed, update
831        * the inventory to simulate the effect of doing so.
832        */
833       installed->AddEntry( filename_key, pathname + sysroot_len );
834
835     return ProcessEntityData( -1 );
836   }
837   else
838   {
839     int status;
840
841     /* Establish an output file stream, extract the entity data,
842      * writing it to this stream...
843      */
844     int fd = SetOutputStream( pathname, octval( header.field.mode ) );
845     if( (status = ExtractFile( fd, pathname, ProcessEntityData( fd ))) == 0 )
846     {
847       /* ...and on successful completion, commit the file
848        * and record it in the installation database.
849        */
850       if( save_on_extract )
851         commit_saved_entity( pathname, octval( header.field.mtime ) );
852       installed->AddEntry( filename_key, pathname + sysroot_len );
853
854       /* Additionally, when the appropriate level of debug
855        * tracing has been enabled, report the installation of
856        * this file to the diagnostic log.
857        *
858        * FIXME: this would be a good place to add reporting
859        * of installation, in verbose execution mode.
860        */
861       DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_TRANSACTIONS ),
862           dmh_printf( "  %s\n", pathname )
863         );
864     }
865     return status;
866   }
867 }
868
869 #endif /* PACKAGE_BASE_COMPONENT */
870
871 /* $RCSfile$: end of file */