• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
normpath(char * path)20 static void normpath(char *path)
21 {
22     while (path && *path) {
23         if (*path == '/')
24             *path = '\\';
25         ++path;
26     }
27 }
28 
ensure_directory(char * pathname,char * new_part,NOTIFYPROC notify)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  */
map_new_file(DWORD flags,char * filename,char * pathname_part,int size,WORD wFatDate,WORD wFatTime,NOTIFYPROC notify)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
extract_file(char * dst,char * src,int method,int comp_size,int uncomp_size,NOTIFYPROC notify)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
unzip_archive(SCHEME * scheme,char * dirname,char * data,DWORD size,NOTIFYPROC notify)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