xref: /PHP-5.5/ext/phar/zip.c (revision a6fdc5bb)
1 /*
2   +----------------------------------------------------------------------+
3   | ZIP archive support for Phar                                         |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2007-2015 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 ((p - buf) + sizeof(locator) <= size && !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 			if(entry.filename_len > 1) {
400 				entry.filename_len--;
401 			}
402 			entry.flags |= PHAR_ENT_PERM_DEF_DIR;
403 		} else {
404 			entry.is_dir = 0;
405 		}
406 
407 		if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
408 			size_t read;
409 			php_stream *sigfile;
410 			off_t now;
411 			char *sig;
412 
413 			now = php_stream_tell(fp);
414 			pefree(entry.filename, entry.is_persistent);
415 			sigfile = php_stream_fopen_tmpfile();
416 			if (!sigfile) {
417 				PHAR_ZIP_FAIL("couldn't open temporary file");
418 			}
419 
420 			php_stream_seek(fp, 0, SEEK_SET);
421 			/* copy file contents + local headers and zip comment, if any, to be hashed for signature */
422 			phar_stream_copy_to_stream(fp, sigfile, entry.header_offset, NULL);
423 			/* seek to central directory */
424 			php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
425 			/* copy central directory header */
426 			phar_stream_copy_to_stream(fp, sigfile, beforeus - PHAR_GET_32(locator.cdir_offset), NULL);
427 			if (metadata) {
428 				php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
429 			}
430 			php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
431 			sig = (char *) emalloc(entry.uncompressed_filesize);
432 			read = php_stream_read(fp, sig, entry.uncompressed_filesize);
433 			if (read != entry.uncompressed_filesize) {
434 				php_stream_close(sigfile);
435 				efree(sig);
436 				PHAR_ZIP_FAIL("signature cannot be read");
437 			}
438 			mydata->sig_flags = PHAR_GET_32(sig);
439 			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)) {
440 				efree(sig);
441 				if (error) {
442 					char *save;
443 					php_stream_close(sigfile);
444 					spprintf(&save, 4096, "signature cannot be verified: %s", *error);
445 					efree(*error);
446 					PHAR_ZIP_FAIL_FREE(save, save);
447 				} else {
448 					php_stream_close(sigfile);
449 					PHAR_ZIP_FAIL("signature cannot be verified");
450 				}
451 			}
452 			php_stream_close(sigfile);
453 			efree(sig);
454 			/* signature checked out, let's ensure this is the last file in the phar */
455 			if (i != PHAR_GET_16(locator.count) - 1) {
456 				PHAR_ZIP_FAIL("entries exist after signature, invalid phar");
457 			}
458 
459 			continue;
460 		}
461 
462 		phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len TSRMLS_CC);
463 
464 		if (PHAR_GET_16(zipentry.extra_len)) {
465 			off_t loc = php_stream_tell(fp);
466 			if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len) TSRMLS_CC)) {
467 				pefree(entry.filename, entry.is_persistent);
468 				PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
469 			}
470 			php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
471 		}
472 
473 		switch (PHAR_GET_16(zipentry.compressed)) {
474 			case PHAR_ZIP_COMP_NONE :
475 				/* compression flag already set */
476 				break;
477 			case PHAR_ZIP_COMP_DEFLATE :
478 				entry.flags |= PHAR_ENT_COMPRESSED_GZ;
479 				if (!PHAR_G(has_zlib)) {
480 					pefree(entry.filename, entry.is_persistent);
481 					PHAR_ZIP_FAIL("zlib extension is required");
482 				}
483 				break;
484 			case PHAR_ZIP_COMP_BZIP2 :
485 				entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
486 				if (!PHAR_G(has_bz2)) {
487 					pefree(entry.filename, entry.is_persistent);
488 					PHAR_ZIP_FAIL("bzip2 extension is required");
489 				}
490 				break;
491 			case 1 :
492 				pefree(entry.filename, entry.is_persistent);
493 				PHAR_ZIP_FAIL("unsupported compression method (Shrunk) used in this zip");
494 			case 2 :
495 			case 3 :
496 			case 4 :
497 			case 5 :
498 				pefree(entry.filename, entry.is_persistent);
499 				PHAR_ZIP_FAIL("unsupported compression method (Reduce) used in this zip");
500 			case 6 :
501 				pefree(entry.filename, entry.is_persistent);
502 				PHAR_ZIP_FAIL("unsupported compression method (Implode) used in this zip");
503 			case 7 :
504 				pefree(entry.filename, entry.is_persistent);
505 				PHAR_ZIP_FAIL("unsupported compression method (Tokenize) used in this zip");
506 			case 9 :
507 				pefree(entry.filename, entry.is_persistent);
508 				PHAR_ZIP_FAIL("unsupported compression method (Deflate64) used in this zip");
509 			case 10 :
510 				pefree(entry.filename, entry.is_persistent);
511 				PHAR_ZIP_FAIL("unsupported compression method (PKWare Implode/old IBM TERSE) used in this zip");
512 			case 14 :
513 				pefree(entry.filename, entry.is_persistent);
514 				PHAR_ZIP_FAIL("unsupported compression method (LZMA) used in this zip");
515 			case 18 :
516 				pefree(entry.filename, entry.is_persistent);
517 				PHAR_ZIP_FAIL("unsupported compression method (IBM TERSE) used in this zip");
518 			case 19 :
519 				pefree(entry.filename, entry.is_persistent);
520 				PHAR_ZIP_FAIL("unsupported compression method (IBM LZ77) used in this zip");
521 			case 97 :
522 				pefree(entry.filename, entry.is_persistent);
523 				PHAR_ZIP_FAIL("unsupported compression method (WavPack) used in this zip");
524 			case 98 :
525 				pefree(entry.filename, entry.is_persistent);
526 				PHAR_ZIP_FAIL("unsupported compression method (PPMd) used in this zip");
527 			default :
528 				pefree(entry.filename, entry.is_persistent);
529 				PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip");
530 		}
531 
532 		/* get file metadata */
533 		if (PHAR_GET_16(zipentry.comment_len)) {
534 			if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) {
535 				pefree(entry.filename, entry.is_persistent);
536 				PHAR_ZIP_FAIL("unable to read in file comment, truncated");
537 			}
538 
539 			p = buf;
540 			entry.metadata_len = PHAR_GET_16(zipentry.comment_len);
541 
542 			if (phar_parse_metadata(&p, &(entry.metadata), PHAR_GET_16(zipentry.comment_len) TSRMLS_CC) == FAILURE) {
543 				entry.metadata_len = 0;
544 				/* if not valid serialized data, it is a regular string */
545 
546 				if (entry.is_persistent) {
547 					ALLOC_PERMANENT_ZVAL(entry.metadata);
548 				} else {
549 					ALLOC_ZVAL(entry.metadata);
550 				}
551 
552 				INIT_ZVAL(*entry.metadata);
553 				ZVAL_STRINGL(entry.metadata, pestrndup(buf, PHAR_GET_16(zipentry.comment_len), entry.is_persistent), PHAR_GET_16(zipentry.comment_len), 0);
554 			}
555 		} else {
556 			entry.metadata = NULL;
557 		}
558 
559 		if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
560 			php_stream_filter *filter;
561 			off_t saveloc;
562 			/* verify local file header */
563 			phar_zip_file_header local;
564 
565 			/* archive alias found */
566 			saveloc = php_stream_tell(fp);
567 			php_stream_seek(fp, PHAR_GET_32(zipentry.offset), SEEK_SET);
568 
569 			if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) {
570 				pefree(entry.filename, entry.is_persistent);
571 				PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (cannot read local file header for alias)");
572 			}
573 
574 			/* verify local header */
575 			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)) {
576 				pefree(entry.filename, entry.is_persistent);
577 				PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)");
578 			}
579 
580 			/* construct actual offset to file start - local extra_len can be different from central extra_len */
581 			entry.offset = entry.offset_abs =
582 				sizeof(local) + entry.header_offset + PHAR_GET_16(local.filename_len) + PHAR_GET_16(local.extra_len);
583 			php_stream_seek(fp, entry.offset, SEEK_SET);
584 			/* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */
585 			fp->writepos = 0;
586 			fp->readpos = 0;
587 			php_stream_seek(fp, entry.offset, SEEK_SET);
588 			fp->writepos = 0;
589 			fp->readpos = 0;
590 			/* the above lines should be for php < 5.2.6 after 5.3 filters are fixed */
591 
592 			mydata->alias_len = entry.uncompressed_filesize;
593 
594 			if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
595 				filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
596 
597 				if (!filter) {
598 					pefree(entry.filename, entry.is_persistent);
599 					PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
600 				}
601 
602 				php_stream_filter_append(&fp->readfilters, filter);
603 
604 				if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
605 					pefree(entry.filename, entry.is_persistent);
606 					PHAR_ZIP_FAIL("unable to read in alias, truncated");
607 				}
608 
609 				php_stream_filter_flush(filter, 1);
610 				php_stream_filter_remove(filter, 1 TSRMLS_CC);
611 
612 			} else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
613 				filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
614 
615 				if (!filter) {
616 					pefree(entry.filename, entry.is_persistent);
617 					PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
618 				}
619 
620 				php_stream_filter_append(&fp->readfilters, filter);
621 
622 				if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
623 					pefree(entry.filename, entry.is_persistent);
624 					PHAR_ZIP_FAIL("unable to read in alias, truncated");
625 				}
626 
627 				php_stream_filter_flush(filter, 1);
628 				php_stream_filter_remove(filter, 1 TSRMLS_CC);
629 			} else {
630 				if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
631 					pefree(entry.filename, entry.is_persistent);
632 					PHAR_ZIP_FAIL("unable to read in alias, truncated");
633 				}
634 			}
635 
636 			/* return to central directory parsing */
637 			php_stream_seek(fp, saveloc, SEEK_SET);
638 		}
639 
640 		phar_set_inode(&entry TSRMLS_CC);
641 		zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
642 	}
643 
644 	mydata->fp = fp;
645 
646 	if (zend_hash_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
647 		mydata->is_data = 0;
648 	} else {
649 		mydata->is_data = 1;
650 	}
651 
652 	zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
653 
654 	if (actual_alias) {
655 		phar_archive_data **fd_ptr;
656 
657 		if (!phar_validate_alias(actual_alias, mydata->alias_len)) {
658 			if (error) {
659 				spprintf(error, 4096, "phar error: invalid alias \"%s\" in zip-based phar \"%s\"", actual_alias, fname);
660 			}
661 			efree(actual_alias);
662 			zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
663 			return FAILURE;
664 		}
665 
666 		mydata->is_temporary_alias = 0;
667 
668 		if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void **)&fd_ptr)) {
669 			if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, mydata->alias_len TSRMLS_CC)) {
670 				if (error) {
671 					spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with implicit alias, alias is already in use", fname);
672 				}
673 				efree(actual_alias);
674 				zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
675 				return FAILURE;
676 			}
677 		}
678 
679 		mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias;
680 
681 		if (entry.is_persistent) {
682 			efree(actual_alias);
683 		}
684 
685 		zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
686 	} else {
687 		phar_archive_data **fd_ptr;
688 
689 		if (alias_len) {
690 			if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
691 				if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
692 					if (error) {
693 						spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with explicit alias, alias is already in use", fname);
694 					}
695 					zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
696 					return FAILURE;
697 				}
698 			}
699 
700 			zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
701 			mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent);
702 			mydata->alias_len = alias_len;
703 		} else {
704 			mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent);
705 			mydata->alias_len = fname_len;
706 		}
707 
708 		mydata->is_temporary_alias = 1;
709 	}
710 
711 	if (pphar) {
712 		*pphar = mydata;
713 	}
714 
715 	return SUCCESS;
716 }
717 /* }}} */
718 
719 /**
720  * Create or open a zip-based phar for writing
721  */
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)722 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) /* {{{ */
723 {
724 	phar_archive_data *phar;
725 	int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error TSRMLS_CC);
726 
727 	if (FAILURE == ret) {
728 		return FAILURE;
729 	}
730 
731 	if (pphar) {
732 		*pphar = phar;
733 	}
734 
735 	phar->is_data = is_data;
736 
737 	if (phar->is_zip) {
738 		return ret;
739 	}
740 
741 	if (phar->is_brandnew) {
742 		phar->internal_file_start = 0;
743 		phar->is_zip = 1;
744 		phar->is_tar = 0;
745 		return SUCCESS;
746 	}
747 
748 	/* we've reached here - the phar exists and is a regular phar */
749 	if (error) {
750 		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);
751 	}
752 
753 	return FAILURE;
754 }
755 /* }}} */
756 
757 struct _phar_zip_pass {
758 	php_stream *filefp;
759 	php_stream *centralfp;
760 	php_stream *old;
761 	int free_fp;
762 	int free_ufp;
763 	char **error;
764 };
765 /* perform final modification of zip contents for each file in the manifest before saving */
phar_zip_changed_apply(void * data,void * arg TSRMLS_DC)766 static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */
767 {
768 	phar_entry_info *entry;
769 	phar_zip_file_header local;
770 	phar_zip_unix3 perms;
771 	phar_zip_central_dir_file central;
772 	struct _phar_zip_pass *p;
773 	php_uint32 newcrc32;
774 	off_t offset;
775 	int not_really_modified = 0;
776 	entry = (phar_entry_info *)data;
777 	p = (struct _phar_zip_pass*) arg;
778 
779 	if (entry->is_mounted) {
780 		return ZEND_HASH_APPLY_KEEP;
781 	}
782 
783 	if (entry->is_deleted) {
784 		if (entry->fp_refcount <= 0) {
785 			return ZEND_HASH_APPLY_REMOVE;
786 		} else {
787 			/* we can't delete this in-memory until it is closed */
788 			return ZEND_HASH_APPLY_KEEP;
789 		}
790 	}
791 
792 	phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len TSRMLS_CC);
793 	memset(&local, 0, sizeof(local));
794 	memset(&central, 0, sizeof(central));
795 	memset(&perms, 0, sizeof(perms));
796 	strncpy(local.signature, "PK\3\4", 4);
797 	strncpy(central.signature, "PK\1\2", 4);
798 	PHAR_SET_16(central.extra_len, sizeof(perms));
799 	PHAR_SET_16(local.extra_len, sizeof(perms));
800 	perms.tag[0] = 'n';
801 	perms.tag[1] = 'u';
802 	PHAR_SET_16(perms.size, sizeof(perms) - 4);
803 	PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
804 	{
805 		php_uint32 crc = (php_uint32) ~0;
806 		CRC32(crc, perms.perms[0]);
807 		CRC32(crc, perms.perms[1]);
808 		PHAR_SET_32(perms.crc32, ~crc);
809 	}
810 
811 	if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
812 		PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_DEFLATE);
813 		PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_DEFLATE);
814 	}
815 
816 	if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
817 		PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_BZIP2);
818 		PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_BZIP2);
819 	}
820 
821 	/* do not use PHAR_GET_16 on either field of the next line */
822 	phar_zip_u2d_time(entry->timestamp, local.timestamp, local.datestamp);
823 	memcpy(central.timestamp, local.timestamp, sizeof(local.timestamp));
824 	memcpy(central.datestamp, local.datestamp, sizeof(local.datestamp));
825 	PHAR_SET_16(central.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
826 	PHAR_SET_16(local.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
827 	PHAR_SET_32(central.offset, php_stream_tell(p->filefp));
828 
829 	/* do extra field for perms later */
830 	if (entry->is_modified) {
831 		php_uint32 loc;
832 		php_stream_filter *filter;
833 		php_stream *efp;
834 
835 		if (entry->is_dir) {
836 			entry->is_modified = 0;
837 			if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
838 				php_stream_close(entry->fp);
839 				entry->fp = NULL;
840 				entry->fp_type = PHAR_FP;
841 			}
842 			goto continue_dir;
843 		}
844 
845 		if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) {
846 			spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
847 			return ZEND_HASH_APPLY_STOP;
848 		}
849 
850 		/* we can be modified and already be compressed, such as when chmod() is executed */
851 		if (entry->flags & PHAR_ENT_COMPRESSION_MASK && (entry->old_flags == entry->flags || !entry->old_flags)) {
852 			not_really_modified = 1;
853 			goto is_compressed;
854 		}
855 
856 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
857 			spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
858 			return ZEND_HASH_APPLY_STOP;
859 		}
860 
861 		efp = phar_get_efp(entry, 0 TSRMLS_CC);
862 		newcrc32 = ~0;
863 
864 		for (loc = 0;loc < entry->uncompressed_filesize; ++loc) {
865 			CRC32(newcrc32, php_stream_getc(efp));
866 		}
867 
868 		entry->crc32 = ~newcrc32;
869 		PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
870 		PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
871 
872 		if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
873 			/* not compressed */
874 			entry->compressed_filesize = entry->uncompressed_filesize;
875 			PHAR_SET_32(central.compsize, entry->uncompressed_filesize);
876 			PHAR_SET_32(local.compsize, entry->uncompressed_filesize);
877 			goto not_compressed;
878 		}
879 
880 		filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
881 
882 		if (!filter) {
883 			if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
884 				spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
885 			} else {
886 				spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
887 			}
888 			return ZEND_HASH_APPLY_STOP;
889 		}
890 
891 		/* create new file that holds the compressed version */
892 		/* work around inability to specify freedom in write and strictness
893 		in read count */
894 		entry->cfp = php_stream_fopen_tmpfile();
895 
896 		if (!entry->cfp) {
897 			spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
898 			return ZEND_HASH_APPLY_STOP;
899 		}
900 
901 		php_stream_flush(efp);
902 
903 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
904 			spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
905 			return ZEND_HASH_APPLY_STOP;
906 		}
907 
908 		php_stream_filter_append((&entry->cfp->writefilters), filter);
909 
910 		if (SUCCESS != phar_stream_copy_to_stream(efp, entry->cfp, entry->uncompressed_filesize, NULL)) {
911 			spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
912 			return ZEND_HASH_APPLY_STOP;
913 		}
914 
915 		php_stream_filter_flush(filter, 1);
916 		php_stream_flush(entry->cfp);
917 		php_stream_filter_remove(filter, 1 TSRMLS_CC);
918 		php_stream_seek(entry->cfp, 0, SEEK_END);
919 		entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
920 		PHAR_SET_32(central.compsize, entry->compressed_filesize);
921 		PHAR_SET_32(local.compsize, entry->compressed_filesize);
922 		/* generate crc on compressed file */
923 		php_stream_rewind(entry->cfp);
924 		entry->old_flags = entry->flags;
925 		entry->is_modified = 1;
926 	} else {
927 is_compressed:
928 		PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
929 		PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
930 		PHAR_SET_32(central.compsize, entry->compressed_filesize);
931 		PHAR_SET_32(local.compsize, entry->compressed_filesize);
932 		if (p->old) {
933 			if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
934 				spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
935 				return ZEND_HASH_APPLY_STOP;
936 			}
937 		}
938 	}
939 not_compressed:
940 	PHAR_SET_32(central.crc32, entry->crc32);
941 	PHAR_SET_32(local.crc32, entry->crc32);
942 continue_dir:
943 	/* set file metadata */
944 	if (entry->metadata) {
945 		php_serialize_data_t metadata_hash;
946 
947 		if (entry->metadata_str.c) {
948 			smart_str_free(&entry->metadata_str);
949 		}
950 		entry->metadata_str.c = 0;
951 		entry->metadata_str.len = 0;
952 		PHP_VAR_SERIALIZE_INIT(metadata_hash);
953 		php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
954 		PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
955 		PHAR_SET_16(central.comment_len, entry->metadata_str.len);
956 	}
957 
958 	entry->header_offset = php_stream_tell(p->filefp);
959 	offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms);
960 
961 	if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
962 		spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
963 		return ZEND_HASH_APPLY_STOP;
964 	}
965 
966 	if (sizeof(central) != php_stream_write(p->centralfp, (char *)&central, sizeof(central))) {
967 		spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
968 		return ZEND_HASH_APPLY_STOP;
969 	}
970 
971 	if (entry->is_dir) {
972 		if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
973 			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);
974 			return ZEND_HASH_APPLY_STOP;
975 		}
976 
977 		if (1 != php_stream_write(p->filefp, "/", 1)) {
978 			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);
979 			return ZEND_HASH_APPLY_STOP;
980 		}
981 
982 		if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
983 			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);
984 			return ZEND_HASH_APPLY_STOP;
985 		}
986 
987 		if (1 != php_stream_write(p->centralfp, "/", 1)) {
988 			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);
989 			return ZEND_HASH_APPLY_STOP;
990 		}
991 	} else {
992 		if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
993 			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);
994 			return ZEND_HASH_APPLY_STOP;
995 		}
996 
997 		if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
998 			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);
999 			return ZEND_HASH_APPLY_STOP;
1000 		}
1001 	}
1002 
1003 	if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
1004 		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);
1005 		return ZEND_HASH_APPLY_STOP;
1006 	}
1007 
1008 	if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
1009 		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);
1010 		return ZEND_HASH_APPLY_STOP;
1011 	}
1012 
1013 	if (!not_really_modified && entry->is_modified) {
1014 		if (entry->cfp) {
1015 			if (SUCCESS != phar_stream_copy_to_stream(entry->cfp, p->filefp, entry->compressed_filesize, NULL)) {
1016 				spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1017 				return ZEND_HASH_APPLY_STOP;
1018 			}
1019 
1020 			php_stream_close(entry->cfp);
1021 			entry->cfp = NULL;
1022 		} else {
1023 			if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) {
1024 				return ZEND_HASH_APPLY_STOP;
1025 			}
1026 
1027 			phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC);
1028 
1029 			if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), p->filefp, entry->uncompressed_filesize, NULL)) {
1030 				spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1031 				return ZEND_HASH_APPLY_STOP;
1032 			}
1033 		}
1034 
1035 		if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
1036 			php_stream_close(entry->fp);
1037 		}
1038 
1039 		entry->is_modified = 0;
1040 	} else {
1041 		entry->is_modified = 0;
1042 		if (entry->fp_refcount) {
1043 			/* open file pointers refer to this fp, do not free the stream */
1044 			switch (entry->fp_type) {
1045 				case PHAR_FP:
1046 					p->free_fp = 0;
1047 					break;
1048 				case PHAR_UFP:
1049 					p->free_ufp = 0;
1050 				default:
1051 					break;
1052 			}
1053 		}
1054 
1055 		if (!entry->is_dir && entry->compressed_filesize && SUCCESS != phar_stream_copy_to_stream(p->old, p->filefp, entry->compressed_filesize, NULL)) {
1056 			spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1057 			return ZEND_HASH_APPLY_STOP;
1058 		}
1059 	}
1060 
1061 	entry->fp = NULL;
1062 	entry->offset = entry->offset_abs = offset;
1063 	entry->fp_type = PHAR_FP;
1064 
1065 	if (entry->metadata_str.c) {
1066 		if (entry->metadata_str.len != php_stream_write(p->centralfp, entry->metadata_str.c, entry->metadata_str.len)) {
1067 			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);
1068 			smart_str_free(&entry->metadata_str);
1069 			return ZEND_HASH_APPLY_STOP;
1070 		}
1071 
1072 		smart_str_free(&entry->metadata_str);
1073 	}
1074 
1075 	return ZEND_HASH_APPLY_KEEP;
1076 }
1077 /* }}} */
1078 
phar_zip_applysignature(phar_archive_data * phar,struct _phar_zip_pass * pass,smart_str * metadata TSRMLS_DC)1079 static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass,
1080 				   smart_str *metadata TSRMLS_DC) /* {{{ */
1081 {
1082 	/* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1083 	if (!phar->is_data || phar->sig_flags) {
1084 		int signature_length;
1085 		char *signature, sigbuf[8];
1086 		phar_entry_info entry = {0};
1087 		php_stream *newfile;
1088 		off_t tell, st;
1089 
1090 		newfile = php_stream_fopen_tmpfile();
1091 		if (newfile == NULL) {
1092 			spprintf(pass->error, 0, "phar error: unable to create temporary file for the signature file");
1093 			return FAILURE;
1094 		}
1095 		st = tell = php_stream_tell(pass->filefp);
1096 		/* copy the local files, central directory, and the zip comment to generate the hash */
1097 		php_stream_seek(pass->filefp, 0, SEEK_SET);
1098 		phar_stream_copy_to_stream(pass->filefp, newfile, tell, NULL);
1099 		tell = php_stream_tell(pass->centralfp);
1100 		php_stream_seek(pass->centralfp, 0, SEEK_SET);
1101 		phar_stream_copy_to_stream(pass->centralfp, newfile, tell, NULL);
1102 		if (metadata->c) {
1103 			php_stream_write(newfile, metadata->c, metadata->len);
1104 		}
1105 
1106 		if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error TSRMLS_CC)) {
1107 			if (pass->error) {
1108 				char *save = *(pass->error);
1109 				spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
1110 				efree(save);
1111 			}
1112 
1113 			php_stream_close(newfile);
1114 			return FAILURE;
1115 		}
1116 
1117 		entry.filename = ".phar/signature.bin";
1118 		entry.filename_len = sizeof(".phar/signature.bin")-1;
1119 		entry.fp = php_stream_fopen_tmpfile();
1120 		entry.fp_type = PHAR_MOD;
1121 		entry.is_modified = 1;
1122 		if (entry.fp == NULL) {
1123 			spprintf(pass->error, 0, "phar error: unable to create temporary file for signature");
1124 			return FAILURE;
1125 		}
1126 
1127 		PHAR_SET_32(sigbuf, phar->sig_flags);
1128 		PHAR_SET_32(sigbuf + 4, signature_length);
1129 
1130 		if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) {
1131 			efree(signature);
1132 			if (pass->error) {
1133 				spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
1134 			}
1135 
1136 			php_stream_close(newfile);
1137 			return FAILURE;
1138 		}
1139 
1140 		efree(signature);
1141 		entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1142 		entry.phar = phar;
1143 		/* throw out return value and write the signature */
1144 		phar_zip_changed_apply((void *)&entry, (void *)pass TSRMLS_CC);
1145 		php_stream_close(newfile);
1146 
1147 		if (pass->error && *(pass->error)) {
1148 			/* error is set by writeheaders */
1149 			php_stream_close(newfile);
1150 			return FAILURE;
1151 		}
1152 	} /* signature */
1153 	return SUCCESS;
1154 }
1155 /* }}} */
1156 
phar_zip_flush(phar_archive_data * phar,char * user_stub,long len,int defaultstub,char ** error TSRMLS_DC)1157 int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */
1158 {
1159 	char *pos;
1160 	smart_str main_metadata_str = {0};
1161 	static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
1162 	char halt_stub[] = "__HALT_COMPILER();";
1163 	char *tmp;
1164 
1165 	php_stream *stubfile, *oldfile;
1166 	php_serialize_data_t metadata_hash;
1167 	int free_user_stub, closeoldfile = 0;
1168 	phar_entry_info entry = {0};
1169 	char *temperr = NULL;
1170 	struct _phar_zip_pass pass;
1171 	phar_zip_dir_end eocd;
1172 	php_uint32 cdir_size, cdir_offset;
1173 
1174 	pass.error = &temperr;
1175 	entry.flags = PHAR_ENT_PERM_DEF_FILE;
1176 	entry.timestamp = time(NULL);
1177 	entry.is_modified = 1;
1178 	entry.is_zip = 1;
1179 	entry.phar = phar;
1180 	entry.fp_type = PHAR_MOD;
1181 
1182 	if (phar->is_persistent) {
1183 		if (error) {
1184 			spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
1185 		}
1186 		return EOF;
1187 	}
1188 
1189 	if (phar->is_data) {
1190 		goto nostub;
1191 	}
1192 
1193 	/* set alias */
1194 	if (!phar->is_temporary_alias && phar->alias_len) {
1195 		entry.fp = php_stream_fopen_tmpfile();
1196 		if (entry.fp == NULL) {
1197 			spprintf(error, 0, "phar error: unable to create temporary file");
1198 			return EOF;
1199 		}
1200 		if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
1201 			if (error) {
1202 				spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1203 			}
1204 			return EOF;
1205 		}
1206 
1207 		entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
1208 		entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1209 		entry.filename_len = sizeof(".phar/alias.txt")-1;
1210 
1211 		if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1212 			if (error) {
1213 				spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1214 			}
1215 			return EOF;
1216 		}
1217 	} else {
1218 		zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1219 	}
1220 
1221 	/* register alias */
1222 	if (phar->alias_len) {
1223 		if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error TSRMLS_CC)) {
1224 			return EOF;
1225 		}
1226 	}
1227 
1228 	/* set stub */
1229 	if (user_stub && !defaultstub) {
1230 		if (len < 0) {
1231 			/* resource passed in */
1232 			if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
1233 				if (error) {
1234 					spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1235 				}
1236 				return EOF;
1237 			}
1238 
1239 			if (len == -1) {
1240 				len = PHP_STREAM_COPY_ALL;
1241 			} else {
1242 				len = -len;
1243 			}
1244 
1245 			user_stub = 0;
1246 
1247 			if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
1248 				if (error) {
1249 					spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1250 				}
1251 				return EOF;
1252 			}
1253 			free_user_stub = 1;
1254 		} else {
1255 			free_user_stub = 0;
1256 		}
1257 
1258 		tmp = estrndup(user_stub, len);
1259 		if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1260 			efree(tmp);
1261 			if (error) {
1262 				spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
1263 			}
1264 			if (free_user_stub) {
1265 				efree(user_stub);
1266 			}
1267 			return EOF;
1268 		}
1269 		pos = user_stub + (pos - tmp);
1270 		efree(tmp);
1271 
1272 		len = pos - user_stub + 18;
1273 		entry.fp = php_stream_fopen_tmpfile();
1274 		if (entry.fp == NULL) {
1275 			spprintf(error, 0, "phar error: unable to create temporary file");
1276 			return EOF;
1277 		}
1278 		entry.uncompressed_filesize = len + 5;
1279 
1280 		if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1281 		||            5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1282 			if (error) {
1283 				spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
1284 			}
1285 			if (free_user_stub) {
1286 				efree(user_stub);
1287 			}
1288 			php_stream_close(entry.fp);
1289 			return EOF;
1290 		}
1291 
1292 		entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1293 		entry.filename_len = sizeof(".phar/stub.php")-1;
1294 
1295 		if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1296 			if (free_user_stub) {
1297 				efree(user_stub);
1298 			}
1299 			if (error) {
1300 				spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", phar->fname);
1301 			}
1302 			return EOF;
1303 		}
1304 
1305 		if (free_user_stub) {
1306 			efree(user_stub);
1307 		}
1308 	} else {
1309 		/* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1310 		entry.fp = php_stream_fopen_tmpfile();
1311 		if (entry.fp == NULL) {
1312 			spprintf(error, 0, "phar error: unable to create temporary file");
1313 			return EOF;
1314 		}
1315 		if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1316 			php_stream_close(entry.fp);
1317 			if (error) {
1318 				spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1319 			}
1320 			return EOF;
1321 		}
1322 
1323 		entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1324 		entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1325 		entry.filename_len = sizeof(".phar/stub.php")-1;
1326 
1327 		if (!defaultstub) {
1328 			if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1329 				if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1330 					php_stream_close(entry.fp);
1331 					efree(entry.filename);
1332 					if (error) {
1333 						spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
1334 					}
1335 					return EOF;
1336 				}
1337 			} else {
1338 				php_stream_close(entry.fp);
1339 				efree(entry.filename);
1340 			}
1341 		} else {
1342 			if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1343 				php_stream_close(entry.fp);
1344 				efree(entry.filename);
1345 				if (error) {
1346 					spprintf(error, 0, "unable to overwrite stub in zip-based phar \"%s\"", phar->fname);
1347 				}
1348 				return EOF;
1349 			}
1350 		}
1351 	}
1352 nostub:
1353 	if (phar->fp && !phar->is_brandnew) {
1354 		oldfile = phar->fp;
1355 		closeoldfile = 0;
1356 		php_stream_rewind(oldfile);
1357 	} else {
1358 		oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1359 		closeoldfile = oldfile != NULL;
1360 	}
1361 
1362 	/* save modified files to the zip */
1363 	pass.old = oldfile;
1364 	pass.filefp = php_stream_fopen_tmpfile();
1365 
1366 	if (!pass.filefp) {
1367 fperror:
1368 		if (closeoldfile) {
1369 			php_stream_close(oldfile);
1370 		}
1371 		if (error) {
1372 			spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
1373 		}
1374 		return EOF;
1375 	}
1376 
1377 	pass.centralfp = php_stream_fopen_tmpfile();
1378 
1379 	if (!pass.centralfp) {
1380 		goto fperror;
1381 	}
1382 
1383 	pass.free_fp = pass.free_ufp = 1;
1384 	memset(&eocd, 0, sizeof(eocd));
1385 
1386 	strncpy(eocd.signature, "PK\5\6", 4);
1387 	if (!phar->is_data && !phar->sig_flags) {
1388 		phar->sig_flags = PHAR_SIG_SHA1;
1389 	}
1390 	if (phar->sig_flags) {
1391 		PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
1392 		PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
1393 	} else {
1394 		PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
1395 		PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
1396 	}
1397 	zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass TSRMLS_CC);
1398 
1399 	if (phar->metadata) {
1400 		/* set phar metadata */
1401 		PHP_VAR_SERIALIZE_INIT(metadata_hash);
1402 		php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
1403 		PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
1404 	}
1405 	if (temperr) {
1406 		if (error) {
1407 			spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
1408 		}
1409 		efree(temperr);
1410 temperror:
1411 		php_stream_close(pass.centralfp);
1412 nocentralerror:
1413 		if (phar->metadata) {
1414 			smart_str_free(&main_metadata_str);
1415 		}
1416 		php_stream_close(pass.filefp);
1417 		if (closeoldfile) {
1418 			php_stream_close(oldfile);
1419 		}
1420 		return EOF;
1421 	}
1422 
1423 	if (FAILURE == phar_zip_applysignature(phar, &pass, &main_metadata_str TSRMLS_CC)) {
1424 		goto temperror;
1425 	}
1426 
1427 	/* save zip */
1428 	cdir_size = php_stream_tell(pass.centralfp);
1429 	cdir_offset = php_stream_tell(pass.filefp);
1430 	PHAR_SET_32(eocd.cdir_size, cdir_size);
1431 	PHAR_SET_32(eocd.cdir_offset, cdir_offset);
1432 	php_stream_seek(pass.centralfp, 0, SEEK_SET);
1433 
1434 	{
1435 		size_t clen;
1436 		int ret = phar_stream_copy_to_stream(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
1437 		if (SUCCESS != ret || clen != cdir_size) {
1438 			if (error) {
1439 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
1440 			}
1441 			goto temperror;
1442 		}
1443 	}
1444 
1445 	php_stream_close(pass.centralfp);
1446 
1447 	if (phar->metadata) {
1448 		/* set phar metadata */
1449 		PHAR_SET_16(eocd.comment_len, main_metadata_str.len);
1450 
1451 		if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1452 			if (error) {
1453 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1454 			}
1455 			goto nocentralerror;
1456 		}
1457 
1458 		if (main_metadata_str.len != php_stream_write(pass.filefp, main_metadata_str.c, main_metadata_str.len)) {
1459 			if (error) {
1460 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
1461 			}
1462 			goto nocentralerror;
1463 		}
1464 
1465 		smart_str_free(&main_metadata_str);
1466 
1467 	} else {
1468 		if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1469 			if (error) {
1470 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1471 			}
1472 			goto nocentralerror;
1473 		}
1474 	}
1475 
1476 	if (phar->fp && pass.free_fp) {
1477 		php_stream_close(phar->fp);
1478 	}
1479 
1480 	if (phar->ufp) {
1481 		if (pass.free_ufp) {
1482 			php_stream_close(phar->ufp);
1483 		}
1484 		phar->ufp = NULL;
1485 	}
1486 
1487 	/* re-open */
1488 	phar->is_brandnew = 0;
1489 
1490 	if (phar->donotflush) {
1491 		/* deferred flush */
1492 		phar->fp = pass.filefp;
1493 	} else {
1494 		phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1495 		if (!phar->fp) {
1496 			if (closeoldfile) {
1497 				php_stream_close(oldfile);
1498 			}
1499 			phar->fp = pass.filefp;
1500 			if (error) {
1501 				spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
1502 			}
1503 			return EOF;
1504 		}
1505 		php_stream_rewind(pass.filefp);
1506 		phar_stream_copy_to_stream(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1507 		/* we could also reopen the file in "rb" mode but there is no need for that */
1508 		php_stream_close(pass.filefp);
1509 	}
1510 
1511 	if (closeoldfile) {
1512 		php_stream_close(oldfile);
1513 	}
1514 	return EOF;
1515 }
1516 /* }}} */
1517 
1518 /*
1519  * Local variables:
1520  * tab-width: 4
1521  * c-basic-offset: 4
1522  * End:
1523  * vim600: noet sw=4 ts=4 fdm=marker
1524  * vim<600: noet sw=4 ts=4
1525  */
1526