Home | History | Annotate | Download | only in bdist_wininst
      1 /*
      2   IMPORTANT NOTE: IF THIS FILE IS CHANGED, WININST-6.EXE MUST BE RECOMPILED
      3   WITH THE MSVC6 WININST.DSW WORKSPACE FILE MANUALLY, AND WININST-7.1.EXE MUST
      4   BE RECOMPILED WITH THE MSVC 2003.NET WININST-7.1.VCPROJ FILE MANUALLY.
      5 
      6   IF CHANGES TO THIS FILE ARE CHECKED INTO PYTHON CVS, THE RECOMPILED BINARIES
      7   MUST BE CHECKED IN AS WELL!
      8 */
      9 
     10 #include <windows.h>
     11 
     12 #include "zlib.h"
     13 
     14 #include <stdio.h>
     15 #include <stdarg.h>
     16 
     17 #include "archive.h"
     18 
     19 /* Convert unix-path to dos-path */
     20 static void normpath(char *path)
     21 {
     22     while (path && *path) {
     23         if (*path == '/')
     24             *path = '\\';
     25         ++path;
     26     }
     27 }
     28 
     29 BOOL ensure_directory(char *pathname, char *new_part, NOTIFYPROC notify)
     30 {
     31     while (new_part && *new_part && (new_part = strchr(new_part, '\\'))) {
     32         DWORD attr;
     33         *new_part = '\0';
     34         attr = GetFileAttributes(pathname);
     35         if (attr == -1) {
     36             /* nothing found */
     37             if (!CreateDirectory(pathname, NULL) && notify)
     38                 notify(SYSTEM_ERROR,
     39                        "CreateDirectory (%s)", pathname);
     40             else
     41                 notify(DIR_CREATED, pathname);
     42         }
     43         if (attr & FILE_ATTRIBUTE_DIRECTORY) {
     44             ;
     45         } else {
     46             SetLastError(183);
     47             if (notify)
     48                 notify(SYSTEM_ERROR,
     49                        "CreateDirectory (%s)", pathname);
     50         }
     51         *new_part = '\\';
     52         ++new_part;
     53     }
     54     return TRUE;
     55 }
     56 
     57 /* XXX Should better explicitly specify
     58  * uncomp_size and file_times instead of pfhdr!
     59  */
     60 char *map_new_file(DWORD flags, char *filename,
     61                    char *pathname_part, int size,
     62                    WORD wFatDate, WORD wFatTime,
     63                    NOTIFYPROC notify)
     64 {
     65     HANDLE hFile, hFileMapping;
     66     char *dst;
     67     FILETIME ft;
     68 
     69   try_again:
     70     if (!flags)
     71         flags = CREATE_NEW;
     72     hFile = CreateFile(filename,
     73                        GENERIC_WRITE | GENERIC_READ,
     74                        0, NULL,
     75                        flags,
     76                        FILE_ATTRIBUTE_NORMAL, NULL);
     77     if (hFile == INVALID_HANDLE_VALUE) {
     78         DWORD x = GetLastError();
     79         switch (x) {
     80         case ERROR_FILE_EXISTS:
     81             if (notify && notify(CAN_OVERWRITE, filename))
     82                 hFile = CreateFile(filename,
     83                                    GENERIC_WRITE|GENERIC_READ,
     84                                    0, NULL,
     85                                    CREATE_ALWAYS,
     86                                    FILE_ATTRIBUTE_NORMAL,
     87                                    NULL);
     88             else {
     89                 if (notify)
     90                     notify(FILE_OVERWRITTEN, filename);
     91                 return NULL;
     92             }
     93             break;
     94         case ERROR_PATH_NOT_FOUND:
     95             if (ensure_directory(filename, pathname_part, notify))
     96                 goto try_again;
     97             else
     98                 return FALSE;
     99             break;
    100         default:
    101             SetLastError(x);
    102             break;
    103         }
    104     }
    105     if (hFile == INVALID_HANDLE_VALUE) {
    106         if (notify)
    107             notify (SYSTEM_ERROR, "CreateFile (%s)", filename);
    108         return NULL;
    109     }
    110 
    111     if (notify)
    112         notify(FILE_CREATED, filename);
    113 
    114     DosDateTimeToFileTime(wFatDate, wFatTime, &ft);
    115     SetFileTime(hFile, &ft, &ft, &ft);
    116 
    117 
    118     if (size == 0) {
    119         /* We cannot map a zero-length file (Also it makes
    120            no sense */
    121         CloseHandle(hFile);
    122         return NULL;
    123     }
    124 
    125     hFileMapping = CreateFileMapping(hFile,
    126                                      NULL, PAGE_READWRITE, 0, size, NULL);
    127 
    128     CloseHandle(hFile);
    129 
    130     if (hFileMapping == INVALID_HANDLE_VALUE) {
    131         if (notify)
    132             notify(SYSTEM_ERROR,
    133                    "CreateFileMapping (%s)", filename);
    134         return NULL;
    135     }
    136 
    137     dst = MapViewOfFile(hFileMapping,
    138                         FILE_MAP_WRITE, 0, 0, 0);
    139 
    140     CloseHandle(hFileMapping);
    141 
    142     if (!dst) {
    143         if (notify)
    144             notify(SYSTEM_ERROR, "MapViewOfFile (%s)", filename);
    145         return NULL;
    146     }
    147     return dst;
    148 }
    149 
    150 
    151 BOOL
    152 extract_file(char *dst, char *src, int method, int comp_size,
    153              int uncomp_size, NOTIFYPROC notify)
    154 {
    155     z_stream zstream;
    156     int result;
    157 
    158     if (method == Z_DEFLATED) {
    159         int x;
    160         memset(&zstream, 0, sizeof(zstream));
    161         zstream.next_in = src;
    162         zstream.avail_in = comp_size+1;
    163         zstream.next_out = dst;
    164         zstream.avail_out = uncomp_size;
    165 
    166 /* Apparently an undocumented feature of zlib: Set windowsize
    167    to negative values to suppress the gzip header and be compatible with
    168    zip! */
    169         result = TRUE;
    170         if (Z_OK != (x = inflateInit2(&zstream, -15))) {
    171             if (notify)
    172                 notify(ZLIB_ERROR,
    173                        "inflateInit2 returns %d", x);
    174             result = FALSE;
    175             goto cleanup;
    176         }
    177         if (Z_STREAM_END != (x = inflate(&zstream, Z_FINISH))) {
    178             if (notify)
    179                 notify(ZLIB_ERROR,
    180                        "inflate returns %d", x);
    181             result = FALSE;
    182         }
    183       cleanup:
    184         if (Z_OK != (x = inflateEnd(&zstream))) {
    185             if (notify)
    186                 notify (ZLIB_ERROR,
    187                     "inflateEnd returns %d", x);
    188             result = FALSE;
    189         }
    190     } else if (method == 0) {
    191         memcpy(dst, src, uncomp_size);
    192         result = TRUE;
    193     } else
    194         result = FALSE;
    195     UnmapViewOfFile(dst);
    196     return result;
    197 }
    198 
    199 /* Open a zip-compatible archive and extract all files
    200  * into the specified directory (which is assumed to exist)
    201  */
    202 BOOL
    203 unzip_archive(SCHEME *scheme, char *dirname, char *data, DWORD size,
    204               NOTIFYPROC notify)
    205 {
    206     int n;
    207     char pathname[MAX_PATH];
    208     char *new_part;
    209 
    210     /* read the end of central directory record */
    211     struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
    212                                                    (struct eof_cdir)];
    213 
    214     int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
    215         pe->ofsCDir;
    216 
    217     /* set position to start of central directory */
    218     int pos = arc_start + pe->ofsCDir;
    219 
    220     /* make sure this is a zip file */
    221     if (pe->tag != 0x06054b50)
    222         return FALSE;
    223 
    224     /* Loop through the central directory, reading all entries */
    225     for (n = 0; n < pe->nTotalCDir; ++n) {
    226         int i;
    227         char *fname;
    228         char *pcomp;
    229         char *dst;
    230         struct cdir *pcdir;
    231         struct fhdr *pfhdr;
    232 
    233         pcdir = (struct cdir *)&data[pos];
    234         pfhdr = (struct fhdr *)&data[pcdir->ofs_local_header +
    235                                      arc_start];
    236 
    237         if (pcdir->tag != 0x02014b50)
    238             return FALSE;
    239         if (pfhdr->tag != 0x04034b50)
    240             return FALSE;
    241         pos += sizeof(struct cdir);
    242         fname = (char *)&data[pos]; /* This is not null terminated! */
    243         pos += pcdir->fname_length + pcdir->extra_length +
    244             pcdir->comment_length;
    245 
    246         pcomp = &data[pcdir->ofs_local_header
    247                       + sizeof(struct fhdr)
    248                       + arc_start
    249                       + pfhdr->fname_length
    250                       + pfhdr->extra_length];
    251 
    252         /* dirname is the Python home directory (prefix) */
    253         strcpy(pathname, dirname);
    254         if (pathname[strlen(pathname)-1] != '\\')
    255             strcat(pathname, "\\");
    256         new_part = &pathname[lstrlen(pathname)];
    257         /* we must now match the first part of the pathname
    258          * in the archive to a component in the installation
    259          * scheme (PURELIB, PLATLIB, HEADERS, SCRIPTS, or DATA)
    260          * and replace this part by the one in the scheme to use
    261          */
    262         for (i = 0; scheme[i].name; ++i) {
    263             if (0 == strnicmp(scheme[i].name, fname,
    264                               strlen(scheme[i].name))) {
    265                 char *rest;
    266                 int len;
    267 
    268                 /* length of the replaced part */
    269                 int namelen = strlen(scheme[i].name);
    270 
    271                 strcat(pathname, scheme[i].prefix);
    272 
    273                 rest = fname + namelen;
    274                 len = pfhdr->fname_length - namelen;
    275 
    276                 if ((pathname[strlen(pathname)-1] != '\\')
    277                     && (pathname[strlen(pathname)-1] != '/'))
    278                     strcat(pathname, "\\");
    279                 /* Now that pathname ends with a separator,
    280                  * we must make sure rest does not start with
    281                  * an additional one.
    282                  */
    283                 if ((rest[0] == '\\') || (rest[0] == '/')) {
    284                     ++rest;
    285                     --len;
    286                 }
    287 
    288                 strncat(pathname, rest, len);
    289                 goto Done;
    290             }
    291         }
    292         /* no prefix to replace found, go unchanged */
    293         strncat(pathname, fname, pfhdr->fname_length);
    294       Done:
    295         normpath(pathname);
    296         if (pathname[strlen(pathname)-1] != '\\') {
    297             /*
    298              * The local file header (pfhdr) does not always
    299              * contain the compressed and uncompressed sizes of
    300              * the data depending on bit 3 of the flags field.  So
    301              * it seems better to use the data from the central
    302              * directory (pcdir).
    303              */
    304             dst = map_new_file(0, pathname, new_part,
    305                                pcdir->uncomp_size,
    306                                pcdir->last_mod_file_date,
    307                                pcdir->last_mod_file_time, notify);
    308             if (dst) {
    309                 if (!extract_file(dst, pcomp, pfhdr->method,
    310                                   pcdir->comp_size,
    311                                   pcdir->uncomp_size,
    312                                   notify))
    313                     return FALSE;
    314             } /* else ??? */
    315         }
    316         if (notify)
    317             notify(NUM_FILES, new_part, (int)pe->nTotalCDir,
    318                    (int)n+1);
    319     }
    320     return TRUE;
    321 }
    322