OSDN Git Service

Implement "Discard" handler for GUI "Apply Changes" dialogue.
[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, MinGW 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 <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <errno.h>
38
39 #include "dmh.h"
40 #include "debug.h"
41 #include "mkpath.h"
42
43 #include "pkginfo.h"
44 #include "pkgkeys.h"
45 #include "pkgproc.h"
46
47 /*******************
48  *
49  * Class Implementation: pkgArchiveProcessor
50  *
51  */
52 int pkgArchiveProcessor::CreateExtractionDirectory( const char *pathname )
53 {
54   /* Helper method for creation of the directory infrastructure
55    * into which archived file entities are to be extracted.
56    */
57   int status;
58   if( (status = mkdir_recursive( pathname, 0755 )) != 0 )
59     dmh_notify( DMH_ERROR, "cannot create directory `%s'\n", pathname );
60   return status;
61 }
62
63 int pkgArchiveProcessor::ExtractFile( int fd, const char *pathname, int status )
64 {
65   /* Helper method to finalise extraction of archived file entities;
66    * called by the ProcessDataStream() method of the extractor class,
67    * where "fd" is the file descriptor for the extraction data stream,
68    * "pathname" is the corresponding path wher the data is extracted,
69    * and "status" is the result of calling the ProcessEntityData()
70    * method of the extractor class on "fd".
71    */
72   if( fd >= 0 )
73   {
74     /* File stream was written; close it...
75      */
76     close( fd );
77     if( status != 0 )
78     {
79       /* The target file was not successfully and completely
80        * written; discard it, and diagnose failure.
81        */
82       unlink( pathname );
83       dmh_notify( DMH_ERROR, "%s: extraction failed\n", pathname );
84     }
85   }
86   /* Finally, we pass the original status value back to the caller.
87    */
88   return status;
89 }
90
91 /*******************
92  *
93  * Class Implementation: pkgTarArchiveProcessor
94  *
95  */
96 pkgTarArchiveProcessor::pkgTarArchiveProcessor( pkgXmlNode *pkg )
97 {
98   /* Constructor to associate a package tar archive with its
99    * nominated sysroot and respective installation directory path,
100    * and prepare it for processing, using an appropriate streaming
101    * decompression filter; (choice of filter is based on archive
102    * file name extension; file names are restricted to the
103    * POSIX Portable Character Set).
104    *
105    * First, we anticipate an invalid initialisation state...
106    */
107   sysroot_len = 0;
108
109   sysroot = NULL;
110   sysroot_path = NULL;
111   installed = NULL;
112   stream = NULL;
113
114   /* The 'pkg' XML database entry must be non-NULL, must
115    * represent a package release, and must specify a canonical
116    * tarname to identify the package...
117    */
118   if( ((origin = pkg) != NULL) && pkg->IsElementOfType( release_key )
119   &&  ((tarname = pkg->GetPropVal( tarname_key, NULL )) != NULL)       )
120   {
121     /* When these pre-conditions are satisfied, we may proceed
122      * to identify and locate the sysroot record with which this
123      * package is to be associated...
124      */
125     pkgSpecs lookup( pkgfile = tarname );
126     if( (sysroot = pkg->GetSysRoot( lookup.GetSubSystemName() )) != NULL )
127     {
128       /* Having located the requisite sysroot record, we may
129        * retrieve its specified installation path prefix...
130        */
131       const char *prefix;
132       if( (prefix = sysroot->GetPropVal( pathname_key, NULL )) != NULL )
133       {
134         /* ...and incorporate it into a formatting template
135          * for use in deriving the full path names for files
136          * which are installed from this package.
137          */
138         const char *template_format = "%F%%/M/%%F";
139         char template_text[mkpath( NULL, template_format, prefix, NULL )];
140         mkpath( template_text, template_format, prefix, NULL );
141         sysroot_len = mkpath( NULL, template_text, "", NULL ) - 1;
142         sysroot_path = strdup( template_text );
143       }
144     }
145     /* Some older packages don't use the canonical tarname
146      * for the archive file name; identify the real file name
147      * associated with such packages...
148      */
149     pkgfile = pkg->ArchiveName();
150
151     /* Finally, initialise the data stream which we will use
152      * for reading the package content.
153      */
154     const char *archive_path_template = pkgArchivePath();
155     char archive_path_name[mkpath( NULL, archive_path_template, pkgfile, NULL )];
156     mkpath( archive_path_name, archive_path_template, pkgfile, NULL );
157     stream = pkgOpenArchiveStream( archive_path_name );
158   }
159 }
160
161 pkgTarArchiveProcessor::~pkgTarArchiveProcessor()
162 {
163   /* Destructor must release the heap memory allocated in
164    * the constructor, (by strdup and pkgManifest), clean up
165    * the decompression filter state, and close the archive
166    * data stream.
167    */
168   free( (void *)(sysroot_path) );
169   delete installed;
170   delete stream;
171 }
172
173 int pkgTarArchiveProcessor::ProcessLinkedEntity( const char *pathname )
174 {
175   /* FIXME: Win32 links need special handling; for hard links, we
176    * may be able to create them directly, with >= Win2K and NTFS;
177    * for symlinks on *all* Win32 variants, and for hard links on
178    * FAT32 or Win9x, we need to make physical copies of the source
179    * file, at the link target location.
180    *
181    * For now, we simply ignore links.
182    */
183   dmh_printf(
184       "FIXME:ProcessLinkedEntity<stub>:Ignoring link: %s --> %s\n",
185        pathname, header.field.linkname
186     );
187   return 0;
188 }
189
190 static
191 uint64_t compute_octval( const char *p, size_t len )
192 # define octval( FIELD ) compute_octval( FIELD, sizeof( FIELD ) )
193 {
194   /* Helper to convert the ASCII representation of octal values,
195    * (as recorded within tar archive header fields), to their actual
196    * numeric values, ignoring leading or trailing garbage.
197    */
198   uint64_t value = 0LL;
199
200   while( (len > 0) && ((*p < '0') || (*p > '7')) )
201   {
202     /* Step over leading garbage.
203      */
204     ++p; --len;
205   }
206   while( (len > 0) && (*p >= '0') && (*p < '8') )
207   {
208     /* Accumulate octal digits; (each represents exactly three
209      * bits in the accumulated value), until we either exhaust
210      * the width of the field, or we encounter trailing junk.
211      */
212     value = (value << 3) + *p++ - '0'; --len;
213   }
214   return value;
215 }
216
217 int pkgTarArchiveProcessor::GetArchiveEntry()
218 {
219   /* Read header for next available entry in the tar archive;
220    * check for end-of-archive mark, (all zero header); verify
221    * checksum for active entry.
222    */
223   char *buf = header.aggregate;
224   size_t count = stream->Read( buf, sizeof( header ) );
225
226   if( count < sizeof( header ) )
227   {
228     /* Failed to read a complete header; return error code.
229      */
230     return -1;
231   }
232
233   while( count-- )
234     /*
235      * Outer loop checks for an all zero header...
236      */
237     if( *buf++ != '\0' )
238     {
239       /* Any non-zero byte transfers control to an inner loop,
240        * to rescan the entire header, accumulating its checksum...
241        */
242       uint64_t sum = 0;
243       for( buf = header.aggregate, count = sizeof( header ); count--; ++buf )
244       {
245         if( (buf < header.field.chksum) || (buf >= header.field.typeflag) )
246           /*
247            * ...counting the actual binary value of each byte,
248            * in all but the checksum field itself...
249            */
250           sum += *buf;
251         else
252           /* ...while treating each byte within the checksum field as
253            * having an effective value equivalent to ASCII <space>.
254            */
255           sum += 0x20;
256       }
257       /* After computing the checksum for a non-zero header,
258        * verify it against the value recorded in the checksum field;
259        * return +1 for a successful match, or -2 for failure.
260        */
261       return (sum == octval( header.field.chksum )) ? 1 : -2;
262     }
263
264   /* If we get to here, then the inner loop was never entered;
265    * the outer loop has completed, confirming an all zero header;
266    * return zero, to indicate end of archive.
267    */
268   return 0;
269 }
270
271 int pkgTarArchiveProcessor::Process()
272 {
273   /* Generic method for reading tar archives, and extracting their
274    * content; loops over each archive entry in turn...
275    */
276   while( GetArchiveEntry() > 0 )
277   {
278     char *prefix = *header.field.prefix ? header.field.prefix : NULL;
279     char *name = header.field.name;
280
281     /* Handle the GNU long name header format. 
282      * If the pathname overflows the name field, GNU tar creates a special
283      * entry type, where the data contains the full pathname for the
284      * following entry.
285      */
286     char *longname = NULL;
287     if( *header.field.typeflag == TAR_ENTITY_TYPE_GNU_LONGNAME )
288     {
289       /* Extract the full pathname from the data of this entry.
290        */
291       longname = EntityDataAsString();
292       if( !longname )
293         dmh_notify( DMH_ERROR, "Unable to read a long name entry\n" );
294
295       /* Read the entry for which this long name is intended.
296        */
297       if( GetArchiveEntry() <= 0 )
298         dmh_notify( DMH_ERROR, "Expected a new entry after a long name entry\n" );
299
300       /* Use the previously determined long name as the pathname for this entry.
301        */
302       prefix = NULL;
303       name = longname;
304     }
305
306     /* Found an archive entry; map it to an equivalent file system
307      * path name, within the designated sysroot hierarchy.
308      */
309     char pathname[mkpath( NULL, sysroot_path, name, prefix )];
310     mkpath( pathname, sysroot_path, name, prefix );
311
312     free( longname );
313
314     /* Direct further processing to the appropriate handler; (this
315      * is specific to the archive entry classification)...
316      */
317     switch( *header.field.typeflag )
318     {
319       int status;
320       
321       case TAR_ENTITY_TYPE_DIRECTORY:
322         /*
323          * We may need to take some action in respect of directories;
324          * e.g. we may need to create a directory, or even a sequence
325          * of directories, to establish a location within the sysroot
326          * hierarchy...
327          */
328          { /* Note: Microsoft's implementation of stat() appears to choke
329             * on directory path names with trailing slashes; thus, before
330             * we invoke the directory processing routine, (which may need
331             * to call stat(), to check if the specified directory already
332             * exists), we remove any such trailing slashes.
333             */
334            char *p = pathname + sizeof( pathname ) - 1;
335            while( (p > pathname) && ((*--p == '/') || (*p == '\\')) )
336              *p = '\0';
337          }
338
339         /* We are now ready to process the directory path name entry...
340          */
341         status = ProcessDirectory( pathname );
342         break;
343
344       case TAR_ENTITY_TYPE_LINK:
345       case TAR_ENTITY_TYPE_SYMLINK:
346         /*
347          * Links ultimately represent file system entities in
348          * our sysroot hierarchy, but we need special processing
349          * to handle them correctly...
350          *
351          */
352         status = ProcessLinkedEntity( pathname );
353         break;
354
355       case TAR_ENTITY_TYPE_FILE:
356       case TAR_ENTITY_TYPE_ALTFILE:
357         /*
358          * These represent regular files; the file content is
359          * embedded within the archive stream, so we need to be
360          * prepared to read or copy it, as appropriate...
361          *
362          */
363         ProcessDataStream( pathname );
364         break;
365
366       default:
367         /* FIXME: we make no provision for handling any other
368          * type of archive entry; we should provide some more
369          * robust error handling, but for now we simply emit
370          * a diagnostic, and return an error condition code...
371          *
372          */
373         dmh_notify( DMH_ERROR,
374             "unexpected archive entry classification: type %d\n",
375             (int)(*header.field.typeflag)
376           );
377         return -1;
378     }
379   }
380   /* If we didn't bail out before getting to here, then the archive
381    * was processed successfully; return the success code.
382    */
383   return 0;
384 }
385
386 int pkgTarArchiveProcessor::ProcessEntityData( int fd )
387 {
388   /* Generic method for reading past the data associated with
389    * a specific header within a tar archive; if given a negative
390    * value for `fd', it will simply skip over the data, otherwise
391    * `fd' is assumed to represent a descriptor for an opened file
392    * stream, to which the data will be copied (extracted).
393    */
394    int status = 0;
395
396   /* Initialise a counter for the length of the data content, and
397    * specify the default size for the transfer buffer in which to
398    * process it; make the initial size of the transfer buffer 16
399    * times the header size.
400    */
401   uint64_t bytes_to_copy = octval( header.field.size );
402   size_t block_size = sizeof( header ) << 4;
403
404   /* While we still have unread data, and no processing error...
405    */
406   while( (bytes_to_copy > 0) && (status == 0) )
407   {
408     /* Adjust the requested size for the transfer buffer, shrinking
409      * it by 50% at each step, until it is smaller than the remaining
410      * data length, but never smaller than the header record length.
411      */
412     while( (bytes_to_copy < block_size) && (block_size > sizeof( header )) )
413       block_size >>= 1;
414
415     /* Allocate a transfer buffer of the requested size, and populate
416      * it, by reading data from the archive; (since the transfer buffer
417      * is never smaller than the header length, this will also capture
418      * any additional padding bytes, which may be required to keep the
419      * data length equal to an exact multiple of the header length).
420      */
421     char buffer[block_size];
422     if( stream->Read( buffer, block_size ) < (int)(block_size) )
423       /*
424        * Failure to fully populate the transfer buffer, (i.e. a short
425        * read), indicates a corrupt archive; bail out immediately.
426        */
427       return -1;
428
429     /* When the number of actual data bytes expected is fewer than the
430      * total number of bytes in the transfer buffer...
431      */
432     if( bytes_to_copy < block_size )
433       /*
434        * ...then we have reached the end of the data for the current
435        * archived entity; adjust the block size to reflect the number
436        * of actual data bytes present in the transfer buffer...
437        */
438       block_size = bytes_to_copy;
439
440     /* With the number of actual data bytes present now accurately
441      * reflected by the block size, we save that data to the stream
442      * specified for archive extraction, (if any).
443      */
444     if( (fd >= 0) && (write( fd, buffer, block_size ) != (int)(block_size)) )
445       /*
446        * An extraction error occurred; set the status code to
447        * indicate failure.
448        */
449       status = -2;
450
451     /* Adjust the count of remaining unprocessed data bytes, and begin
452      * a new processing cycle, to capture any which may be present.
453      */
454     bytes_to_copy -= block_size;
455   }
456
457   /* Finally, when all data for the current archive entry has been
458    * processed, we return to the caller with an appropriate completion
459    * status code.
460    */
461   return status;
462 }
463
464 char *pkgTarArchiveProcessor::EntityDataAsString()
465 {
466   /* Read the data associated with a specific header within a tar archive
467    * and return it as a string.  The return value is stored in memory which
468    * is allocated by malloc; it should be freed when no longer required.
469    *
470    * It is assumed that the return data can be accommodated within available
471    * heap memory.  Since the length isn't returned, we assume that the string
472    * is NUL-terminated, and that it contains no embedded NULs.
473    *
474    * In the event of any error, NULL is returned.
475    */
476   char *data;
477   uint64_t bytes_to_copy = octval( header.field.size );
478   
479   /* Round the buffer size to the smallest multiple of the record size.
480    */
481   bytes_to_copy += sizeof( header ) - 1;
482   bytes_to_copy -= bytes_to_copy % sizeof( header );
483
484   /* Allocate the data buffer.
485    */
486   data = (char*)(malloc( bytes_to_copy ));
487   if( !data )
488     return NULL;
489   
490   /* Read the data into the buffer.
491    */
492   size_t count = stream->Read( data, bytes_to_copy );
493   if( count < bytes_to_copy )
494   {
495     /* Failure to fully populate the transfer buffer, (i.e. a short
496      * read), indicates a corrupt archive.
497      */
498     free( data );
499     return NULL;
500   }
501   return data;
502 }
503
504 /*******************
505  *
506  * Class Implementation: pkgTarArchiveExtractor
507  *
508  */
509 #include <utime.h>
510
511 static int commit_saved_entity( const char *pathname, time_t mtime )
512 {
513   /* Helper to set the access and modification times for a file,
514    * after extraction from an archive, to match the specified "mtime";
515    * (typically "mtime" is as recorded within the archive).
516    */
517   struct utimbuf timestamp;
518
519   timestamp.actime = timestamp.modtime = mtime;
520   return utime( pathname, &timestamp );
521 }
522
523 pkgTarArchiveExtractor::pkgTarArchiveExtractor( const char *fn, const char *dir )
524 {
525   /* A simplified variation on the installer theme; this extracts
526    * the tar archive named by "fn" into any arbitrarily chosen path,
527    * specified by "dir", without creating an installation record.
528    *
529    * The extractor uses a specialised constructor; however, we
530    * begin by initialising as for the general case.
531    */
532   sysroot_len = 0;
533
534   sysroot = NULL;
535   sysroot_path = NULL;
536   installed = NULL;
537   stream = NULL;
538
539   /* When an explicit extraction path name is specified...
540    */
541   if( dir != NULL )
542   {
543     /* ...then set up the template which the extractor will use
544      * to generate path names for each extracted file entity...
545      */
546     const char *template_format = "%F%%/M/%%F";
547     char template_text[mkpath( NULL, template_format, dir, NULL )];
548     mkpath( template_text, template_format, dir, NULL );
549
550     /* ...suborning the sysroot_len and sysroot_path properties
551      * to pass it to the extraction methods.
552      */
553     sysroot_len = mkpath( NULL, template_text, "", NULL ) - 1;
554     sysroot_path = strdup( template_text );
555   }
556
557   /* Finally, open the specified archive using the appropriate
558    * stream type, and invoke the extraction Process() method.
559    */
560   stream = pkgOpenArchiveStream( fn );
561   Process();
562 }
563
564 int pkgTarArchiveExtractor::ProcessDirectory( const char *pathname )
565 {
566   /* We are obliged to provide an implementation for this method,
567    * since the base class declares it as abstract; in this instance,
568    * delegation to a real base class method suffices.
569    */
570   return CreateExtractionDirectory( pathname );
571 }
572
573 int pkgTarArchiveExtractor::ProcessDataStream( const char *pathname )
574 {
575   /* Also declared as abstract in the base class, in this case
576    * we must set up the output stream, and initiate entity data
577    * processing on behalf of the base class ExtractFile() method..
578    */
579   int status;
580   int fd = set_output_stream( pathname, octval( header.field.mode ) );
581   if( (status = ExtractFile( fd, pathname, ProcessEntityData( fd ))) == 0 )
582     /*
583      * ...and commit the file after successful extraction...
584      */
585     commit_saved_entity( pathname, octval( header.field.mtime ) );
586
587   /* ...ultimately returning the extraction status code.
588    */
589   return status;
590 }
591
592 /*******************
593  *
594  * Class Implementation: pkgTarArchiveInstaller
595  *
596  */
597 pkgTarArchiveInstaller::
598 pkgTarArchiveInstaller( pkgXmlNode *pkg ):pkgTarArchiveProcessor( pkg )
599 {
600   /* Constructor: having successfully set up the pkgTarArchiveProcessor
601    * base class, we attach a pkgManifest to track the installation.
602    */
603   if( (tarname != NULL) && (sysroot != NULL) && stream->IsReady() )
604     installed = new pkgManifest( package_key, tarname );
605 }
606
607 int pkgTarArchiveInstaller::Process()
608 {
609   /* Specialisation of the base class Process() method.
610    */
611   int status;
612   /* First, process the archive as for the base class...
613    */
614   if( (status = pkgTarArchiveProcessor::Process()) == 0 )
615   {
616     /* ...then, on successful completion...
617      *
618      * Update the package installation manifest, to record
619      * the installation in the current sysroot...
620      */
621     installed->BindSysRoot( sysroot, package_key );
622     pkgRegister( sysroot, origin, tarname, pkgfile );
623   }
624   return status;
625 }
626
627 int pkgTarArchiveInstaller::ProcessDirectory( const char *pathname )
628 {
629   /* Create the directory infrastructure required to support
630    * a specific package installation.
631    */
632   int status = 0;
633   if( DEBUG_REQUEST( DEBUG_SUPPRESS_INSTALLATION ) )
634   {
635     /* Debugging stub...
636      * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
637      */
638     dmh_printf(
639         "FIXME:ProcessDirectory<stub>:not executing: mkdir -p %s\n",
640          pathname
641       );
642     if( DEBUG_REQUEST( DEBUG_UPDATE_INVENTORY ) )
643       /*
644        * Although no installation directory has actually been created,
645        * update the inventory to simulate the effect of doing so.
646        */
647       installed->AddEntry( dirname_key, pathname + sysroot_len );
648   }
649   else
650   {
651     if( (status = CreateExtractionDirectory( pathname )) == 0 )
652       /*
653        * Either the specified directory already exists,
654        * or we just successfully created it; attach a reference
655        * in the installation manifest for the current package.
656        */
657       installed->AddEntry( dirname_key, pathname + sysroot_len );
658   }
659   return status;
660 }
661
662 int pkgTarArchiveInstaller::ProcessDataStream( const char *pathname )
663 {
664   /* Extract file data from the archive, and copy it to the
665    * associated target file stream, if any.
666    */
667   if( DEBUG_REQUEST( DEBUG_SUPPRESS_INSTALLATION ) )
668   {
669     /* Debugging stub...
670      * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
671      */
672     dmh_printf(
673         "FIXME:ProcessDataStream<stub>:not extracting: %s\n",
674         pathname
675       );
676     if( DEBUG_REQUEST( DEBUG_UPDATE_INVENTORY ) )
677       /*
678        * Although no file has actually been installed, update
679        * the inventory to simulate the effect of doing so.
680        */
681       installed->AddEntry( filename_key, pathname + sysroot_len );
682
683     return ProcessEntityData( -1 );
684   }
685   else
686   {
687     int status;
688
689     /* Establish an output file stream, extract the entity data,
690      * writing it to this stream...
691      */
692     int fd = set_output_stream( pathname, octval( header.field.mode ) );
693     if( (status = ExtractFile( fd, pathname, ProcessEntityData( fd ))) == 0 )
694     {
695         /* ...and on successful completion, commit the file
696          * and record it in the installation database.
697          */
698         commit_saved_entity( pathname, octval( header.field.mtime ) );
699         installed->AddEntry( filename_key, pathname + sysroot_len );
700     }
701     return status;
702   }
703 }
704
705 /* $RCSfile$: end of file */