1/* 2 IMPORTANT NOTE: IF THIS FILE IS CHANGED, PCBUILD\BDIST_WININST.VCXPROJ MUST 3 BE REBUILT AS WELL. 4 5 IF CHANGES TO THIS FILE ARE CHECKED IN, THE RECOMPILED BINARIES MUST BE 6 CHECKED IN AS WELL! 7*/ 8 9#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{ 21 while (path && *path) { 22 if (*path == '/') 23 *path = '\\'; 24 ++path; 25 } 26} 27 28BOOL ensure_directory(char *pathname, char *new_part, NOTIFYPROC notify) 29{ 30 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; 54} 55 56/* XXX Should better explicitly specify 57 * uncomp_size and file_times instead of pfhdr! 58 */ 59char *map_new_file(DWORD flags, char *filename, 60 char *pathname_part, int size, 61 WORD wFatDate, WORD wFatTime, 62 NOTIFYPROC notify) 63{ 64 HANDLE hFile, hFileMapping; 65 char *dst; 66 FILETIME ft; 67 68 try_again: 69 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 } 109 110 if (notify) 111 notify(FILE_CREATED, filename); 112 113 DosDateTimeToFileTime(wFatDate, wFatTime, &ft); 114 SetFileTime(hFile, &ft, &ft, &ft); 115 116 117 if (size == 0) { 118 /* We cannot map a zero-length file (Also it makes 119 no sense */ 120 CloseHandle(hFile); 121 return NULL; 122 } 123 124 hFileMapping = CreateFileMapping(hFile, 125 NULL, PAGE_READWRITE, 0, size, NULL); 126 127 CloseHandle(hFile); 128 129 if (hFileMapping == NULL) { 130 if (notify) 131 notify(SYSTEM_ERROR, 132 "CreateFileMapping (%s)", filename); 133 return NULL; 134 } 135 136 dst = MapViewOfFile(hFileMapping, 137 FILE_MAP_WRITE, 0, 0, 0); 138 139 CloseHandle(hFileMapping); 140 141 if (!dst) { 142 if (notify) 143 notify(SYSTEM_ERROR, "MapViewOfFile (%s)", filename); 144 return NULL; 145 } 146 return dst; 147} 148 149 150BOOL 151extract_file(char *dst, char *src, int method, int comp_size, 152 int uncomp_size, NOTIFYPROC notify) 153{ 154 z_stream zstream; 155 int result; 156 157 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; 164 165/* Apparently an undocumented feature of zlib: Set windowsize 166 to negative values to suppress the gzip header and be compatible with 167 zip! */ 168 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; 196} 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, 203 NOTIFYPROC notify) 204{ 205 int n; 206 char pathname[MAX_PATH]; 207 char *new_part; 208 209 /* read the end of central directory record */ 210 struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof 211 (struct eof_cdir)]; 212 213 int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir - 214 pe->ofsCDir; 215 216 /* set position to start of central directory */ 217 int pos = arc_start + pe->ofsCDir; 218 219 /* make sure this is a zip file */ 220 if (pe->tag != 0x06054b50) 221 return FALSE; 222 223 /* 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; 231 232 pcdir = (struct cdir *)&data[pos]; 233 pfhdr = (struct fhdr *)&data[pcdir->ofs_local_header + 234 arc_start]; 235 236 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; 244 245 pcomp = &data[pcdir->ofs_local_header 246 + sizeof(struct fhdr) 247 + arc_start 248 + pfhdr->fname_length 249 + pfhdr->extra_length]; 250 251 /* 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; 320} 321