blob: 0249d9ff542f8a2813a830d96174a07b0ad2b598 [file] [log] [blame]
Thomas Hellerd1d92ea2004-07-14 15:17:04 +00001/*
Steve Dower65e4cb12014-11-22 12:54:57 -08002 IMPORTANT NOTE: IF THIS FILE IS CHANGED, PCBUILD\BDIST_WININST.VCXPROJ MUST
3 BE REBUILT AS WELL.
Thomas Hellerd1d92ea2004-07-14 15:17:04 +00004
Steve Dower65e4cb12014-11-22 12:54:57 -08005 IF CHANGES TO THIS FILE ARE CHECKED IN, THE RECOMPILED BINARIES MUST BE
6 CHECKED IN AS WELL!
Thomas Hellerd1d92ea2004-07-14 15:17:04 +00007*/
8
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00009#include <windows.h>
10
11#include "zlib.h"
12
13#include <stdio.h>
14#include <stdarg.h>
15
16#include "archive.h"
17
18/* Convert unix-path to dos-path */
19static void normpath(char *path)
20{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +000021 while (path && *path) {
22 if (*path == '/')
23 *path = '\\';
24 ++path;
25 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000026}
27
28BOOL ensure_directory(char *pathname, char *new_part, NOTIFYPROC notify)
29{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +000030 while (new_part && *new_part && (new_part = strchr(new_part, '\\'))) {
31 DWORD attr;
32 *new_part = '\0';
33 attr = GetFileAttributes(pathname);
34 if (attr == -1) {
35 /* nothing found */
36 if (!CreateDirectory(pathname, NULL) && notify)
37 notify(SYSTEM_ERROR,
38 "CreateDirectory (%s)", pathname);
39 else
40 notify(DIR_CREATED, pathname);
41 }
42 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
43 ;
44 } else {
45 SetLastError(183);
46 if (notify)
47 notify(SYSTEM_ERROR,
48 "CreateDirectory (%s)", pathname);
49 }
50 *new_part = '\\';
51 ++new_part;
52 }
53 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000054}
55
Ezio Melotti13925002011-03-16 11:05:33 +020056/* XXX Should better explicitly specify
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000057 * uncomp_size and file_times instead of pfhdr!
58 */
59char *map_new_file(DWORD flags, char *filename,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +000060 char *pathname_part, int size,
61 WORD wFatDate, WORD wFatTime,
62 NOTIFYPROC notify)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000063{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +000064 HANDLE hFile, hFileMapping;
65 char *dst;
66 FILETIME ft;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000067
68 try_again:
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +000069 if (!flags)
70 flags = CREATE_NEW;
71 hFile = CreateFile(filename,
72 GENERIC_WRITE | GENERIC_READ,
73 0, NULL,
74 flags,
75 FILE_ATTRIBUTE_NORMAL, NULL);
76 if (hFile == INVALID_HANDLE_VALUE) {
77 DWORD x = GetLastError();
78 switch (x) {
79 case ERROR_FILE_EXISTS:
80 if (notify && notify(CAN_OVERWRITE, filename))
81 hFile = CreateFile(filename,
82 GENERIC_WRITE|GENERIC_READ,
83 0, NULL,
84 CREATE_ALWAYS,
85 FILE_ATTRIBUTE_NORMAL,
86 NULL);
87 else {
88 if (notify)
89 notify(FILE_OVERWRITTEN, filename);
90 return NULL;
91 }
92 break;
93 case ERROR_PATH_NOT_FOUND:
94 if (ensure_directory(filename, pathname_part, notify))
95 goto try_again;
96 else
97 return FALSE;
98 break;
99 default:
100 SetLastError(x);
101 break;
102 }
103 }
104 if (hFile == INVALID_HANDLE_VALUE) {
105 if (notify)
106 notify (SYSTEM_ERROR, "CreateFile (%s)", filename);
107 return NULL;
108 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000109
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000110 if (notify)
111 notify(FILE_CREATED, filename);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000112
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000113 DosDateTimeToFileTime(wFatDate, wFatTime, &ft);
114 SetFileTime(hFile, &ft, &ft, &ft);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000115
116
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000117 if (size == 0) {
118 /* We cannot map a zero-length file (Also it makes
119 no sense */
120 CloseHandle(hFile);
121 return NULL;
122 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000123
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000124 hFileMapping = CreateFileMapping(hFile,
125 NULL, PAGE_READWRITE, 0, size, NULL);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000126
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000127 CloseHandle(hFile);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000128
Gregory P. Smithb803c6c2013-03-23 16:05:36 -0700129 if (hFileMapping == NULL) {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000130 if (notify)
131 notify(SYSTEM_ERROR,
132 "CreateFileMapping (%s)", filename);
133 return NULL;
134 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000135
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000136 dst = MapViewOfFile(hFileMapping,
137 FILE_MAP_WRITE, 0, 0, 0);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000138
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000139 CloseHandle(hFileMapping);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000140
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000141 if (!dst) {
142 if (notify)
143 notify(SYSTEM_ERROR, "MapViewOfFile (%s)", filename);
144 return NULL;
145 }
146 return dst;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000147}
148
149
150BOOL
151extract_file(char *dst, char *src, int method, int comp_size,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000152 int uncomp_size, NOTIFYPROC notify)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000153{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000154 z_stream zstream;
155 int result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000156
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000157 if (method == Z_DEFLATED) {
158 int x;
159 memset(&zstream, 0, sizeof(zstream));
160 zstream.next_in = src;
161 zstream.avail_in = comp_size+1;
162 zstream.next_out = dst;
163 zstream.avail_out = uncomp_size;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000164
165/* Apparently an undocumented feature of zlib: Set windowsize
Ezio Melotti13925002011-03-16 11:05:33 +0200166 to negative values to suppress the gzip header and be compatible with
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000167 zip! */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000168 result = TRUE;
169 if (Z_OK != (x = inflateInit2(&zstream, -15))) {
170 if (notify)
171 notify(ZLIB_ERROR,
172 "inflateInit2 returns %d", x);
173 result = FALSE;
174 goto cleanup;
175 }
176 if (Z_STREAM_END != (x = inflate(&zstream, Z_FINISH))) {
177 if (notify)
178 notify(ZLIB_ERROR,
179 "inflate returns %d", x);
180 result = FALSE;
181 }
182 cleanup:
183 if (Z_OK != (x = inflateEnd(&zstream))) {
184 if (notify)
185 notify (ZLIB_ERROR,
186 "inflateEnd returns %d", x);
187 result = FALSE;
188 }
189 } else if (method == 0) {
190 memcpy(dst, src, uncomp_size);
191 result = TRUE;
192 } else
193 result = FALSE;
194 UnmapViewOfFile(dst);
195 return result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000196}
197
198/* Open a zip-compatible archive and extract all files
199 * into the specified directory (which is assumed to exist)
200 */
201BOOL
202unzip_archive(SCHEME *scheme, char *dirname, char *data, DWORD size,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000203 NOTIFYPROC notify)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000204{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000205 int n;
206 char pathname[MAX_PATH];
207 char *new_part;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000208
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000209 /* read the end of central directory record */
210 struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
211 (struct eof_cdir)];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000212
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000213 int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
214 pe->ofsCDir;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000215
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000216 /* set position to start of central directory */
217 int pos = arc_start + pe->ofsCDir;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000218
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000219 /* make sure this is a zip file */
220 if (pe->tag != 0x06054b50)
221 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000222
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000223 /* Loop through the central directory, reading all entries */
224 for (n = 0; n < pe->nTotalCDir; ++n) {
225 int i;
226 char *fname;
227 char *pcomp;
228 char *dst;
229 struct cdir *pcdir;
230 struct fhdr *pfhdr;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000231
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000232 pcdir = (struct cdir *)&data[pos];
233 pfhdr = (struct fhdr *)&data[pcdir->ofs_local_header +
234 arc_start];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000235
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000236 if (pcdir->tag != 0x02014b50)
237 return FALSE;
238 if (pfhdr->tag != 0x04034b50)
239 return FALSE;
240 pos += sizeof(struct cdir);
241 fname = (char *)&data[pos]; /* This is not null terminated! */
242 pos += pcdir->fname_length + pcdir->extra_length +
243 pcdir->comment_length;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000244
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000245 pcomp = &data[pcdir->ofs_local_header
246 + sizeof(struct fhdr)
247 + arc_start
248 + pfhdr->fname_length
249 + pfhdr->extra_length];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000250
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000251 /* dirname is the Python home directory (prefix) */
252 strcpy(pathname, dirname);
253 if (pathname[strlen(pathname)-1] != '\\')
254 strcat(pathname, "\\");
255 new_part = &pathname[lstrlen(pathname)];
256 /* we must now match the first part of the pathname
257 * in the archive to a component in the installation
258 * scheme (PURELIB, PLATLIB, HEADERS, SCRIPTS, or DATA)
259 * and replace this part by the one in the scheme to use
260 */
261 for (i = 0; scheme[i].name; ++i) {
262 if (0 == strnicmp(scheme[i].name, fname,
263 strlen(scheme[i].name))) {
264 char *rest;
265 int len;
266
267 /* length of the replaced part */
268 int namelen = strlen(scheme[i].name);
269
270 strcat(pathname, scheme[i].prefix);
271
272 rest = fname + namelen;
273 len = pfhdr->fname_length - namelen;
274
275 if ((pathname[strlen(pathname)-1] != '\\')
276 && (pathname[strlen(pathname)-1] != '/'))
277 strcat(pathname, "\\");
278 /* Now that pathname ends with a separator,
279 * we must make sure rest does not start with
280 * an additional one.
281 */
282 if ((rest[0] == '\\') || (rest[0] == '/')) {
283 ++rest;
284 --len;
285 }
286
287 strncat(pathname, rest, len);
288 goto Done;
289 }
290 }
291 /* no prefix to replace found, go unchanged */
292 strncat(pathname, fname, pfhdr->fname_length);
293 Done:
294 normpath(pathname);
295 if (pathname[strlen(pathname)-1] != '\\') {
296 /*
297 * The local file header (pfhdr) does not always
298 * contain the compressed and uncompressed sizes of
299 * the data depending on bit 3 of the flags field. So
300 * it seems better to use the data from the central
301 * directory (pcdir).
302 */
303 dst = map_new_file(0, pathname, new_part,
304 pcdir->uncomp_size,
305 pcdir->last_mod_file_date,
306 pcdir->last_mod_file_time, notify);
307 if (dst) {
308 if (!extract_file(dst, pcomp, pfhdr->method,
309 pcdir->comp_size,
310 pcdir->uncomp_size,
311 notify))
312 return FALSE;
313 } /* else ??? */
314 }
315 if (notify)
316 notify(NUM_FILES, new_part, (int)pe->nTotalCDir,
317 (int)n+1);
318 }
319 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000320}