xref: /PHP-5.3/ext/phar/zip.c (revision 3eb17456)
1 /*
2   +----------------------------------------------------------------------+
3   | ZIP archive support for Phar                                         |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2007-2013 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt.                                 |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Gregory Beaver <cellog@php.net>                             |
16   +----------------------------------------------------------------------+
17 */
18 
19 #include "phar_internal.h"
20 
21 #define PHAR_GET_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
22 	(((php_uint16)var[1]) & 0xff) << 8))
23 #define PHAR_GET_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
24 	(((php_uint32)var[1]) & 0xff) << 8 | \
25 	(((php_uint32)var[2]) & 0xff) << 16 | \
26 	(((php_uint32)var[3]) & 0xff) << 24))
phar_write_32(char buffer[4],php_uint32 value)27 static inline void phar_write_32(char buffer[4], php_uint32 value)
28 {
29 	buffer[3] = (unsigned char) ((value & 0xff000000) >> 24);
30 	buffer[2] = (unsigned char) ((value & 0xff0000) >> 16);
31 	buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
32 	buffer[0] = (unsigned char) (value & 0xff);
33 }
phar_write_16(char buffer[2],php_uint32 value)34 static inline void phar_write_16(char buffer[2], php_uint32 value)
35 {
36 	buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
37 	buffer[0] = (unsigned char) (value & 0xff);
38 }
39 # define PHAR_SET_32(var, value) phar_write_32(var, (php_uint32) (value));
40 # define PHAR_SET_16(var, value) phar_write_16(var, (php_uint16) (value));
41 
phar_zip_process_extra(php_stream * fp,phar_entry_info * entry,php_uint16 len TSRMLS_DC)42 static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len TSRMLS_DC) /* {{{ */
43 {
44 	union {
45 		phar_zip_extra_field_header header;
46 		phar_zip_unix3 unix3;
47 	} h;
48 	int read;
49 
50 	do {
51 		if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) {
52 			return FAILURE;
53 		}
54 
55 		if (h.header.tag[0] != 'n' || h.header.tag[1] != 'u') {
56 			/* skip to next header */
57 			php_stream_seek(fp, PHAR_GET_16(h.header.size), SEEK_CUR);
58 			len -= PHAR_GET_16(h.header.size) + 4;
59 			continue;
60 		}
61 
62 		/* unix3 header found */
63 		read = php_stream_read(fp, (char *) &(h.unix3.crc32), sizeof(h.unix3) - sizeof(h.header));
64 		len -= read + 4;
65 
66 		if (sizeof(h.unix3) - sizeof(h.header) != read) {
67 			return FAILURE;
68 		}
69 
70 		if (PHAR_GET_16(h.unix3.size) > sizeof(h.unix3) - 4) {
71 			/* skip symlink filename - we may add this support in later */
72 			php_stream_seek(fp, PHAR_GET_16(h.unix3.size) - sizeof(h.unix3.size), SEEK_CUR);
73 		}
74 
75 		/* set permissions */
76 		entry->flags &= PHAR_ENT_COMPRESSION_MASK;
77 
78 		if (entry->is_dir) {
79 			entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK;
80 		} else {
81 			entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK;
82 		}
83 
84 	} while (len);
85 
86 	return SUCCESS;
87 }
88 /* }}} */
89 
90 /*
91   extracted from libzip
92   zip_dirent.c -- read directory entry (local or central), clean dirent
93   Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
94 
95   This function is part of libzip, a library to manipulate ZIP archives.
96   The authors can be contacted at <nih@giga.or.at>
97 
98   Redistribution and use in source and binary forms, with or without
99   modification, are permitted provided that the following conditions
100   are met:
101   1. Redistributions of source code must retain the above copyright
102      notice, this list of conditions and the following disclaimer.
103   2. Redistributions in binary form must reproduce the above copyright
104      notice, this list of conditions and the following disclaimer in
105      the documentation and/or other materials provided with the
106      distribution.
107   3. The names of the authors may not be used to endorse or promote
108      products derived from this software without specific prior
109      written permission.
110 
111   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
112   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
113   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
114   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
115   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
116   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
117   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
118   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
119   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
120   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
121   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
122  */
phar_zip_d2u_time(char * cdtime,char * cddate)123 static time_t phar_zip_d2u_time(char *cdtime, char *cddate) /* {{{ */
124 {
125 	int dtime = PHAR_GET_16(cdtime), ddate = PHAR_GET_16(cddate);
126 	struct tm *tm, tmbuf;
127 	time_t now;
128 
129 	now = time(NULL);
130 	tm = php_localtime_r(&now, &tmbuf);
131 
132 	tm->tm_year = ((ddate>>9)&127) + 1980 - 1900;
133 	tm->tm_mon = ((ddate>>5)&15) - 1;
134 	tm->tm_mday = ddate&31;
135 
136 	tm->tm_hour = (dtime>>11)&31;
137 	tm->tm_min = (dtime>>5)&63;
138 	tm->tm_sec = (dtime<<1)&62;
139 
140 	return mktime(tm);
141 }
142 /* }}} */
143 
phar_zip_u2d_time(time_t time,char * dtime,char * ddate)144 static void phar_zip_u2d_time(time_t time, char *dtime, char *ddate) /* {{{ */
145 {
146 	php_uint16 ctime, cdate;
147 	struct tm *tm, tmbuf;
148 
149 	tm = php_localtime_r(&time, &tmbuf);
150 	cdate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday;
151 	ctime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1);
152 	PHAR_SET_16(dtime, ctime);
153 	PHAR_SET_16(ddate, cdate);
154 }
155 /* }}} */
156 
157 /**
158  * Does not check for a previously opened phar in the cache.
159  *
160  * Parse a new one and add it to the cache, returning either SUCCESS or
161  * FAILURE, and setting pphar to the pointer to the manifest entry
162  *
163  * This is used by phar_open_from_fp to process a zip-based phar, but can be called
164  * directly.
165  */
phar_parse_zipfile(php_stream * fp,char * fname,int fname_len,char * alias,int alias_len,phar_archive_data ** pphar,char ** error TSRMLS_DC)166 int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
167 {
168 	phar_zip_dir_end locator;
169 	char buf[sizeof(locator) + 65536];
170 	long size;
171 	php_uint16 i;
172 	phar_archive_data *mydata = NULL;
173 	phar_entry_info entry = {0};
174 	char *p = buf, *ext, *actual_alias = NULL;
175 	char *metadata = NULL;
176 
177 	size = php_stream_tell(fp);
178 
179 	if (size > sizeof(locator) + 65536) {
180 		/* seek to max comment length + end of central directory record */
181 		size = sizeof(locator) + 65536;
182 		if (FAILURE == php_stream_seek(fp, -size, SEEK_END)) {
183 			php_stream_close(fp);
184 			if (error) {
185 				spprintf(error, 4096, "phar error: unable to search for end of central directory in zip-based phar \"%s\"", fname);
186 			}
187 			return FAILURE;
188 		}
189 	} else {
190 		php_stream_seek(fp, 0, SEEK_SET);
191 	}
192 
193 	if (!php_stream_read(fp, buf, size)) {
194 		php_stream_close(fp);
195 		if (error) {
196 			spprintf(error, 4096, "phar error: unable to read in data to search for end of central directory in zip-based phar \"%s\"", fname);
197 		}
198 		return FAILURE;
199 	}
200 
201 	while ((p=(char *) memchr(p + 1, 'P', (size_t) (size - (p + 1 - buf)))) != NULL) {
202 		if (!memcmp(p + 1, "K\5\6", 3)) {
203 			memcpy((void *)&locator, (void *) p, sizeof(locator));
204 			if (PHAR_GET_16(locator.centraldisk) != 0 || PHAR_GET_16(locator.disknumber) != 0) {
205 				/* split archives not handled */
206 				php_stream_close(fp);
207 				if (error) {
208 					spprintf(error, 4096, "phar error: split archives spanning multiple zips cannot be processed in zip-based phar \"%s\"", fname);
209 				}
210 				return FAILURE;
211 			}
212 
213 			if (PHAR_GET_16(locator.counthere) != PHAR_GET_16(locator.count)) {
214 				if (error) {
215 					spprintf(error, 4096, "phar error: corrupt zip archive, conflicting file count in end of central directory record in zip-based phar \"%s\"", fname);
216 				}
217 				php_stream_close(fp);
218 				return FAILURE;
219 			}
220 
221 			mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
222 			mydata->is_persistent = PHAR_G(persist);
223 
224 			/* read in archive comment, if any */
225 			if (PHAR_GET_16(locator.comment_len)) {
226 
227 				metadata = p + sizeof(locator);
228 
229 				if (PHAR_GET_16(locator.comment_len) != size - (metadata - buf)) {
230 					if (error) {
231 						spprintf(error, 4096, "phar error: corrupt zip archive, zip file comment truncated in zip-based phar \"%s\"", fname);
232 					}
233 					php_stream_close(fp);
234 					pefree(mydata, mydata->is_persistent);
235 					return FAILURE;
236 				}
237 
238 				mydata->metadata_len = PHAR_GET_16(locator.comment_len);
239 
240 				if (phar_parse_metadata(&metadata, &mydata->metadata, PHAR_GET_16(locator.comment_len) TSRMLS_CC) == FAILURE) {
241 					mydata->metadata_len = 0;
242 					/* if not valid serialized data, it is a regular string */
243 
244 					if (entry.is_persistent) {
245 						ALLOC_PERMANENT_ZVAL(mydata->metadata);
246 					} else {
247 						ALLOC_ZVAL(mydata->metadata);
248 					}
249 
250 					INIT_ZVAL(*mydata->metadata);
251 					metadata = pestrndup(metadata, PHAR_GET_16(locator.comment_len), mydata->is_persistent);
252 					ZVAL_STRINGL(mydata->metadata, metadata, PHAR_GET_16(locator.comment_len), 0);
253 				}
254 			} else {
255 				mydata->metadata = NULL;
256 			}
257 
258 			goto foundit;
259 		}
260 	}
261 
262 	php_stream_close(fp);
263 
264 	if (error) {
265 		spprintf(error, 4096, "phar error: end of central directory not found in zip-based phar \"%s\"", fname);
266 	}
267 
268 	return FAILURE;
269 foundit:
270 	mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
271 #ifdef PHP_WIN32
272 	phar_unixify_path_separators(mydata->fname, fname_len);
273 #endif
274 	mydata->is_zip = 1;
275 	mydata->fname_len = fname_len;
276 	ext = strrchr(mydata->fname, '/');
277 
278 	if (ext) {
279 		mydata->ext = memchr(ext, '.', (mydata->fname + fname_len) - ext);
280 		if (mydata->ext == ext) {
281 			mydata->ext = memchr(ext + 1, '.', (mydata->fname + fname_len) - ext - 1);
282 		}
283 		if (mydata->ext) {
284 			mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
285 		}
286 	}
287 
288 	/* clean up on big-endian systems */
289 	/* seek to central directory */
290 	php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
291 	/* read in central directory */
292 	zend_hash_init(&mydata->manifest, PHAR_GET_16(locator.count),
293 		zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
294 	zend_hash_init(&mydata->mounted_dirs, 5,
295 		zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
296 	zend_hash_init(&mydata->virtual_dirs, PHAR_GET_16(locator.count) * 2,
297 		zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
298 	entry.phar = mydata;
299 	entry.is_zip = 1;
300 	entry.fp_type = PHAR_FP;
301 	entry.is_persistent = mydata->is_persistent;
302 #define PHAR_ZIP_FAIL_FREE(errmsg, save) \
303 			zend_hash_destroy(&mydata->manifest); \
304 			mydata->manifest.arBuckets = 0; \
305 			zend_hash_destroy(&mydata->mounted_dirs); \
306 			mydata->mounted_dirs.arBuckets = 0; \
307 			zend_hash_destroy(&mydata->virtual_dirs); \
308 			mydata->virtual_dirs.arBuckets = 0; \
309 			php_stream_close(fp); \
310 			if (mydata->metadata) { \
311 				zval_dtor(mydata->metadata); \
312 			} \
313 			if (mydata->signature) { \
314 				efree(mydata->signature); \
315 			} \
316 			if (error) { \
317 				spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
318 			} \
319 			pefree(mydata->fname, mydata->is_persistent); \
320 			if (mydata->alias) { \
321 				pefree(mydata->alias, mydata->is_persistent); \
322 			} \
323 			pefree(mydata, mydata->is_persistent); \
324 			efree(save); \
325 			return FAILURE;
326 #define PHAR_ZIP_FAIL(errmsg) \
327 			zend_hash_destroy(&mydata->manifest); \
328 			mydata->manifest.arBuckets = 0; \
329 			zend_hash_destroy(&mydata->mounted_dirs); \
330 			mydata->mounted_dirs.arBuckets = 0; \
331 			zend_hash_destroy(&mydata->virtual_dirs); \
332 			mydata->virtual_dirs.arBuckets = 0; \
333 			php_stream_close(fp); \
334 			if (mydata->metadata) { \
335 				zval_dtor(mydata->metadata); \
336 			} \
337 			if (mydata->signature) { \
338 				efree(mydata->signature); \
339 			} \
340 			if (error) { \
341 				spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
342 			} \
343 			pefree(mydata->fname, mydata->is_persistent); \
344 			if (mydata->alias) { \
345 				pefree(mydata->alias, mydata->is_persistent); \
346 			} \
347 			pefree(mydata, mydata->is_persistent); \
348 			return FAILURE;
349 
350 	/* add each central directory item to the manifest */
351 	for (i = 0; i < PHAR_GET_16(locator.count); ++i) {
352 		phar_zip_central_dir_file zipentry;
353 		off_t beforeus = php_stream_tell(fp);
354 
355 		if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) {
356 			PHAR_ZIP_FAIL("unable to read central directory entry, truncated");
357 		}
358 
359 		/* clean up for bigendian systems */
360 		if (memcmp("PK\1\2", zipentry.signature, 4)) {
361 			/* corrupted entry */
362 			PHAR_ZIP_FAIL("corrupted central directory entry, no magic signature");
363 		}
364 
365 		if (entry.is_persistent) {
366 			entry.manifest_pos = i;
367 		}
368 
369 		entry.compressed_filesize = PHAR_GET_32(zipentry.compsize);
370 		entry.uncompressed_filesize = PHAR_GET_32(zipentry.uncompsize);
371 		entry.crc32 = PHAR_GET_32(zipentry.crc32);
372 		/* do not PHAR_GET_16 either on the next line */
373 		entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
374 		entry.flags = PHAR_ENT_PERM_DEF_FILE;
375 		entry.header_offset = PHAR_GET_32(zipentry.offset);
376 		entry.offset = entry.offset_abs = PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + PHAR_GET_16(zipentry.filename_len) +
377 			PHAR_GET_16(zipentry.extra_len);
378 
379 		if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) {
380 			PHAR_ZIP_FAIL("Cannot process encrypted zip files");
381 		}
382 
383 		if (!PHAR_GET_16(zipentry.filename_len)) {
384 			PHAR_ZIP_FAIL("Cannot process zips created from stdin (zero-length filename)");
385 		}
386 
387 		entry.filename_len = PHAR_GET_16(zipentry.filename_len);
388 		entry.filename = (char *) pemalloc(entry.filename_len + 1, entry.is_persistent);
389 
390 		if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
391 			pefree(entry.filename, entry.is_persistent);
392 			PHAR_ZIP_FAIL("unable to read in filename from central directory, truncated");
393 		}
394 
395 		entry.filename[entry.filename_len] = '\0';
396 
397 		if (entry.filename[entry.filename_len - 1] == '/') {
398 			entry.is_dir = 1;
399 			entry.filename_len--;
400 			entry.flags |= PHAR_ENT_PERM_DEF_DIR;
401 		} else {
402 			entry.is_dir = 0;
403 		}
404 
405 		if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
406 			size_t read;
407 			php_stream *sigfile;
408 			off_t now;
409 			char *sig;
410 
411 			now = php_stream_tell(fp);
412 			pefree(entry.filename, entry.is_persistent);
413 			sigfile = php_stream_fopen_tmpfile();
414 			if (!sigfile) {
415 				PHAR_ZIP_FAIL("couldn't open temporary file");
416 			}
417 
418 			php_stream_seek(fp, 0, SEEK_SET);
419 			/* copy file contents + local headers and zip comment, if any, to be hashed for signature */
420 			phar_stream_copy_to_stream(fp, sigfile, entry.header_offset, NULL);
421 			/* seek to central directory */
422 			php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
423 			/* copy central directory header */
424 			phar_stream_copy_to_stream(fp, sigfile, beforeus - PHAR_GET_32(locator.cdir_offset), NULL);
425 			if (metadata) {
426 				php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
427 			}
428 			php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
429 			sig = (char *) emalloc(entry.uncompressed_filesize);
430 			read = php_stream_read(fp, sig, entry.uncompressed_filesize);
431 			if (read != entry.uncompressed_filesize) {
432 				php_stream_close(sigfile);
433 				efree(sig);
434 				PHAR_ZIP_FAIL("signature cannot be read");
435 			}
436 			mydata->sig_flags = PHAR_GET_32(sig);
437 			if (FAILURE == phar_verify_signature(sigfile, php_stream_tell(sigfile), mydata->sig_flags, sig + 8, entry.uncompressed_filesize - 8, fname, &mydata->signature, &mydata->sig_len, error TSRMLS_CC)) {
438 				efree(sig);
439 				if (error) {
440 					char *save;
441 					php_stream_close(sigfile);
442 					spprintf(&save, 4096, "signature cannot be verified: %s", *error);
443 					efree(*error);
444 					PHAR_ZIP_FAIL_FREE(save, save);
445 				} else {
446 					php_stream_close(sigfile);
447 					PHAR_ZIP_FAIL("signature cannot be verified");
448 				}
449 			}
450 			php_stream_close(sigfile);
451 			efree(sig);
452 			/* signature checked out, let's ensure this is the last file in the phar */
453 			if (i != PHAR_GET_16(locator.count) - 1) {
454 				PHAR_ZIP_FAIL("entries exist after signature, invalid phar");
455 			}
456 
457 			continue;
458 		}
459 
460 		phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len TSRMLS_CC);
461 
462 		if (PHAR_GET_16(zipentry.extra_len)) {
463 			off_t loc = php_stream_tell(fp);
464 			if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len) TSRMLS_CC)) {
465 				pefree(entry.filename, entry.is_persistent);
466 				PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
467 			}
468 			php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
469 		}
470 
471 		switch (PHAR_GET_16(zipentry.compressed)) {
472 			case PHAR_ZIP_COMP_NONE :
473 				/* compression flag already set */
474 				break;
475 			case PHAR_ZIP_COMP_DEFLATE :
476 				entry.flags |= PHAR_ENT_COMPRESSED_GZ;
477 				if (!PHAR_G(has_zlib)) {
478 					pefree(entry.filename, entry.is_persistent);
479 					PHAR_ZIP_FAIL("zlib extension is required");
480 				}
481 				break;
482 			case PHAR_ZIP_COMP_BZIP2 :
483 				entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
484 				if (!PHAR_G(has_bz2)) {
485 					pefree(entry.filename, entry.is_persistent);
486 					PHAR_ZIP_FAIL("bzip2 extension is required");
487 				}
488 				break;
489 			case 1 :
490 				pefree(entry.filename, entry.is_persistent);
491 				PHAR_ZIP_FAIL("unsupported compression method (Shrunk) used in this zip");
492 			case 2 :
493 			case 3 :
494 			case 4 :
495 			case 5 :
496 				pefree(entry.filename, entry.is_persistent);
497 				PHAR_ZIP_FAIL("unsupported compression method (Reduce) used in this zip");
498 			case 6 :
499 				pefree(entry.filename, entry.is_persistent);
500 				PHAR_ZIP_FAIL("unsupported compression method (Implode) used in this zip");
501 			case 7 :
502 				pefree(entry.filename, entry.is_persistent);
503 				PHAR_ZIP_FAIL("unsupported compression method (Tokenize) used in this zip");
504 			case 9 :
505 				pefree(entry.filename, entry.is_persistent);
506 				PHAR_ZIP_FAIL("unsupported compression method (Deflate64) used in this zip");
507 			case 10 :
508 				pefree(entry.filename, entry.is_persistent);
509 				PHAR_ZIP_FAIL("unsupported compression method (PKWare Implode/old IBM TERSE) used in this zip");
510 			case 14 :
511 				pefree(entry.filename, entry.is_persistent);
512 				PHAR_ZIP_FAIL("unsupported compression method (LZMA) used in this zip");
513 			case 18 :
514 				pefree(entry.filename, entry.is_persistent);
515 				PHAR_ZIP_FAIL("unsupported compression method (IBM TERSE) used in this zip");
516 			case 19 :
517 				pefree(entry.filename, entry.is_persistent);
518 				PHAR_ZIP_FAIL("unsupported compression method (IBM LZ77) used in this zip");
519 			case 97 :
520 				pefree(entry.filename, entry.is_persistent);
521 				PHAR_ZIP_FAIL("unsupported compression method (WavPack) used in this zip");
522 			case 98 :
523 				pefree(entry.filename, entry.is_persistent);
524 				PHAR_ZIP_FAIL("unsupported compression method (PPMd) used in this zip");
525 			default :
526 				pefree(entry.filename, entry.is_persistent);
527 				PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip");
528 		}
529 
530 		/* get file metadata */
531 		if (PHAR_GET_16(zipentry.comment_len)) {
532 			if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) {
533 				pefree(entry.filename, entry.is_persistent);
534 				PHAR_ZIP_FAIL("unable to read in file comment, truncated");
535 			}
536 
537 			p = buf;
538 			entry.metadata_len = PHAR_GET_16(zipentry.comment_len);
539 
540 			if (phar_parse_metadata(&p, &(entry.metadata), PHAR_GET_16(zipentry.comment_len) TSRMLS_CC) == FAILURE) {
541 				entry.metadata_len = 0;
542 				/* if not valid serialized data, it is a regular string */
543 
544 				if (entry.is_persistent) {
545 					ALLOC_PERMANENT_ZVAL(entry.metadata);
546 				} else {
547 					ALLOC_ZVAL(entry.metadata);
548 				}
549 
550 				INIT_ZVAL(*entry.metadata);
551 				ZVAL_STRINGL(entry.metadata, pestrndup(buf, PHAR_GET_16(zipentry.comment_len), entry.is_persistent), PHAR_GET_16(zipentry.comment_len), 0);
552 			}
553 		} else {
554 			entry.metadata = NULL;
555 		}
556 
557 		if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
558 			php_stream_filter *filter;
559 			off_t saveloc;
560 			/* verify local file header */
561 			phar_zip_file_header local;
562 
563 			/* archive alias found */
564 			saveloc = php_stream_tell(fp);
565 			php_stream_seek(fp, PHAR_GET_32(zipentry.offset), SEEK_SET);
566 
567 			if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) {
568 				pefree(entry.filename, entry.is_persistent);
569 				PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (cannot read local file header for alias)");
570 			}
571 
572 			/* verify local header */
573 			if (entry.filename_len != PHAR_GET_16(local.filename_len) || entry.crc32 != PHAR_GET_32(local.crc32) || entry.uncompressed_filesize != PHAR_GET_32(local.uncompsize) || entry.compressed_filesize != PHAR_GET_32(local.compsize)) {
574 				pefree(entry.filename, entry.is_persistent);
575 				PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)");
576 			}
577 
578 			/* construct actual offset to file start - local extra_len can be different from central extra_len */
579 			entry.offset = entry.offset_abs =
580 				sizeof(local) + entry.header_offset + PHAR_GET_16(local.filename_len) + PHAR_GET_16(local.extra_len);
581 #if PHP_VERSION_ID < 50207
582 			/* work around Bug #46147 */
583 			fp->writepos = fp->readpos = 0;
584 #endif
585 			php_stream_seek(fp, entry.offset, SEEK_SET);
586 			/* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */
587 			fp->writepos = 0;
588 			fp->readpos = 0;
589 			php_stream_seek(fp, entry.offset, SEEK_SET);
590 			fp->writepos = 0;
591 			fp->readpos = 0;
592 			/* the above lines should be for php < 5.2.6 after 5.3 filters are fixed */
593 
594 			mydata->alias_len = entry.uncompressed_filesize;
595 
596 			if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
597 				filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
598 
599 				if (!filter) {
600 					pefree(entry.filename, entry.is_persistent);
601 					PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
602 				}
603 
604 				php_stream_filter_append(&fp->readfilters, filter);
605 
606 				if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
607 					pefree(entry.filename, entry.is_persistent);
608 #if PHP_VERSION_ID < 50207
609 					PHAR_ZIP_FAIL("unable to read in alias, truncated (PHP 5.2.7 and newer has a potential fix for this problem)");
610 #endif
611 					PHAR_ZIP_FAIL("unable to read in alias, truncated");
612 				}
613 
614 				php_stream_filter_flush(filter, 1);
615 				php_stream_filter_remove(filter, 1 TSRMLS_CC);
616 
617 			} else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
618 				filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
619 
620 				if (!filter) {
621 					pefree(entry.filename, entry.is_persistent);
622 					PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
623 				}
624 
625 				php_stream_filter_append(&fp->readfilters, filter);
626 
627 				if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
628 					pefree(entry.filename, entry.is_persistent);
629 #if PHP_VERSION_ID < 50207
630 					PHAR_ZIP_FAIL("unable to read in alias, truncated (PHP 5.2.7 and newer has a potential fix for this problem)");
631 #endif
632 					PHAR_ZIP_FAIL("unable to read in alias, truncated");
633 				}
634 
635 				php_stream_filter_flush(filter, 1);
636 				php_stream_filter_remove(filter, 1 TSRMLS_CC);
637 			} else {
638 				if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
639 					pefree(entry.filename, entry.is_persistent);
640 					PHAR_ZIP_FAIL("unable to read in alias, truncated");
641 				}
642 			}
643 
644 			/* return to central directory parsing */
645 			php_stream_seek(fp, saveloc, SEEK_SET);
646 		}
647 
648 		phar_set_inode(&entry TSRMLS_CC);
649 		zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
650 	}
651 
652 	mydata->fp = fp;
653 
654 	if (zend_hash_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
655 		mydata->is_data = 0;
656 	} else {
657 		mydata->is_data = 1;
658 	}
659 
660 	zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
661 
662 	if (actual_alias) {
663 		phar_archive_data **fd_ptr;
664 
665 		if (!phar_validate_alias(actual_alias, mydata->alias_len)) {
666 			if (error) {
667 				spprintf(error, 4096, "phar error: invalid alias \"%s\" in zip-based phar \"%s\"", actual_alias, fname);
668 			}
669 			efree(actual_alias);
670 			zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
671 			return FAILURE;
672 		}
673 
674 		mydata->is_temporary_alias = 0;
675 
676 		if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void **)&fd_ptr)) {
677 			if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, mydata->alias_len TSRMLS_CC)) {
678 				if (error) {
679 					spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with implicit alias, alias is already in use", fname);
680 				}
681 				efree(actual_alias);
682 				zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
683 				return FAILURE;
684 			}
685 		}
686 
687 		mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias;
688 
689 		if (entry.is_persistent) {
690 			efree(actual_alias);
691 		}
692 
693 		zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
694 	} else {
695 		phar_archive_data **fd_ptr;
696 
697 		if (alias_len) {
698 			if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
699 				if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
700 					if (error) {
701 						spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with explicit alias, alias is already in use", fname);
702 					}
703 					zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
704 					return FAILURE;
705 				}
706 			}
707 
708 			zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
709 			mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent);
710 			mydata->alias_len = alias_len;
711 		} else {
712 			mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent);
713 			mydata->alias_len = fname_len;
714 		}
715 
716 		mydata->is_temporary_alias = 1;
717 	}
718 
719 	if (pphar) {
720 		*pphar = mydata;
721 	}
722 
723 	return SUCCESS;
724 }
725 /* }}} */
726 
727 /**
728  * Create or open a zip-based phar for writing
729  */
phar_open_or_create_zip(char * fname,int fname_len,char * alias,int alias_len,int is_data,int options,phar_archive_data ** pphar,char ** error TSRMLS_DC)730 int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
731 {
732 	phar_archive_data *phar;
733 	int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error TSRMLS_CC);
734 
735 	if (FAILURE == ret) {
736 		return FAILURE;
737 	}
738 
739 	if (pphar) {
740 		*pphar = phar;
741 	}
742 
743 	phar->is_data = is_data;
744 
745 	if (phar->is_zip) {
746 		return ret;
747 	}
748 
749 	if (phar->is_brandnew) {
750 		phar->internal_file_start = 0;
751 		phar->is_zip = 1;
752 		phar->is_tar = 0;
753 		return SUCCESS;
754 	}
755 
756 	/* we've reached here - the phar exists and is a regular phar */
757 	if (error) {
758 		spprintf(error, 4096, "phar zip error: phar \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a zip-based phar", fname);
759 	}
760 
761 	return FAILURE;
762 }
763 /* }}} */
764 
765 struct _phar_zip_pass {
766 	php_stream *filefp;
767 	php_stream *centralfp;
768 	php_stream *old;
769 	int free_fp;
770 	int free_ufp;
771 	char **error;
772 };
773 /* perform final modification of zip contents for each file in the manifest before saving */
phar_zip_changed_apply(void * data,void * arg TSRMLS_DC)774 static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */
775 {
776 	phar_entry_info *entry;
777 	phar_zip_file_header local;
778 	phar_zip_unix3 perms;
779 	phar_zip_central_dir_file central;
780 	struct _phar_zip_pass *p;
781 	php_uint32 newcrc32;
782 	off_t offset;
783 	int not_really_modified = 0;
784 	entry = (phar_entry_info *)data;
785 	p = (struct _phar_zip_pass*) arg;
786 
787 	if (entry->is_mounted) {
788 		return ZEND_HASH_APPLY_KEEP;
789 	}
790 
791 	if (entry->is_deleted) {
792 		if (entry->fp_refcount <= 0) {
793 			return ZEND_HASH_APPLY_REMOVE;
794 		} else {
795 			/* we can't delete this in-memory until it is closed */
796 			return ZEND_HASH_APPLY_KEEP;
797 		}
798 	}
799 
800 	phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len TSRMLS_CC);
801 	memset(&local, 0, sizeof(local));
802 	memset(&central, 0, sizeof(central));
803 	memset(&perms, 0, sizeof(perms));
804 	strncpy(local.signature, "PK\3\4", 4);
805 	strncpy(central.signature, "PK\1\2", 4);
806 	PHAR_SET_16(central.extra_len, sizeof(perms));
807 	PHAR_SET_16(local.extra_len, sizeof(perms));
808 	perms.tag[0] = 'n';
809 	perms.tag[1] = 'u';
810 	PHAR_SET_16(perms.size, sizeof(perms) - 4);
811 	PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
812 	{
813 		php_uint32 crc = (php_uint32) ~0;
814 		CRC32(crc, perms.perms[0]);
815 		CRC32(crc, perms.perms[1]);
816 		PHAR_SET_32(perms.crc32, ~crc);
817 	}
818 
819 	if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
820 		PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_DEFLATE);
821 		PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_DEFLATE);
822 	}
823 
824 	if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
825 		PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_BZIP2);
826 		PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_BZIP2);
827 	}
828 
829 	/* do not use PHAR_GET_16 on either field of the next line */
830 	phar_zip_u2d_time(entry->timestamp, local.timestamp, local.datestamp);
831 	memcpy(central.timestamp, local.timestamp, sizeof(local.timestamp));
832 	memcpy(central.datestamp, local.datestamp, sizeof(local.datestamp));
833 	PHAR_SET_16(central.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
834 	PHAR_SET_16(local.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
835 	PHAR_SET_32(central.offset, php_stream_tell(p->filefp));
836 
837 	/* do extra field for perms later */
838 	if (entry->is_modified) {
839 		php_uint32 loc;
840 		php_stream_filter *filter;
841 		php_stream *efp;
842 
843 		if (entry->is_dir) {
844 			entry->is_modified = 0;
845 			if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
846 				php_stream_close(entry->fp);
847 				entry->fp = NULL;
848 				entry->fp_type = PHAR_FP;
849 			}
850 			goto continue_dir;
851 		}
852 
853 		if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) {
854 			spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
855 			return ZEND_HASH_APPLY_STOP;
856 		}
857 
858 		/* we can be modified and already be compressed, such as when chmod() is executed */
859 		if (entry->flags & PHAR_ENT_COMPRESSION_MASK && (entry->old_flags == entry->flags || !entry->old_flags)) {
860 			not_really_modified = 1;
861 			goto is_compressed;
862 		}
863 
864 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
865 			spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
866 			return ZEND_HASH_APPLY_STOP;
867 		}
868 
869 		efp = phar_get_efp(entry, 0 TSRMLS_CC);
870 		newcrc32 = ~0;
871 
872 		for (loc = 0;loc < entry->uncompressed_filesize; ++loc) {
873 			CRC32(newcrc32, php_stream_getc(efp));
874 		}
875 
876 		entry->crc32 = ~newcrc32;
877 		PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
878 		PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
879 
880 		if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
881 			/* not compressed */
882 			entry->compressed_filesize = entry->uncompressed_filesize;
883 			PHAR_SET_32(central.compsize, entry->uncompressed_filesize);
884 			PHAR_SET_32(local.compsize, entry->uncompressed_filesize);
885 			goto not_compressed;
886 		}
887 
888 		filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
889 
890 		if (!filter) {
891 			if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
892 				spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
893 			} else {
894 				spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
895 			}
896 			return ZEND_HASH_APPLY_STOP;
897 		}
898 
899 		/* create new file that holds the compressed version */
900 		/* work around inability to specify freedom in write and strictness
901 		in read count */
902 		entry->cfp = php_stream_fopen_tmpfile();
903 
904 		if (!entry->cfp) {
905 			spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
906 			return ZEND_HASH_APPLY_STOP;
907 		}
908 
909 		php_stream_flush(efp);
910 
911 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
912 			spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
913 			return ZEND_HASH_APPLY_STOP;
914 		}
915 
916 		php_stream_filter_append((&entry->cfp->writefilters), filter);
917 
918 		if (SUCCESS != phar_stream_copy_to_stream(efp, entry->cfp, entry->uncompressed_filesize, NULL)) {
919 			spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
920 			return ZEND_HASH_APPLY_STOP;
921 		}
922 
923 		php_stream_filter_flush(filter, 1);
924 		php_stream_flush(entry->cfp);
925 		php_stream_filter_remove(filter, 1 TSRMLS_CC);
926 		php_stream_seek(entry->cfp, 0, SEEK_END);
927 		entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
928 		PHAR_SET_32(central.compsize, entry->compressed_filesize);
929 		PHAR_SET_32(local.compsize, entry->compressed_filesize);
930 		/* generate crc on compressed file */
931 		php_stream_rewind(entry->cfp);
932 		entry->old_flags = entry->flags;
933 		entry->is_modified = 1;
934 	} else {
935 is_compressed:
936 		PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
937 		PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
938 		PHAR_SET_32(central.compsize, entry->compressed_filesize);
939 		PHAR_SET_32(local.compsize, entry->compressed_filesize);
940 		if (p->old) {
941 			if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
942 				spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
943 				return ZEND_HASH_APPLY_STOP;
944 			}
945 		}
946 	}
947 not_compressed:
948 	PHAR_SET_32(central.crc32, entry->crc32);
949 	PHAR_SET_32(local.crc32, entry->crc32);
950 continue_dir:
951 	/* set file metadata */
952 	if (entry->metadata) {
953 		php_serialize_data_t metadata_hash;
954 
955 		if (entry->metadata_str.c) {
956 			smart_str_free(&entry->metadata_str);
957 		}
958 		entry->metadata_str.c = 0;
959 		entry->metadata_str.len = 0;
960 		PHP_VAR_SERIALIZE_INIT(metadata_hash);
961 		php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
962 		PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
963 		PHAR_SET_16(central.comment_len, entry->metadata_str.len);
964 	}
965 
966 	entry->header_offset = php_stream_tell(p->filefp);
967 	offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms);
968 
969 	if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
970 		spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
971 		return ZEND_HASH_APPLY_STOP;
972 	}
973 
974 	if (sizeof(central) != php_stream_write(p->centralfp, (char *)&central, sizeof(central))) {
975 		spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
976 		return ZEND_HASH_APPLY_STOP;
977 	}
978 
979 	if (entry->is_dir) {
980 		if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
981 			spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
982 			return ZEND_HASH_APPLY_STOP;
983 		}
984 
985 		if (1 != php_stream_write(p->filefp, "/", 1)) {
986 			spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
987 			return ZEND_HASH_APPLY_STOP;
988 		}
989 
990 		if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
991 			spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
992 			return ZEND_HASH_APPLY_STOP;
993 		}
994 
995 		if (1 != php_stream_write(p->centralfp, "/", 1)) {
996 			spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
997 			return ZEND_HASH_APPLY_STOP;
998 		}
999 	} else {
1000 		if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1001 			spprintf(p->error, 0, "unable to write filename to local directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1002 			return ZEND_HASH_APPLY_STOP;
1003 		}
1004 
1005 		if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1006 			spprintf(p->error, 0, "unable to write filename to central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1007 			return ZEND_HASH_APPLY_STOP;
1008 		}
1009 	}
1010 
1011 	if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
1012 		spprintf(p->error, 0, "unable to write local extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1013 		return ZEND_HASH_APPLY_STOP;
1014 	}
1015 
1016 	if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
1017 		spprintf(p->error, 0, "unable to write central extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1018 		return ZEND_HASH_APPLY_STOP;
1019 	}
1020 
1021 	if (!not_really_modified && entry->is_modified) {
1022 		if (entry->cfp) {
1023 			if (SUCCESS != phar_stream_copy_to_stream(entry->cfp, p->filefp, entry->compressed_filesize, NULL)) {
1024 				spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1025 				return ZEND_HASH_APPLY_STOP;
1026 			}
1027 
1028 			php_stream_close(entry->cfp);
1029 			entry->cfp = NULL;
1030 		} else {
1031 			if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) {
1032 				return ZEND_HASH_APPLY_STOP;
1033 			}
1034 
1035 			phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC);
1036 
1037 			if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), p->filefp, entry->uncompressed_filesize, NULL)) {
1038 				spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1039 				return ZEND_HASH_APPLY_STOP;
1040 			}
1041 		}
1042 
1043 		if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
1044 			php_stream_close(entry->fp);
1045 		}
1046 
1047 		entry->is_modified = 0;
1048 	} else {
1049 		entry->is_modified = 0;
1050 		if (entry->fp_refcount) {
1051 			/* open file pointers refer to this fp, do not free the stream */
1052 			switch (entry->fp_type) {
1053 				case PHAR_FP:
1054 					p->free_fp = 0;
1055 					break;
1056 				case PHAR_UFP:
1057 					p->free_ufp = 0;
1058 				default:
1059 					break;
1060 			}
1061 		}
1062 
1063 		if (!entry->is_dir && entry->compressed_filesize && SUCCESS != phar_stream_copy_to_stream(p->old, p->filefp, entry->compressed_filesize, NULL)) {
1064 			spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1065 			return ZEND_HASH_APPLY_STOP;
1066 		}
1067 	}
1068 
1069 	entry->fp = NULL;
1070 	entry->offset = entry->offset_abs = offset;
1071 	entry->fp_type = PHAR_FP;
1072 
1073 	if (entry->metadata_str.c) {
1074 		if (entry->metadata_str.len != php_stream_write(p->centralfp, entry->metadata_str.c, entry->metadata_str.len)) {
1075 			spprintf(p->error, 0, "unable to write metadata as file comment for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1076 			smart_str_free(&entry->metadata_str);
1077 			return ZEND_HASH_APPLY_STOP;
1078 		}
1079 
1080 		smart_str_free(&entry->metadata_str);
1081 	}
1082 
1083 	return ZEND_HASH_APPLY_KEEP;
1084 }
1085 /* }}} */
1086 
phar_zip_applysignature(phar_archive_data * phar,struct _phar_zip_pass * pass,smart_str * metadata TSRMLS_DC)1087 static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass,
1088 				   smart_str *metadata TSRMLS_DC) /* {{{ */
1089 {
1090 	/* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1091 	if (!phar->is_data || phar->sig_flags) {
1092 		int signature_length;
1093 		char *signature, sigbuf[8];
1094 		phar_entry_info entry = {0};
1095 		php_stream *newfile;
1096 		off_t tell, st;
1097 
1098 		newfile = php_stream_fopen_tmpfile();
1099 		if (newfile == NULL) {
1100 			spprintf(pass->error, 0, "phar error: unable to create temporary file for the signature file");
1101 			return FAILURE;
1102 		}
1103 		st = tell = php_stream_tell(pass->filefp);
1104 		/* copy the local files, central directory, and the zip comment to generate the hash */
1105 		php_stream_seek(pass->filefp, 0, SEEK_SET);
1106 		phar_stream_copy_to_stream(pass->filefp, newfile, tell, NULL);
1107 		tell = php_stream_tell(pass->centralfp);
1108 		php_stream_seek(pass->centralfp, 0, SEEK_SET);
1109 		phar_stream_copy_to_stream(pass->centralfp, newfile, tell, NULL);
1110 		if (metadata->c) {
1111 			php_stream_write(newfile, metadata->c, metadata->len);
1112 		}
1113 
1114 		if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error TSRMLS_CC)) {
1115 			if (pass->error) {
1116 				char *save = *(pass->error);
1117 				spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
1118 				efree(save);
1119 			}
1120 
1121 			php_stream_close(newfile);
1122 			return FAILURE;
1123 		}
1124 
1125 		entry.filename = ".phar/signature.bin";
1126 		entry.filename_len = sizeof(".phar/signature.bin")-1;
1127 		entry.fp = php_stream_fopen_tmpfile();
1128 		entry.fp_type = PHAR_MOD;
1129 		entry.is_modified = 1;
1130 		if (entry.fp == NULL) {
1131 			spprintf(pass->error, 0, "phar error: unable to create temporary file for signature");
1132 			return FAILURE;
1133 		}
1134 
1135 		PHAR_SET_32(sigbuf, phar->sig_flags);
1136 		PHAR_SET_32(sigbuf + 4, signature_length);
1137 
1138 		if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) {
1139 			efree(signature);
1140 			if (pass->error) {
1141 				spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
1142 			}
1143 
1144 			php_stream_close(newfile);
1145 			return FAILURE;
1146 		}
1147 
1148 		efree(signature);
1149 		entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1150 		entry.phar = phar;
1151 		/* throw out return value and write the signature */
1152 		phar_zip_changed_apply((void *)&entry, (void *)pass TSRMLS_CC);
1153 		php_stream_close(newfile);
1154 
1155 		if (pass->error && *(pass->error)) {
1156 			/* error is set by writeheaders */
1157 			php_stream_close(newfile);
1158 			return FAILURE;
1159 		}
1160 	} /* signature */
1161 	return SUCCESS;
1162 }
1163 /* }}} */
1164 
phar_zip_flush(phar_archive_data * phar,char * user_stub,long len,int defaultstub,char ** error TSRMLS_DC)1165 int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */
1166 {
1167 	char *pos;
1168 	smart_str main_metadata_str = {0};
1169 	static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
1170 	char halt_stub[] = "__HALT_COMPILER();";
1171 	char *tmp;
1172 
1173 	php_stream *stubfile, *oldfile;
1174 	php_serialize_data_t metadata_hash;
1175 	int free_user_stub, closeoldfile = 0;
1176 	phar_entry_info entry = {0};
1177 	char *temperr = NULL;
1178 	struct _phar_zip_pass pass;
1179 	phar_zip_dir_end eocd;
1180 	php_uint32 cdir_size, cdir_offset;
1181 
1182 	pass.error = &temperr;
1183 	entry.flags = PHAR_ENT_PERM_DEF_FILE;
1184 	entry.timestamp = time(NULL);
1185 	entry.is_modified = 1;
1186 	entry.is_zip = 1;
1187 	entry.phar = phar;
1188 	entry.fp_type = PHAR_MOD;
1189 
1190 	if (phar->is_persistent) {
1191 		if (error) {
1192 			spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
1193 		}
1194 		return EOF;
1195 	}
1196 
1197 	if (phar->is_data) {
1198 		goto nostub;
1199 	}
1200 
1201 	/* set alias */
1202 	if (!phar->is_temporary_alias && phar->alias_len) {
1203 		entry.fp = php_stream_fopen_tmpfile();
1204 		if (entry.fp == NULL) {
1205 			spprintf(error, 0, "phar error: unable to create temporary file");
1206 			return EOF;
1207 		}
1208 		if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
1209 			if (error) {
1210 				spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1211 			}
1212 			return EOF;
1213 		}
1214 
1215 		entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
1216 		entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1217 		entry.filename_len = sizeof(".phar/alias.txt")-1;
1218 
1219 		if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1220 			if (error) {
1221 				spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1222 			}
1223 			return EOF;
1224 		}
1225 	} else {
1226 		zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1227 	}
1228 
1229 	/* register alias */
1230 	if (phar->alias_len) {
1231 		if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error TSRMLS_CC)) {
1232 			return EOF;
1233 		}
1234 	}
1235 
1236 	/* set stub */
1237 	if (user_stub && !defaultstub) {
1238 		if (len < 0) {
1239 			/* resource passed in */
1240 			if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
1241 				if (error) {
1242 					spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1243 				}
1244 				return EOF;
1245 			}
1246 
1247 			if (len == -1) {
1248 				len = PHP_STREAM_COPY_ALL;
1249 			} else {
1250 				len = -len;
1251 			}
1252 
1253 			user_stub = 0;
1254 
1255 			if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
1256 				if (error) {
1257 					spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1258 				}
1259 				return EOF;
1260 			}
1261 			free_user_stub = 1;
1262 		} else {
1263 			free_user_stub = 0;
1264 		}
1265 
1266 		tmp = estrndup(user_stub, len);
1267 		if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1268 			efree(tmp);
1269 			if (error) {
1270 				spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
1271 			}
1272 			if (free_user_stub) {
1273 				efree(user_stub);
1274 			}
1275 			return EOF;
1276 		}
1277 		pos = user_stub + (pos - tmp);
1278 		efree(tmp);
1279 
1280 		len = pos - user_stub + 18;
1281 		entry.fp = php_stream_fopen_tmpfile();
1282 		if (entry.fp == NULL) {
1283 			spprintf(error, 0, "phar error: unable to create temporary file");
1284 			return EOF;
1285 		}
1286 		entry.uncompressed_filesize = len + 5;
1287 
1288 		if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1289 		||            5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1290 			if (error) {
1291 				spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
1292 			}
1293 			if (free_user_stub) {
1294 				efree(user_stub);
1295 			}
1296 			php_stream_close(entry.fp);
1297 			return EOF;
1298 		}
1299 
1300 		entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1301 		entry.filename_len = sizeof(".phar/stub.php")-1;
1302 
1303 		if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1304 			if (free_user_stub) {
1305 				efree(user_stub);
1306 			}
1307 			if (error) {
1308 				spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", phar->fname);
1309 			}
1310 			return EOF;
1311 		}
1312 
1313 		if (free_user_stub) {
1314 			efree(user_stub);
1315 		}
1316 	} else {
1317 		/* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1318 		entry.fp = php_stream_fopen_tmpfile();
1319 		if (entry.fp == NULL) {
1320 			spprintf(error, 0, "phar error: unable to create temporary file");
1321 			return EOF;
1322 		}
1323 		if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1324 			php_stream_close(entry.fp);
1325 			if (error) {
1326 				spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1327 			}
1328 			return EOF;
1329 		}
1330 
1331 		entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1332 		entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1333 		entry.filename_len = sizeof(".phar/stub.php")-1;
1334 
1335 		if (!defaultstub) {
1336 			if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1337 				if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1338 					php_stream_close(entry.fp);
1339 					efree(entry.filename);
1340 					if (error) {
1341 						spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
1342 					}
1343 					return EOF;
1344 				}
1345 			} else {
1346 				php_stream_close(entry.fp);
1347 				efree(entry.filename);
1348 			}
1349 		} else {
1350 			if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1351 				php_stream_close(entry.fp);
1352 				efree(entry.filename);
1353 				if (error) {
1354 					spprintf(error, 0, "unable to overwrite stub in zip-based phar \"%s\"", phar->fname);
1355 				}
1356 				return EOF;
1357 			}
1358 		}
1359 	}
1360 nostub:
1361 	if (phar->fp && !phar->is_brandnew) {
1362 		oldfile = phar->fp;
1363 		closeoldfile = 0;
1364 		php_stream_rewind(oldfile);
1365 	} else {
1366 		oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1367 		closeoldfile = oldfile != NULL;
1368 	}
1369 
1370 	/* save modified files to the zip */
1371 	pass.old = oldfile;
1372 	pass.filefp = php_stream_fopen_tmpfile();
1373 
1374 	if (!pass.filefp) {
1375 fperror:
1376 		if (closeoldfile) {
1377 			php_stream_close(oldfile);
1378 		}
1379 		if (error) {
1380 			spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
1381 		}
1382 		return EOF;
1383 	}
1384 
1385 	pass.centralfp = php_stream_fopen_tmpfile();
1386 
1387 	if (!pass.centralfp) {
1388 		goto fperror;
1389 	}
1390 
1391 	pass.free_fp = pass.free_ufp = 1;
1392 	memset(&eocd, 0, sizeof(eocd));
1393 
1394 	strncpy(eocd.signature, "PK\5\6", 4);
1395 	if (!phar->is_data && !phar->sig_flags) {
1396 		phar->sig_flags = PHAR_SIG_SHA1;
1397 	}
1398 	if (phar->sig_flags) {
1399 		PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
1400 		PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
1401 	} else {
1402 		PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
1403 		PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
1404 	}
1405 	zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass TSRMLS_CC);
1406 
1407 	if (phar->metadata) {
1408 		/* set phar metadata */
1409 		PHP_VAR_SERIALIZE_INIT(metadata_hash);
1410 		php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
1411 		PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
1412 	}
1413 	if (temperr) {
1414 		if (error) {
1415 			spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
1416 		}
1417 		efree(temperr);
1418 temperror:
1419 		php_stream_close(pass.centralfp);
1420 nocentralerror:
1421 		if (phar->metadata) {
1422 			smart_str_free(&main_metadata_str);
1423 		}
1424 		php_stream_close(pass.filefp);
1425 		if (closeoldfile) {
1426 			php_stream_close(oldfile);
1427 		}
1428 		return EOF;
1429 	}
1430 
1431 	if (FAILURE == phar_zip_applysignature(phar, &pass, &main_metadata_str TSRMLS_CC)) {
1432 		goto temperror;
1433 	}
1434 
1435 	/* save zip */
1436 	cdir_size = php_stream_tell(pass.centralfp);
1437 	cdir_offset = php_stream_tell(pass.filefp);
1438 	PHAR_SET_32(eocd.cdir_size, cdir_size);
1439 	PHAR_SET_32(eocd.cdir_offset, cdir_offset);
1440 	php_stream_seek(pass.centralfp, 0, SEEK_SET);
1441 
1442 	{
1443 		size_t clen;
1444 		int ret = phar_stream_copy_to_stream(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
1445 		if (SUCCESS != ret || clen != cdir_size) {
1446 			if (error) {
1447 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
1448 			}
1449 			goto temperror;
1450 		}
1451 	}
1452 
1453 	php_stream_close(pass.centralfp);
1454 
1455 	if (phar->metadata) {
1456 		/* set phar metadata */
1457 		PHAR_SET_16(eocd.comment_len, main_metadata_str.len);
1458 
1459 		if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1460 			if (error) {
1461 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1462 			}
1463 			goto nocentralerror;
1464 		}
1465 
1466 		if (main_metadata_str.len != php_stream_write(pass.filefp, main_metadata_str.c, main_metadata_str.len)) {
1467 			if (error) {
1468 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
1469 			}
1470 			goto nocentralerror;
1471 		}
1472 
1473 		smart_str_free(&main_metadata_str);
1474 
1475 	} else {
1476 		if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1477 			if (error) {
1478 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1479 			}
1480 			goto nocentralerror;
1481 		}
1482 	}
1483 
1484 	if (phar->fp && pass.free_fp) {
1485 		php_stream_close(phar->fp);
1486 	}
1487 
1488 	if (phar->ufp) {
1489 		if (pass.free_ufp) {
1490 			php_stream_close(phar->ufp);
1491 		}
1492 		phar->ufp = NULL;
1493 	}
1494 
1495 	/* re-open */
1496 	phar->is_brandnew = 0;
1497 
1498 	if (phar->donotflush) {
1499 		/* deferred flush */
1500 		phar->fp = pass.filefp;
1501 	} else {
1502 		phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1503 		if (!phar->fp) {
1504 			if (closeoldfile) {
1505 				php_stream_close(oldfile);
1506 			}
1507 			phar->fp = pass.filefp;
1508 			if (error) {
1509 				spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
1510 			}
1511 			return EOF;
1512 		}
1513 		php_stream_rewind(pass.filefp);
1514 		phar_stream_copy_to_stream(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1515 		/* we could also reopen the file in "rb" mode but there is no need for that */
1516 		php_stream_close(pass.filefp);
1517 	}
1518 
1519 	if (closeoldfile) {
1520 		php_stream_close(oldfile);
1521 	}
1522 	return EOF;
1523 }
1524 /* }}} */
1525 
1526 /*
1527  * Local variables:
1528  * tab-width: 4
1529  * c-basic-offset: 4
1530  * End:
1531  * vim600: noet sw=4 ts=4 fdm=marker
1532  * vim<600: noet sw=4 ts=4
1533  */
1534