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