1 /*
2   +----------------------------------------------------------------------+
3   | phar php single-file executable PHP extension                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 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   |          Marcus Boerger <helly@php.net>                              |
17   +----------------------------------------------------------------------+
18 */
19 
20 #define PHAR_MAIN 1
21 #include "phar_internal.h"
22 #include "SAPI.h"
23 #include "func_interceptors.h"
24 
25 static void destroy_phar_data(zval *zv);
26 
27 ZEND_DECLARE_MODULE_GLOBALS(phar)
28 zend_string *(*phar_save_resolve_path)(const char *filename, size_t filename_len);
29 
30 /**
31  * set's phar->is_writeable based on the current INI value
32  */
phar_set_writeable_bit(zval * zv,void * argument)33 static int phar_set_writeable_bit(zval *zv, void *argument) /* {{{ */
34 {
35 	zend_bool keep = *(zend_bool *)argument;
36 	phar_archive_data *phar = (phar_archive_data *)Z_PTR_P(zv);
37 
38 	if (!phar->is_data) {
39 		phar->is_writeable = !keep;
40 	}
41 
42 	return ZEND_HASH_APPLY_KEEP;
43 }
44 /* }}} */
45 
46 /* if the original value is 0 (disabled), then allow setting/unsetting at will. Otherwise only allow 1 (enabled), and error on disabling */
ZEND_INI_MH(phar_ini_modify_handler)47 ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */
48 {
49 	zend_bool old, ini;
50 
51 	if (ZSTR_LEN(entry->name) == sizeof("phar.readonly")-1) {
52 		old = PHAR_G(readonly_orig);
53 	} else {
54 		old = PHAR_G(require_hash_orig);
55 	}
56 
57 	if (ZSTR_LEN(new_value) == 2 && !strcasecmp("on", ZSTR_VAL(new_value))) {
58 		ini = (zend_bool) 1;
59 	}
60 	else if (ZSTR_LEN(new_value) == 3 && !strcasecmp("yes", ZSTR_VAL(new_value))) {
61 		ini = (zend_bool) 1;
62 	}
63 	else if (ZSTR_LEN(new_value) == 4 && !strcasecmp("true", ZSTR_VAL(new_value))) {
64 		ini = (zend_bool) 1;
65 	}
66 	else {
67 		ini = (zend_bool) atoi(ZSTR_VAL(new_value));
68 	}
69 
70 	/* do not allow unsetting in runtime */
71 	if (stage == ZEND_INI_STAGE_STARTUP) {
72 		if (ZSTR_LEN(entry->name) == sizeof("phar.readonly")-1) {
73 			PHAR_G(readonly_orig) = ini;
74 		} else {
75 			PHAR_G(require_hash_orig) = ini;
76 		}
77 	} else if (old && !ini) {
78 		return FAILURE;
79 	}
80 
81 	if (ZSTR_LEN(entry->name) == sizeof("phar.readonly")-1) {
82 		PHAR_G(readonly) = ini;
83 		if (PHAR_G(request_init) && HT_IS_INITIALIZED(&PHAR_G(phar_fname_map))) {
84 			zend_hash_apply_with_argument(&(PHAR_G(phar_fname_map)), phar_set_writeable_bit, (void *)&ini);
85 		}
86 	} else {
87 		PHAR_G(require_hash) = ini;
88 	}
89 
90 	return SUCCESS;
91 }
92 /* }}}*/
93 
94 /* this global stores the global cached pre-parsed manifests */
95 HashTable cached_phars;
96 HashTable cached_alias;
97 
phar_split_cache_list(void)98 static void phar_split_cache_list(void) /* {{{ */
99 {
100 	char *tmp;
101 	char *key, *lasts, *end;
102 	char ds[2];
103 	phar_archive_data *phar;
104 	uint32_t i = 0;
105 
106 	if (!PHAR_G(cache_list) || !(PHAR_G(cache_list)[0])) {
107 		return;
108 	}
109 
110 	ds[0] = DEFAULT_DIR_SEPARATOR;
111 	ds[1] = '\0';
112 	tmp = estrdup(PHAR_G(cache_list));
113 
114 	/* fake request startup */
115 	PHAR_G(request_init) = 1;
116 	zend_init_rsrc_list();
117 	EG(regular_list).nNextFreeElement=1;	/* we don't want resource id 0 */
118 
119 	PHAR_G(has_bz2) = zend_hash_str_exists(&module_registry, "bz2", sizeof("bz2")-1);
120 	PHAR_G(has_zlib) = zend_hash_str_exists(&module_registry, "zlib", sizeof("zlib")-1);
121 	/* these two are dummies and will be destroyed later */
122 	zend_hash_init(&cached_phars, sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data,  1);
123 	zend_hash_init(&cached_alias, sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
124 	/* these two are real and will be copied over cached_phars/cached_alias later */
125 	zend_hash_init(&(PHAR_G(phar_fname_map)), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data,  1);
126 	zend_hash_init(&(PHAR_G(phar_alias_map)), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
127 	PHAR_G(manifest_cached) = 1;
128 	PHAR_G(persist) = 1;
129 
130 	for (key = php_strtok_r(tmp, ds, &lasts);
131 			key;
132 			key = php_strtok_r(NULL, ds, &lasts)) {
133 		size_t len;
134 		end = strchr(key, DEFAULT_DIR_SEPARATOR);
135 		if (end) {
136 			len = end - key;
137 		} else {
138 			len = strlen(key);
139 		}
140 
141 		if (SUCCESS == phar_open_from_filename(key, len, NULL, 0, 0, &phar, NULL)) {
142 			phar->phar_pos = i++;
143 			php_stream_close(phar->fp);
144 			phar->fp = NULL;
145 		} else {
146 			PHAR_G(persist) = 0;
147 			PHAR_G(manifest_cached) = 0;
148 			efree(tmp);
149 			zend_hash_destroy(&(PHAR_G(phar_fname_map)));
150 			HT_INVALIDATE(&PHAR_G(phar_fname_map));
151 			zend_hash_destroy(&(PHAR_G(phar_alias_map)));
152 			HT_INVALIDATE(&PHAR_G(phar_alias_map));
153 			zend_hash_destroy(&cached_phars);
154 			zend_hash_destroy(&cached_alias);
155 			zend_hash_graceful_reverse_destroy(&EG(regular_list));
156 			memset(&EG(regular_list), 0, sizeof(HashTable));
157 			/* free cached manifests */
158 			PHAR_G(request_init) = 0;
159 			return;
160 		}
161 	}
162 
163 	PHAR_G(persist) = 0;
164 	PHAR_G(request_init) = 0;
165 	/* destroy dummy values from before */
166 	zend_hash_destroy(&cached_phars);
167 	zend_hash_destroy(&cached_alias);
168 	cached_phars = PHAR_G(phar_fname_map);
169 	cached_alias = PHAR_G(phar_alias_map);
170 	HT_INVALIDATE(&PHAR_G(phar_fname_map));
171 	HT_INVALIDATE(&PHAR_G(phar_alias_map));
172 	zend_hash_graceful_reverse_destroy(&EG(regular_list));
173 	memset(&EG(regular_list), 0, sizeof(HashTable));
174 	efree(tmp);
175 }
176 /* }}} */
177 
ZEND_INI_MH(phar_ini_cache_list)178 ZEND_INI_MH(phar_ini_cache_list) /* {{{ */
179 {
180 	PHAR_G(cache_list) = ZSTR_VAL(new_value);
181 
182 	if (stage == ZEND_INI_STAGE_STARTUP) {
183 		phar_split_cache_list();
184 	}
185 
186 	return SUCCESS;
187 }
188 /* }}} */
189 
190 PHP_INI_BEGIN()
191 	STD_PHP_INI_BOOLEAN("phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals)
192 	STD_PHP_INI_BOOLEAN("phar.require_hash", "1", PHP_INI_ALL, phar_ini_modify_handler, require_hash, zend_phar_globals, phar_globals)
193 	STD_PHP_INI_ENTRY("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals)
PHP_INI_END()194 PHP_INI_END()
195 
196 /**
197  * When all uses of a phar have been concluded, this frees the manifest
198  * and the phar slot
199  */
200 void phar_destroy_phar_data(phar_archive_data *phar) /* {{{ */
201 {
202 	if (phar->alias && phar->alias != phar->fname) {
203 		pefree(phar->alias, phar->is_persistent);
204 		phar->alias = NULL;
205 	}
206 
207 	if (phar->fname) {
208 		pefree(phar->fname, phar->is_persistent);
209 		phar->fname = NULL;
210 	}
211 
212 	if (phar->signature) {
213 		pefree(phar->signature, phar->is_persistent);
214 		phar->signature = NULL;
215 	}
216 
217 	if (HT_IS_INITIALIZED(&phar->manifest)) {
218 		zend_hash_destroy(&phar->manifest);
219 		HT_INVALIDATE(&phar->manifest);
220 	}
221 
222 	if (HT_IS_INITIALIZED(&phar->mounted_dirs)) {
223 		zend_hash_destroy(&phar->mounted_dirs);
224 		HT_INVALIDATE(&phar->mounted_dirs);
225 	}
226 
227 	if (HT_IS_INITIALIZED(&phar->virtual_dirs)) {
228 		zend_hash_destroy(&phar->virtual_dirs);
229 		HT_INVALIDATE(&phar->virtual_dirs);
230 	}
231 
232 	if (Z_TYPE(phar->metadata) != IS_UNDEF) {
233 		if (phar->is_persistent) {
234 			if (phar->metadata_len) {
235 				/* for zip comments that are strings */
236 				free(Z_PTR(phar->metadata));
237 			} else {
238 				zval_internal_ptr_dtor(&phar->metadata);
239 			}
240 		} else {
241 			zval_ptr_dtor(&phar->metadata);
242 		}
243 		phar->metadata_len = 0;
244 		ZVAL_UNDEF(&phar->metadata);
245 	}
246 
247 	if (phar->fp) {
248 		php_stream_close(phar->fp);
249 		phar->fp = 0;
250 	}
251 
252 	if (phar->ufp) {
253 		php_stream_close(phar->ufp);
254 		phar->ufp = 0;
255 	}
256 
257 	pefree(phar, phar->is_persistent);
258 }
259 /* }}}*/
260 
261 /**
262  * Delete refcount and destruct if needed. On destruct return 1 else 0.
263  */
phar_archive_delref(phar_archive_data * phar)264 int phar_archive_delref(phar_archive_data *phar) /* {{{ */
265 {
266 	if (phar->is_persistent) {
267 		return 0;
268 	}
269 
270 	if (--phar->refcount < 0) {
271 		if (PHAR_G(request_done)
272 		|| zend_hash_str_del(&(PHAR_G(phar_fname_map)), phar->fname, phar->fname_len) != SUCCESS) {
273 			phar_destroy_phar_data(phar);
274 		}
275 		return 1;
276 	} else if (!phar->refcount) {
277 		/* invalidate phar cache */
278 		PHAR_G(last_phar) = NULL;
279 		PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
280 
281 		if (phar->fp && (!(phar->flags & PHAR_FILE_COMPRESSION_MASK) || !phar->alias)) {
282 			/* close open file handle - allows removal or rename of
283 			the file on windows, which has greedy locking
284 			only close if the archive was not already compressed.  If it
285 			was compressed, then the fp does not refer to the original file.
286 			We're also closing compressed files to save resources,
287 			but only if the archive isn't aliased. */
288 			php_stream_close(phar->fp);
289 			phar->fp = NULL;
290 		}
291 
292 		if (!zend_hash_num_elements(&phar->manifest)) {
293 			/* this is a new phar that has perhaps had an alias/metadata set, but has never
294 			been flushed */
295 			if (zend_hash_str_del(&(PHAR_G(phar_fname_map)), phar->fname, phar->fname_len) != SUCCESS) {
296 				phar_destroy_phar_data(phar);
297 			}
298 			return 1;
299 		}
300 	}
301 	return 0;
302 }
303 /* }}}*/
304 
305 /**
306  * Destroy phar's in shutdown, here we don't care about aliases
307  */
destroy_phar_data_only(zval * zv)308 static void destroy_phar_data_only(zval *zv) /* {{{ */
309 {
310 	phar_archive_data *phar_data = (phar_archive_data *) Z_PTR_P(zv);
311 
312 	if (EG(exception) || --phar_data->refcount < 0) {
313 		phar_destroy_phar_data(phar_data);
314 	}
315 }
316 /* }}}*/
317 
318 /**
319  * Delete aliases to phar's that got kicked out of the global table
320  */
phar_unalias_apply(zval * zv,void * argument)321 static int phar_unalias_apply(zval *zv, void *argument) /* {{{ */
322 {
323 	return Z_PTR_P(zv) == argument ? ZEND_HASH_APPLY_REMOVE : ZEND_HASH_APPLY_KEEP;
324 }
325 /* }}} */
326 
327 /**
328  * Delete aliases to phar's that got kicked out of the global table
329  */
phar_tmpclose_apply(zval * zv)330 static int phar_tmpclose_apply(zval *zv) /* {{{ */
331 {
332 	phar_entry_info *entry = (phar_entry_info *) Z_PTR_P(zv);
333 
334 	if (entry->fp_type != PHAR_TMP) {
335 		return ZEND_HASH_APPLY_KEEP;
336 	}
337 
338 	if (entry->fp && !entry->fp_refcount) {
339 		php_stream_close(entry->fp);
340 		entry->fp = NULL;
341 	}
342 
343 	return ZEND_HASH_APPLY_KEEP;
344 }
345 /* }}} */
346 
347 /**
348  * Filename map destructor
349  */
destroy_phar_data(zval * zv)350 static void destroy_phar_data(zval *zv) /* {{{ */
351 {
352 	phar_archive_data *phar_data = (phar_archive_data *)Z_PTR_P(zv);
353 
354 	if (PHAR_G(request_ends)) {
355 		/* first, iterate over the manifest and close all PHAR_TMP entry fp handles,
356 		this prevents unnecessary unfreed stream resources */
357 		zend_hash_apply(&(phar_data->manifest), phar_tmpclose_apply);
358 		destroy_phar_data_only(zv);
359 		return;
360 	}
361 
362 	zend_hash_apply_with_argument(&(PHAR_G(phar_alias_map)), phar_unalias_apply, phar_data);
363 
364 	if (--phar_data->refcount < 0) {
365 		phar_destroy_phar_data(phar_data);
366 	}
367 }
368 /* }}}*/
369 
370 /**
371  * destructor for the manifest hash, frees each file's entry
372  */
destroy_phar_manifest_entry_int(phar_entry_info * entry)373 void destroy_phar_manifest_entry_int(phar_entry_info *entry) /* {{{ */
374 {
375 
376 	if (entry->cfp) {
377 		php_stream_close(entry->cfp);
378 		entry->cfp = 0;
379 	}
380 
381 	if (entry->fp) {
382 		php_stream_close(entry->fp);
383 		entry->fp = 0;
384 	}
385 
386 	if (Z_TYPE(entry->metadata) != IS_UNDEF) {
387 		if (entry->is_persistent) {
388 			if (entry->metadata_len) {
389 				/* for zip comments that are strings */
390 				free(Z_PTR(entry->metadata));
391 			} else {
392 				zval_internal_ptr_dtor(&entry->metadata);
393 			}
394 		} else {
395 			zval_ptr_dtor(&entry->metadata);
396 		}
397 		entry->metadata_len = 0;
398 		ZVAL_UNDEF(&entry->metadata);
399 	}
400 
401 	if (entry->metadata_str.s) {
402 		smart_str_free(&entry->metadata_str);
403 		entry->metadata_str.s = NULL;
404 	}
405 
406 	pefree(entry->filename, entry->is_persistent);
407 
408 	if (entry->link) {
409 		pefree(entry->link, entry->is_persistent);
410 		entry->link = 0;
411 	}
412 
413 	if (entry->tmp) {
414 		pefree(entry->tmp, entry->is_persistent);
415 		entry->tmp = 0;
416 	}
417 }
418 /* }}} */
419 
destroy_phar_manifest_entry(zval * zv)420 void destroy_phar_manifest_entry(zval *zv) /* {{{ */
421 {
422 	phar_entry_info *entry = Z_PTR_P(zv);
423 	destroy_phar_manifest_entry_int(entry);
424 	pefree(entry, entry->is_persistent);
425 }
426 /* }}} */
427 
phar_entry_delref(phar_entry_data * idata)428 int phar_entry_delref(phar_entry_data *idata) /* {{{ */
429 {
430 	int ret = 0;
431 
432 	if (idata->internal_file && !idata->internal_file->is_persistent) {
433 		if (--idata->internal_file->fp_refcount < 0) {
434 			idata->internal_file->fp_refcount = 0;
435 		}
436 
437 		if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
438 			php_stream_close(idata->fp);
439 		}
440 		/* if phar_get_or_create_entry_data returns a sub-directory, we have to free it */
441 		if (idata->internal_file->is_temp_dir) {
442 			destroy_phar_manifest_entry_int(idata->internal_file);
443 			efree(idata->internal_file);
444 		}
445 	}
446 
447 	phar_archive_delref(idata->phar);
448 	efree(idata);
449 	return ret;
450 }
451 /* }}} */
452 
453 /**
454  * Removes an entry, either by actually removing it or by marking it.
455  */
phar_entry_remove(phar_entry_data * idata,char ** error)456 void phar_entry_remove(phar_entry_data *idata, char **error) /* {{{ */
457 {
458 	phar_archive_data *phar;
459 
460 	phar = idata->phar;
461 
462 	if (idata->internal_file->fp_refcount < 2) {
463 		if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
464 			php_stream_close(idata->fp);
465 		}
466 		zend_hash_str_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
467 		idata->phar->refcount--;
468 		efree(idata);
469 	} else {
470 		idata->internal_file->is_deleted = 1;
471 		phar_entry_delref(idata);
472 	}
473 
474 	if (!phar->donotflush) {
475 		phar_flush(phar, 0, 0, 0, error);
476 	}
477 }
478 /* }}} */
479 
480 #define MAPPHAR_ALLOC_FAIL(msg) \
481 	if (fp) {\
482 		php_stream_close(fp);\
483 	}\
484 	if (error) {\
485 		spprintf(error, 0, msg, fname);\
486 	}\
487 	return FAILURE;
488 
489 #define MAPPHAR_FAIL(msg) \
490 	efree(savebuf);\
491 	if (mydata) {\
492 		phar_destroy_phar_data(mydata);\
493 	}\
494 	if (signature) {\
495 		pefree(signature, PHAR_G(persist));\
496 	}\
497 	MAPPHAR_ALLOC_FAIL(msg)
498 
499 #ifdef WORDS_BIGENDIAN
500 # define PHAR_GET_32(buffer, var) \
501 	var = ((((unsigned char*)(buffer))[3]) << 24) \
502 		| ((((unsigned char*)(buffer))[2]) << 16) \
503 		| ((((unsigned char*)(buffer))[1]) <<  8) \
504 		| (((unsigned char*)(buffer))[0]); \
505 	(buffer) += 4
506 # define PHAR_GET_16(buffer, var) \
507 	var = ((((unsigned char*)(buffer))[1]) <<  8) \
508 		| (((unsigned char*)(buffer))[0]); \
509 	(buffer) += 2
510 #else
511 # define PHAR_GET_32(buffer, var) \
512 	memcpy(&var, buffer, sizeof(var)); \
513 	buffer += 4
514 # define PHAR_GET_16(buffer, var) \
515 	var = *(uint16_t*)(buffer); \
516 	buffer += 2
517 #endif
518 #define PHAR_ZIP_16(var) ((uint16_t)((((uint16_t)var[0]) & 0xff) | \
519 	(((uint16_t)var[1]) & 0xff) << 8))
520 #define PHAR_ZIP_32(var) ((uint32_t)((((uint32_t)var[0]) & 0xff) | \
521 	(((uint32_t)var[1]) & 0xff) << 8 | \
522 	(((uint32_t)var[2]) & 0xff) << 16 | \
523 	(((uint32_t)var[3]) & 0xff) << 24))
524 
525 /**
526  * Open an already loaded phar
527  */
phar_open_parsed_phar(char * fname,size_t fname_len,char * alias,size_t alias_len,zend_bool is_data,uint32_t options,phar_archive_data ** pphar,char ** error)528 int phar_open_parsed_phar(char *fname, size_t fname_len, char *alias, size_t alias_len, zend_bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
529 {
530 	phar_archive_data *phar;
531 #ifdef PHP_WIN32
532 	char *save_fname;
533 	ALLOCA_FLAG(fname_use_heap)
534 #endif
535 
536 	if (error) {
537 		*error = NULL;
538 	}
539 #ifdef PHP_WIN32
540 	save_fname = fname;
541 	if (memchr(fname, '\\', fname_len)) {
542 		fname = do_alloca(fname_len + 1, fname_use_heap);
543 		memcpy(fname, save_fname, fname_len);
544 		fname[fname_len] = '\0';
545 		phar_unixify_path_separators(fname, fname_len);
546 	}
547 #endif
548 	if (SUCCESS == phar_get_archive(&phar, fname, fname_len, alias, alias_len, error)
549 		&& ((alias && fname_len == phar->fname_len
550 		&& !strncmp(fname, phar->fname, fname_len)) || !alias)
551 	) {
552 		phar_entry_info *stub;
553 #ifdef PHP_WIN32
554 		if (fname != save_fname) {
555 			free_alloca(fname, fname_use_heap);
556 			fname = save_fname;
557 		}
558 #endif
559 		/* logic above is as follows:
560 		   If an explicit alias was requested, ensure the filename passed in
561 		   matches the phar's filename.
562 		   If no alias was passed in, then it can match either and be valid
563 		 */
564 
565 		if (!is_data) {
566 			/* prevent any ".phar" without a stub getting through */
567 			if (!phar->halt_offset && !phar->is_brandnew && (phar->is_tar || phar->is_zip)) {
568 				if (PHAR_G(readonly) && NULL == (stub = zend_hash_str_find_ptr(&(phar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1))) {
569 					if (error) {
570 						spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
571 					}
572 					return FAILURE;
573 				}
574 			}
575 		}
576 
577 		if (pphar) {
578 			*pphar = phar;
579 		}
580 
581 		return SUCCESS;
582 	} else {
583 #ifdef PHP_WIN32
584 		if (fname != save_fname) {
585 			free_alloca(fname, fname_use_heap);
586 			fname = save_fname;
587 		}
588 #endif
589 		if (pphar) {
590 			*pphar = NULL;
591 		}
592 
593 		if (phar && error && !(options & REPORT_ERRORS)) {
594 			efree(error);
595 		}
596 
597 		return FAILURE;
598 	}
599 }
600 /* }}}*/
601 
602 /**
603  * Parse out metadata from the manifest for a single file
604  *
605  * Meta-data is in this format:
606  * [len32][data...]
607  *
608  * data is the serialized zval
609  */
phar_parse_metadata(char ** buffer,zval * metadata,uint32_t zip_metadata_len)610 int phar_parse_metadata(char **buffer, zval *metadata, uint32_t zip_metadata_len) /* {{{ */
611 {
612 	php_unserialize_data_t var_hash;
613 
614 	if (zip_metadata_len) {
615 		const unsigned char *p;
616 		unsigned char *p_buff = (unsigned char *)estrndup(*buffer, zip_metadata_len);
617 		p = p_buff;
618 		ZVAL_NULL(metadata);
619 		PHP_VAR_UNSERIALIZE_INIT(var_hash);
620 
621 		if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash)) {
622 			efree(p_buff);
623 			PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
624 			zval_ptr_dtor(metadata);
625 			ZVAL_UNDEF(metadata);
626 			return FAILURE;
627 		}
628 		efree(p_buff);
629 		PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
630 
631 		if (PHAR_G(persist)) {
632 			/* lazy init metadata */
633 			zval_ptr_dtor(metadata);
634 			Z_PTR_P(metadata) = pemalloc(zip_metadata_len, 1);
635 			memcpy(Z_PTR_P(metadata), *buffer, zip_metadata_len);
636 			return SUCCESS;
637 		}
638 	} else {
639 		ZVAL_UNDEF(metadata);
640 	}
641 
642 	return SUCCESS;
643 }
644 /* }}}*/
645 
646 /**
647  * Size of fixed fields in the manifest.
648  * See: http://php.net/manual/en/phar.fileformat.phar.php
649  */
650 #define MANIFEST_FIXED_LEN	18
651 
652 #define SAFE_PHAR_GET_32(buffer, endbuffer, var) \
653 	if (UNEXPECTED(buffer + 4 > endbuffer)) { \
654 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)"); \
655 	} \
656 	PHAR_GET_32(buffer, var);
657 
658 /**
659  * Does not check for a previously opened phar in the cache.
660  *
661  * Parse a new one and add it to the cache, returning either SUCCESS or
662  * FAILURE, and setting pphar to the pointer to the manifest entry
663  *
664  * This is used by phar_open_from_filename to process the manifest, but can be called
665  * directly.
666  */
phar_parse_pharfile(php_stream * fp,char * fname,size_t fname_len,char * alias,size_t alias_len,zend_long halt_offset,phar_archive_data ** pphar,uint32_t compression,char ** error)667 static int phar_parse_pharfile(php_stream *fp, char *fname, size_t fname_len, char *alias, size_t alias_len, zend_long halt_offset, phar_archive_data** pphar, uint32_t compression, char **error) /* {{{ */
668 {
669 	char b32[4], *buffer, *endbuffer, *savebuf;
670 	phar_archive_data *mydata = NULL;
671 	phar_entry_info entry;
672 	uint32_t manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
673 	uint16_t manifest_ver;
674 	uint32_t len;
675 	zend_long offset;
676 	size_t sig_len;
677 	int register_alias = 0, temp_alias = 0;
678 	char *signature = NULL;
679 	zend_string *str;
680 
681 	if (pphar) {
682 		*pphar = NULL;
683 	}
684 
685 	if (error) {
686 		*error = NULL;
687 	}
688 
689 	/* check for ?>\n and increment accordingly */
690 	if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
691 		MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
692 	}
693 
694 	buffer = b32;
695 
696 	if (3 != php_stream_read(fp, buffer, 3)) {
697 		MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
698 	}
699 
700 	if ((*buffer == ' ' || *buffer == '\n') && *(buffer + 1) == '?' && *(buffer + 2) == '>') {
701 		int nextchar;
702 		halt_offset += 3;
703 		if (EOF == (nextchar = php_stream_getc(fp))) {
704 			MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
705 		}
706 
707 		if ((char) nextchar == '\r') {
708 			/* if we have an \r we require an \n as well */
709 			if (EOF == (nextchar = php_stream_getc(fp)) || (char)nextchar != '\n') {
710 				MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
711 			}
712 			++halt_offset;
713 		}
714 
715 		if ((char) nextchar == '\n') {
716 			++halt_offset;
717 		}
718 	}
719 
720 	/* make sure we are at the right location to read the manifest */
721 	if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
722 		MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
723 	}
724 
725 	/* read in manifest */
726 	buffer = b32;
727 
728 	if (4 != php_stream_read(fp, buffer, 4)) {
729 		MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at manifest length)")
730 	}
731 
732 	PHAR_GET_32(buffer, manifest_len);
733 
734 	if (manifest_len > 1048576 * 100) {
735 		/* prevent serious memory issues by limiting manifest to at most 100 MB in length */
736 		MAPPHAR_ALLOC_FAIL("manifest cannot be larger than 100 MB in phar \"%s\"")
737 	}
738 
739 	buffer = (char *)emalloc(manifest_len);
740 	savebuf = buffer;
741 	endbuffer = buffer + manifest_len;
742 
743 	if (manifest_len < MANIFEST_FIXED_LEN || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
744 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
745 	}
746 
747 	/* extract the number of entries */
748 	SAFE_PHAR_GET_32(buffer, endbuffer, manifest_count);
749 
750 	if (manifest_count == 0) {
751 		MAPPHAR_FAIL("in phar \"%s\", manifest claims to have zero entries.  Phars must have at least 1 entry");
752 	}
753 
754 	/* extract API version, lowest nibble currently unused */
755 	manifest_ver = (((unsigned char)buffer[0]) << 8)
756 				 + ((unsigned char)buffer[1]);
757 	buffer += 2;
758 
759 	if ((manifest_ver & PHAR_API_VER_MASK) < PHAR_API_MIN_READ) {
760 		efree(savebuf);
761 		php_stream_close(fp);
762 		if (error) {
763 			spprintf(error, 0, "phar \"%s\" is API version %1.u.%1.u.%1.u, and cannot be processed", fname, manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0x0F);
764 		}
765 		return FAILURE;
766 	}
767 
768 	SAFE_PHAR_GET_32(buffer, endbuffer, manifest_flags);
769 
770 	manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK;
771 	manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK;
772 	/* remember whether this entire phar was compressed with gz/bzip2 */
773 	manifest_flags |= compression;
774 
775 	/* The lowest nibble contains the phar wide flags. The compression flags can */
776 	/* be ignored on reading because it is being generated anyways. */
777 	if (manifest_flags & PHAR_HDR_SIGNATURE) {
778 		char sig_buf[8], *sig_ptr = sig_buf;
779 		zend_off_t read_len;
780 		size_t end_of_phar;
781 
782 		if (-1 == php_stream_seek(fp, -8, SEEK_END)
783 		|| (read_len = php_stream_tell(fp)) < 20
784 		|| 8 != php_stream_read(fp, sig_buf, 8)
785 		|| memcmp(sig_buf+4, "GBMB", 4)) {
786 			efree(savebuf);
787 			php_stream_close(fp);
788 			if (error) {
789 				spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
790 			}
791 			return FAILURE;
792 		}
793 
794 		PHAR_GET_32(sig_ptr, sig_flags);
795 
796 		switch(sig_flags) {
797 			case PHAR_SIG_OPENSSL: {
798 				uint32_t signature_len;
799 				char *sig;
800 				zend_off_t whence;
801 
802 				/* we store the signature followed by the signature length */
803 				if (-1 == php_stream_seek(fp, -12, SEEK_CUR)
804 				|| 4 != php_stream_read(fp, sig_buf, 4)) {
805 					efree(savebuf);
806 					php_stream_close(fp);
807 					if (error) {
808 						spprintf(error, 0, "phar \"%s\" openssl signature length could not be read", fname);
809 					}
810 					return FAILURE;
811 				}
812 
813 				sig_ptr = sig_buf;
814 				PHAR_GET_32(sig_ptr, signature_len);
815 				sig = (char *) emalloc(signature_len);
816 				whence = signature_len + 4;
817 				whence = -whence;
818 
819 				if (-1 == php_stream_seek(fp, whence, SEEK_CUR)
820 				|| !(end_of_phar = php_stream_tell(fp))
821 				|| signature_len != php_stream_read(fp, sig, signature_len)) {
822 					efree(savebuf);
823 					efree(sig);
824 					php_stream_close(fp);
825 					if (error) {
826 						spprintf(error, 0, "phar \"%s\" openssl signature could not be read", fname);
827 					}
828 					return FAILURE;
829 				}
830 
831 				if (FAILURE == phar_verify_signature(fp, end_of_phar, PHAR_SIG_OPENSSL, sig, signature_len, fname, &signature, &sig_len, error)) {
832 					efree(savebuf);
833 					efree(sig);
834 					php_stream_close(fp);
835 					if (error) {
836 						char *save = *error;
837 						spprintf(error, 0, "phar \"%s\" openssl signature could not be verified: %s", fname, *error);
838 						efree(save);
839 					}
840 					return FAILURE;
841 				}
842 				efree(sig);
843 			}
844 			break;
845 			case PHAR_SIG_SHA512: {
846 				unsigned char digest[64];
847 
848 				php_stream_seek(fp, -(8 + 64), SEEK_END);
849 				read_len = php_stream_tell(fp);
850 
851 				if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
852 					efree(savebuf);
853 					php_stream_close(fp);
854 					if (error) {
855 						spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
856 					}
857 					return FAILURE;
858 				}
859 
860 				if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA512, (char *)digest, 64, fname, &signature, &sig_len, error)) {
861 					efree(savebuf);
862 					php_stream_close(fp);
863 					if (error) {
864 						char *save = *error;
865 						spprintf(error, 0, "phar \"%s\" SHA512 signature could not be verified: %s", fname, *error);
866 						efree(save);
867 					}
868 					return FAILURE;
869 				}
870 				break;
871 			}
872 			case PHAR_SIG_SHA256: {
873 				unsigned char digest[32];
874 
875 				php_stream_seek(fp, -(8 + 32), SEEK_END);
876 				read_len = php_stream_tell(fp);
877 
878 				if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
879 					efree(savebuf);
880 					php_stream_close(fp);
881 					if (error) {
882 						spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
883 					}
884 					return FAILURE;
885 				}
886 
887 				if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error)) {
888 					efree(savebuf);
889 					php_stream_close(fp);
890 					if (error) {
891 						char *save = *error;
892 						spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error);
893 						efree(save);
894 					}
895 					return FAILURE;
896 				}
897 				break;
898 			}
899 			case PHAR_SIG_SHA1: {
900 				unsigned char digest[20];
901 
902 				php_stream_seek(fp, -(8 + 20), SEEK_END);
903 				read_len = php_stream_tell(fp);
904 
905 				if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
906 					efree(savebuf);
907 					php_stream_close(fp);
908 					if (error) {
909 						spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
910 					}
911 					return FAILURE;
912 				}
913 
914 				if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error)) {
915 					efree(savebuf);
916 					php_stream_close(fp);
917 					if (error) {
918 						char *save = *error;
919 						spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error);
920 						efree(save);
921 					}
922 					return FAILURE;
923 				}
924 				break;
925 			}
926 			case PHAR_SIG_MD5: {
927 				unsigned char digest[16];
928 
929 				php_stream_seek(fp, -(8 + 16), SEEK_END);
930 				read_len = php_stream_tell(fp);
931 
932 				if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
933 					efree(savebuf);
934 					php_stream_close(fp);
935 					if (error) {
936 						spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
937 					}
938 					return FAILURE;
939 				}
940 
941 				if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error)) {
942 					efree(savebuf);
943 					php_stream_close(fp);
944 					if (error) {
945 						char *save = *error;
946 						spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error);
947 						efree(save);
948 					}
949 					return FAILURE;
950 				}
951 				break;
952 			}
953 			default:
954 				efree(savebuf);
955 				php_stream_close(fp);
956 
957 				if (error) {
958 					spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname);
959 				}
960 				return FAILURE;
961 		}
962 	} else if (PHAR_G(require_hash)) {
963 		efree(savebuf);
964 		php_stream_close(fp);
965 
966 		if (error) {
967 			spprintf(error, 0, "phar \"%s\" does not have a signature", fname);
968 		}
969 		return FAILURE;
970 	} else {
971 		sig_flags = 0;
972 		sig_len = 0;
973 	}
974 
975 	/* extract alias */
976 	SAFE_PHAR_GET_32(buffer, endbuffer, tmp_len);
977 
978 	if (buffer + tmp_len > endbuffer) {
979 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)");
980 	}
981 
982 	if (manifest_len < MANIFEST_FIXED_LEN + tmp_len) {
983 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
984 	}
985 
986 	/* tmp_len = 0 says alias length is 0, which means the alias is not stored in the phar */
987 	if (tmp_len) {
988 		/* if the alias is stored we enforce it (implicit overrides explicit) */
989 		if (alias && alias_len && (alias_len != tmp_len || strncmp(alias, buffer, tmp_len)))
990 		{
991 			php_stream_close(fp);
992 
993 			if (signature) {
994 				efree(signature);
995 			}
996 
997 			if (error) {
998 				spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%.*s\" under different alias \"%s\"", fname, tmp_len, buffer, alias);
999 			}
1000 
1001 			efree(savebuf);
1002 			return FAILURE;
1003 		}
1004 
1005 		alias_len = tmp_len;
1006 		alias = buffer;
1007 		buffer += tmp_len;
1008 		register_alias = 1;
1009 	} else if (!alias_len || !alias) {
1010 		/* if we neither have an explicit nor an implicit alias, we use the filename */
1011 		alias = NULL;
1012 		alias_len = 0;
1013 		register_alias = 0;
1014 	} else if (alias_len) {
1015 		register_alias = 1;
1016 		temp_alias = 1;
1017 	}
1018 
1019 	/* we have 5 32-bit items plus 1 byte at least */
1020 	if (manifest_count > ((manifest_len - MANIFEST_FIXED_LEN - tmp_len) / (5 * 4 + 1))) {
1021 		/* prevent serious memory issues */
1022 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
1023 	}
1024 
1025 	mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
1026 	mydata->is_persistent = PHAR_G(persist);
1027 
1028 	/* check whether we have meta data, zero check works regardless of byte order */
1029 	SAFE_PHAR_GET_32(buffer, endbuffer, len);
1030 	if (mydata->is_persistent) {
1031 		mydata->metadata_len = len;
1032 		if (!len) {
1033 			/* FIXME: not sure why this is needed but removing it breaks tests */
1034 			SAFE_PHAR_GET_32(buffer, endbuffer, len);
1035 		}
1036 	}
1037 	if(len > (size_t)(endbuffer - buffer)) {
1038 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (trying to read past buffer end)");
1039 	}
1040 	if (phar_parse_metadata(&buffer, &mydata->metadata, len) == FAILURE) {
1041 		MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
1042 	}
1043 	buffer += len;
1044 
1045 	/* set up our manifest */
1046 	zend_hash_init(&mydata->manifest, manifest_count,
1047 		zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
1048 	zend_hash_init(&mydata->mounted_dirs, 5,
1049 		zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1050 	zend_hash_init(&mydata->virtual_dirs, manifest_count * 2,
1051 		zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1052 	mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
1053 #ifdef PHP_WIN32
1054 	phar_unixify_path_separators(mydata->fname, fname_len);
1055 #endif
1056 	mydata->fname_len = fname_len;
1057 	offset = halt_offset + manifest_len + 4;
1058 	memset(&entry, 0, sizeof(phar_entry_info));
1059 	entry.phar = mydata;
1060 	entry.fp_type = PHAR_FP;
1061 	entry.is_persistent = mydata->is_persistent;
1062 
1063 	for (manifest_index = 0; manifest_index < manifest_count; ++manifest_index) {
1064 		if (buffer + 28 > endbuffer) {
1065 			MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)")
1066 		}
1067 
1068 		PHAR_GET_32(buffer, entry.filename_len);
1069 
1070 		if (entry.filename_len == 0) {
1071 			MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\"");
1072 		}
1073 
1074 		if (entry.is_persistent) {
1075 			entry.manifest_pos = manifest_index;
1076 		}
1077 
1078 		if (entry.filename_len > (size_t)(endbuffer - buffer - 24)) {
1079 			MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
1080 		}
1081 
1082 		if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') {
1083 			entry.is_dir = 1;
1084 		} else {
1085 			entry.is_dir = 0;
1086 		}
1087 
1088 		phar_add_virtual_dirs(mydata, buffer, entry.filename_len);
1089 		entry.filename = pestrndup(buffer, entry.filename_len, entry.is_persistent);
1090 		buffer += entry.filename_len;
1091 		PHAR_GET_32(buffer, entry.uncompressed_filesize);
1092 		PHAR_GET_32(buffer, entry.timestamp);
1093 
1094 		if (offset == halt_offset + manifest_len + 4) {
1095 			mydata->min_timestamp = entry.timestamp;
1096 			mydata->max_timestamp = entry.timestamp;
1097 		} else {
1098 			if (mydata->min_timestamp > entry.timestamp) {
1099 				mydata->min_timestamp = entry.timestamp;
1100 			} else if (mydata->max_timestamp < entry.timestamp) {
1101 				mydata->max_timestamp = entry.timestamp;
1102 			}
1103 		}
1104 
1105 		PHAR_GET_32(buffer, entry.compressed_filesize);
1106 		PHAR_GET_32(buffer, entry.crc32);
1107 		PHAR_GET_32(buffer, entry.flags);
1108 
1109 		if (entry.is_dir) {
1110 			entry.filename_len--;
1111 			entry.flags |= PHAR_ENT_PERM_DEF_DIR;
1112 		}
1113 
1114 		PHAR_GET_32(buffer, len);
1115 		if (entry.is_persistent) {
1116 			entry.metadata_len = len;
1117 		} else {
1118 			entry.metadata_len = 0;
1119 		}
1120 		if (len > (size_t)(endbuffer - buffer)) {
1121 			pefree(entry.filename, entry.is_persistent);
1122 			MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
1123 		}
1124 		if (phar_parse_metadata(&buffer, &entry.metadata, len) == FAILURE) {
1125 			pefree(entry.filename, entry.is_persistent);
1126 			MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
1127 		}
1128 		buffer += len;
1129 
1130 		entry.offset = entry.offset_abs = offset;
1131 		offset += entry.compressed_filesize;
1132 
1133 		switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
1134 			case PHAR_ENT_COMPRESSED_GZ:
1135 				if (!PHAR_G(has_zlib)) {
1136 					if (Z_TYPE(entry.metadata) != IS_UNDEF) {
1137 						if (entry.is_persistent) {
1138 							free(Z_PTR(entry.metadata));
1139 						} else {
1140 							zval_ptr_dtor(&entry.metadata);
1141 						}
1142 					}
1143 					pefree(entry.filename, entry.is_persistent);
1144 					MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\"");
1145 				}
1146 				break;
1147 			case PHAR_ENT_COMPRESSED_BZ2:
1148 				if (!PHAR_G(has_bz2)) {
1149 					if (Z_TYPE(entry.metadata) != IS_UNDEF) {
1150 						if (entry.is_persistent) {
1151 							free(Z_PTR(entry.metadata));
1152 						} else {
1153 							zval_ptr_dtor(&entry.metadata);
1154 						}
1155 					}
1156 					pefree(entry.filename, entry.is_persistent);
1157 					MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\"");
1158 				}
1159 				break;
1160 			default:
1161 				if (entry.uncompressed_filesize != entry.compressed_filesize) {
1162 					if (Z_TYPE(entry.metadata) != IS_UNDEF) {
1163 						if (entry.is_persistent) {
1164 							free(Z_PTR(entry.metadata));
1165 						} else {
1166 							zval_ptr_dtor(&entry.metadata);
1167 						}
1168 					}
1169 					pefree(entry.filename, entry.is_persistent);
1170 					MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)");
1171 				}
1172 				break;
1173 		}
1174 
1175 		manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK);
1176 		/* if signature matched, no need to check CRC32 for each file */
1177 		entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0);
1178 		phar_set_inode(&entry);
1179 		if (mydata->is_persistent) {
1180 			str = zend_string_init_interned(entry.filename, entry.filename_len, 1);
1181 		} else {
1182 			str = zend_string_init(entry.filename, entry.filename_len, 0);
1183 		}
1184 		zend_hash_add_mem(&mydata->manifest, str, (void*)&entry, sizeof(phar_entry_info));
1185 		zend_string_release(str);
1186 	}
1187 
1188 	snprintf(mydata->version, sizeof(mydata->version), "%u.%u.%u", manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0xF);
1189 	mydata->internal_file_start = halt_offset + manifest_len + 4;
1190 	mydata->halt_offset = halt_offset;
1191 	mydata->flags = manifest_flags;
1192 	endbuffer = strrchr(mydata->fname, '/');
1193 
1194 	if (endbuffer) {
1195 		mydata->ext = memchr(endbuffer, '.', (mydata->fname + fname_len) - endbuffer);
1196 		if (mydata->ext == endbuffer) {
1197 			mydata->ext = memchr(endbuffer + 1, '.', (mydata->fname + fname_len) - endbuffer - 1);
1198 		}
1199 		if (mydata->ext) {
1200 			mydata->ext_len = (mydata->fname + mydata->fname_len) - mydata->ext;
1201 		}
1202 	}
1203 
1204 	mydata->alias = alias ?
1205 		pestrndup(alias, alias_len, mydata->is_persistent) :
1206 		pestrndup(mydata->fname, fname_len, mydata->is_persistent);
1207 	mydata->alias_len = alias ? alias_len : fname_len;
1208 	mydata->sig_flags = sig_flags;
1209 	mydata->fp = fp;
1210 	mydata->sig_len = sig_len;
1211 	mydata->signature = signature;
1212 	phar_request_initialize();
1213 
1214 	if (register_alias) {
1215 		phar_archive_data *fd_ptr;
1216 
1217 		mydata->is_temporary_alias = temp_alias;
1218 
1219 		if (!phar_validate_alias(mydata->alias, mydata->alias_len)) {
1220 			signature = NULL;
1221 			fp = NULL;
1222 			MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias");
1223 		}
1224 
1225 		if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
1226 			if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
1227 				signature = NULL;
1228 				fp = NULL;
1229 				MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive");
1230 			}
1231 		}
1232 
1233 		if (mydata->is_persistent) {
1234 			str = zend_string_init_interned(alias, alias_len, 1);
1235 		} else {
1236 			str = zend_string_init(alias, alias_len, 0);
1237 		}
1238 		zend_hash_add_ptr(&(PHAR_G(phar_alias_map)), str, mydata);
1239 		zend_string_release(str);
1240 	} else {
1241 		mydata->is_temporary_alias = 1;
1242 	}
1243 
1244 	if (mydata->is_persistent) {
1245 		str = zend_string_init_interned(mydata->fname, fname_len, 1);
1246 	} else {
1247 		str = zend_string_init(mydata->fname, fname_len, 0);
1248 	}
1249 	zend_hash_add_ptr(&(PHAR_G(phar_fname_map)), str, mydata);
1250 	zend_string_release(str);
1251 	efree(savebuf);
1252 
1253 	if (pphar) {
1254 		*pphar = mydata;
1255 	}
1256 
1257 	return SUCCESS;
1258 }
1259 /* }}} */
1260 
1261 /**
1262  * Create or open a phar for writing
1263  */
phar_open_or_create_filename(char * fname,size_t fname_len,char * alias,size_t alias_len,zend_bool is_data,uint32_t options,phar_archive_data ** pphar,char ** error)1264 int phar_open_or_create_filename(char *fname, size_t fname_len, char *alias, size_t alias_len, zend_bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
1265 {
1266 	const char *ext_str, *z;
1267 	char *my_error;
1268 	size_t ext_len;
1269 	phar_archive_data **test, *unused = NULL;
1270 
1271 	test = &unused;
1272 
1273 	if (error) {
1274 		*error = NULL;
1275 	}
1276 
1277 	/* first try to open an existing file */
1278 	if (phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 0, 1) == SUCCESS) {
1279 		goto check_file;
1280 	}
1281 
1282 	/* next try to create a new file */
1283 	if (FAILURE == phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 1, 1)) {
1284 		if (error) {
1285 			if (ext_len == -2) {
1286 				spprintf(error, 0, "Cannot create a phar archive from a URL like \"%s\". Phar objects can only be created from local files", fname);
1287 			} else {
1288 				spprintf(error, 0, "Cannot create phar '%s', file extension (or combination) not recognised or the directory does not exist", fname);
1289 			}
1290 		}
1291 		return FAILURE;
1292 	}
1293 check_file:
1294 	if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, test, &my_error) == SUCCESS) {
1295 		if (pphar) {
1296 			*pphar = *test;
1297 		}
1298 
1299 		if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) {
1300 			if (error) {
1301 				spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname);
1302 			}
1303 			return FAILURE;
1304 		}
1305 
1306 		if (PHAR_G(readonly) && !(*test)->is_data && ((*test)->is_tar || (*test)->is_zip)) {
1307 			phar_entry_info *stub;
1308 			if (NULL == (stub = zend_hash_str_find_ptr(&((*test)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1))) {
1309 				spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
1310 				return FAILURE;
1311 			}
1312 		}
1313 
1314 		if (!PHAR_G(readonly) || (*test)->is_data) {
1315 			(*test)->is_writeable = 1;
1316 		}
1317 		return SUCCESS;
1318 	} else if (my_error) {
1319 		if (error) {
1320 			*error = my_error;
1321 		} else {
1322 			efree(my_error);
1323 		}
1324 		return FAILURE;
1325 	}
1326 
1327 	if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) {
1328 		/* assume zip-based phar */
1329 		return phar_open_or_create_zip(fname, fname_len, alias, alias_len, is_data, options, pphar, error);
1330 	}
1331 
1332 	if (ext_len > 3 && (z = memchr(ext_str, 't', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ar", 2)) {
1333 		/* assume tar-based phar */
1334 		return phar_open_or_create_tar(fname, fname_len, alias, alias_len, is_data, options, pphar, error);
1335 	}
1336 
1337 	return phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, pphar, error);
1338 }
1339 /* }}} */
1340 
phar_create_or_parse_filename(char * fname,size_t fname_len,char * alias,size_t alias_len,zend_bool is_data,uint32_t options,phar_archive_data ** pphar,char ** error)1341 int phar_create_or_parse_filename(char *fname, size_t fname_len, char *alias, size_t alias_len, zend_bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
1342 {
1343 	phar_archive_data *mydata;
1344 	php_stream *fp;
1345 	zend_string *actual = NULL;
1346 	char *p;
1347 
1348 	if (!pphar) {
1349 		pphar = &mydata;
1350 	}
1351 	if (php_check_open_basedir(fname)) {
1352 		return FAILURE;
1353 	}
1354 
1355 	/* first open readonly so it won't be created if not present */
1356 	fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual);
1357 
1358 	if (actual) {
1359 		fname = ZSTR_VAL(actual);
1360 		fname_len = ZSTR_LEN(actual);
1361 	}
1362 
1363 	if (fp) {
1364 		if (phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error) == SUCCESS) {
1365 			if ((*pphar)->is_data || !PHAR_G(readonly)) {
1366 				(*pphar)->is_writeable = 1;
1367 			}
1368 			if (actual) {
1369 				zend_string_release_ex(actual, 0);
1370 			}
1371 			return SUCCESS;
1372 		} else {
1373 			/* file exists, but is either corrupt or not a phar archive */
1374 			if (actual) {
1375 				zend_string_release_ex(actual, 0);
1376 			}
1377 			return FAILURE;
1378 		}
1379 	}
1380 
1381 	if (actual) {
1382 		zend_string_release_ex(actual, 0);
1383 	}
1384 
1385 	if (PHAR_G(readonly) && !is_data) {
1386 		if (options & REPORT_ERRORS) {
1387 			if (error) {
1388 				spprintf(error, 0, "creating archive \"%s\" disabled by the php.ini setting phar.readonly", fname);
1389 			}
1390 		}
1391 		return FAILURE;
1392 	}
1393 
1394 	/* set up our manifest */
1395 	mydata = ecalloc(1, sizeof(phar_archive_data));
1396 	mydata->fname = expand_filepath(fname, NULL);
1397 	if (mydata->fname == NULL) {
1398 		efree(mydata);
1399 		return FAILURE;
1400 	}
1401 	fname_len = strlen(mydata->fname);
1402 #ifdef PHP_WIN32
1403 	phar_unixify_path_separators(mydata->fname, fname_len);
1404 #endif
1405 	p = strrchr(mydata->fname, '/');
1406 
1407 	if (p) {
1408 		mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p);
1409 		if (mydata->ext == p) {
1410 			mydata->ext = memchr(p + 1, '.', (mydata->fname + fname_len) - p - 1);
1411 		}
1412 		if (mydata->ext) {
1413 			mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
1414 		}
1415 	}
1416 
1417 	if (pphar) {
1418 		*pphar = mydata;
1419 	}
1420 
1421 	zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
1422 		zend_get_hash_value, destroy_phar_manifest_entry, 0);
1423 	zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
1424 		zend_get_hash_value, NULL, 0);
1425 	zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
1426 		zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1427 	mydata->fname_len = fname_len;
1428 	snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
1429 	mydata->is_temporary_alias = alias ? 0 : 1;
1430 	mydata->internal_file_start = -1;
1431 	mydata->fp = NULL;
1432 	mydata->is_writeable = 1;
1433 	mydata->is_brandnew = 1;
1434 	phar_request_initialize();
1435 	zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len, mydata);
1436 
1437 	if (is_data) {
1438 		alias = NULL;
1439 		alias_len = 0;
1440 		mydata->is_data = 1;
1441 		/* assume tar format, PharData can specify other */
1442 		mydata->is_tar = 1;
1443 	} else {
1444 		phar_archive_data *fd_ptr;
1445 
1446 		if (alias && NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
1447 			if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
1448 				if (error) {
1449 					spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias);
1450 				}
1451 
1452 				zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
1453 
1454 				if (pphar) {
1455 					*pphar = NULL;
1456 				}
1457 
1458 				return FAILURE;
1459 			}
1460 		}
1461 
1462 		mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len);
1463 		mydata->alias_len = alias ? alias_len : fname_len;
1464 	}
1465 
1466 	if (alias_len && alias) {
1467 		if (NULL == zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, mydata)) {
1468 			if (options & REPORT_ERRORS) {
1469 				if (error) {
1470 					spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias);
1471 				}
1472 			}
1473 
1474 			zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
1475 
1476 			if (pphar) {
1477 				*pphar = NULL;
1478 			}
1479 
1480 			return FAILURE;
1481 		}
1482 	}
1483 
1484 	return SUCCESS;
1485 }
1486 /* }}}*/
1487 
1488 /**
1489  * Return an already opened filename.
1490  *
1491  * Or scan a phar file for the required __HALT_COMPILER(); ?> token and verify
1492  * that the manifest is proper, then pass it to phar_parse_pharfile().  SUCCESS
1493  * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1494  */
phar_open_from_filename(char * fname,size_t fname_len,char * alias,size_t alias_len,uint32_t options,phar_archive_data ** pphar,char ** error)1495 int phar_open_from_filename(char *fname, size_t fname_len, char *alias, size_t alias_len, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
1496 {
1497 	php_stream *fp;
1498 	zend_string *actual;
1499 	int ret, is_data = 0;
1500 
1501 	if (error) {
1502 		*error = NULL;
1503 	}
1504 
1505 	if (!strstr(fname, ".phar")) {
1506 		is_data = 1;
1507 	}
1508 
1509 	if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error) == SUCCESS) {
1510 		return SUCCESS;
1511 	} else if (error && *error) {
1512 		return FAILURE;
1513 	}
1514 	if (php_check_open_basedir(fname)) {
1515 		return FAILURE;
1516 	}
1517 
1518 	fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
1519 
1520 	if (!fp) {
1521 		if (options & REPORT_ERRORS) {
1522 			if (error) {
1523 				spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
1524 			}
1525 		}
1526 		if (actual) {
1527 			zend_string_release_ex(actual, 0);
1528 		}
1529 		return FAILURE;
1530 	}
1531 
1532 	if (actual) {
1533 		fname = ZSTR_VAL(actual);
1534 		fname_len = ZSTR_LEN(actual);
1535 	}
1536 
1537 	ret =  phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error);
1538 
1539 	if (actual) {
1540 		zend_string_release_ex(actual, 0);
1541 	}
1542 
1543 	return ret;
1544 }
1545 /* }}}*/
1546 
phar_strnstr(const char * buf,int buf_len,const char * search,int search_len)1547 static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) /* {{{ */
1548 {
1549 	const char *c;
1550 	ptrdiff_t so_far = 0;
1551 
1552 	if (buf_len < search_len) {
1553 		return NULL;
1554 	}
1555 
1556 	c = buf - 1;
1557 
1558 	do {
1559 		if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) {
1560 			return (char *) NULL;
1561 		}
1562 
1563 		so_far = c - buf;
1564 
1565 		if (so_far >= (buf_len - search_len)) {
1566 			return (char *) NULL;
1567 		}
1568 
1569 		if (!memcmp(c, search, search_len)) {
1570 			return (char *) c;
1571 		}
1572 	} while (1);
1573 }
1574 /* }}} */
1575 
1576 /**
1577  * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify
1578  * that the manifest is proper, then pass it to phar_parse_pharfile().  SUCCESS
1579  * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1580  */
phar_open_from_fp(php_stream * fp,char * fname,size_t fname_len,char * alias,size_t alias_len,uint32_t options,phar_archive_data ** pphar,int is_data,char ** error)1581 static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char *alias, size_t alias_len, uint32_t options, phar_archive_data** pphar, int is_data, char **error) /* {{{ */
1582 {
1583 	const char token[] = "__HALT_COMPILER();";
1584 	const char zip_magic[] = "PK\x03\x04";
1585 	const char gz_magic[] = "\x1f\x8b\x08";
1586 	const char bz_magic[] = "BZh";
1587 	char *pos, test = '\0';
1588 	const int window_size = 1024;
1589 	char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */
1590 	const zend_long readsize = sizeof(buffer) - sizeof(token);
1591 	const zend_long tokenlen = sizeof(token) - 1;
1592 	zend_long halt_offset;
1593 	size_t got;
1594 	uint32_t compression = PHAR_FILE_COMPRESSED_NONE;
1595 
1596 	if (error) {
1597 		*error = NULL;
1598 	}
1599 
1600 	if (-1 == php_stream_rewind(fp)) {
1601 		MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"")
1602 	}
1603 
1604 	buffer[sizeof(buffer)-1] = '\0';
1605 	memset(buffer, 32, sizeof(token));
1606 	halt_offset = 0;
1607 
1608 	/* Maybe it's better to compile the file instead of just searching,  */
1609 	/* but we only want the offset. So we want a .re scanner to find it. */
1610 	while(!php_stream_eof(fp)) {
1611 		if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) {
1612 			MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
1613 		}
1614 
1615 		if (!test) {
1616 			test = '\1';
1617 			pos = buffer+tokenlen;
1618 			if (!memcmp(pos, gz_magic, 3)) {
1619 				char err = 0;
1620 				php_stream_filter *filter;
1621 				php_stream *temp;
1622 				/* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
1623 				zval filterparams;
1624 
1625 				if (!PHAR_G(has_zlib)) {
1626 					MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini")
1627 				}
1628 				array_init(&filterparams);
1629 /* this is defined in zlib's zconf.h */
1630 #ifndef MAX_WBITS
1631 #define MAX_WBITS 15
1632 #endif
1633 				add_assoc_long_ex(&filterparams, "window", sizeof("window") - 1, MAX_WBITS + 32);
1634 
1635 				/* entire file is gzip-compressed, uncompress to temporary file */
1636 				if (!(temp = php_stream_fopen_tmpfile())) {
1637 					MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"")
1638 				}
1639 
1640 				php_stream_rewind(fp);
1641 				filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp));
1642 
1643 				if (!filter) {
1644 					err = 1;
1645 					add_assoc_long_ex(&filterparams, "window", sizeof("window") - 1, MAX_WBITS);
1646 					filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp));
1647 					zend_array_destroy(Z_ARR(filterparams));
1648 
1649 					if (!filter) {
1650 						php_stream_close(temp);
1651 						MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1652 					}
1653 				} else {
1654 					zend_array_destroy(Z_ARR(filterparams));
1655 				}
1656 
1657 				php_stream_filter_append(&temp->writefilters, filter);
1658 
1659 				if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1660 					if (err) {
1661 						php_stream_close(temp);
1662 						MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1663 					}
1664 					php_stream_close(temp);
1665 					MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file")
1666 				}
1667 
1668 				php_stream_filter_flush(filter, 1);
1669 				php_stream_filter_remove(filter, 1);
1670 				php_stream_close(fp);
1671 				fp = temp;
1672 				php_stream_rewind(fp);
1673 				compression = PHAR_FILE_COMPRESSED_GZ;
1674 
1675 				/* now, start over */
1676 				test = '\0';
1677 				continue;
1678 			} else if (!memcmp(pos, bz_magic, 3)) {
1679 				php_stream_filter *filter;
1680 				php_stream *temp;
1681 
1682 				if (!PHAR_G(has_bz2)) {
1683 					MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini")
1684 				}
1685 
1686 				/* entire file is bzip-compressed, uncompress to temporary file */
1687 				if (!(temp = php_stream_fopen_tmpfile())) {
1688 					MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"")
1689 				}
1690 
1691 				php_stream_rewind(fp);
1692 				filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp));
1693 
1694 				if (!filter) {
1695 					php_stream_close(temp);
1696 					MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed")
1697 				}
1698 
1699 				php_stream_filter_append(&temp->writefilters, filter);
1700 
1701 				if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1702 					php_stream_close(temp);
1703 					MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file")
1704 				}
1705 
1706 				php_stream_filter_flush(filter, 1);
1707 				php_stream_filter_remove(filter, 1);
1708 				php_stream_close(fp);
1709 				fp = temp;
1710 				php_stream_rewind(fp);
1711 				compression = PHAR_FILE_COMPRESSED_BZ2;
1712 
1713 				/* now, start over */
1714 				test = '\0';
1715 				continue;
1716 			}
1717 
1718 			if (!memcmp(pos, zip_magic, 4)) {
1719 				php_stream_seek(fp, 0, SEEK_END);
1720 				return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error);
1721 			}
1722 
1723 			if (got > 512) {
1724 				if (phar_is_tar(pos, fname)) {
1725 					php_stream_rewind(fp);
1726 					return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error);
1727 				}
1728 			}
1729 		}
1730 
1731 		if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) {
1732 			halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */
1733 			return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error);
1734 		}
1735 
1736 		halt_offset += got;
1737 		memmove(buffer, buffer + window_size, tokenlen); /* move the memory buffer by the size of the window */
1738 	}
1739 
1740 	MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
1741 }
1742 /* }}} */
1743 
1744 /*
1745  * given the location of the file extension and the start of the file path,
1746  * determine the end of the portion of the path (i.e. /path/to/file.ext/blah
1747  * grabs "/path/to/file.ext" as does the straight /path/to/file.ext),
1748  * stat it to determine if it exists.
1749  * if so, check to see if it is a directory and fail if so
1750  * if not, check to see if its dirname() exists (i.e. "/path/to") and is a directory
1751  * succeed if we are creating the file, otherwise fail.
1752  */
phar_analyze_path(const char * fname,const char * ext,size_t ext_len,int for_create)1753 static int phar_analyze_path(const char *fname, const char *ext, size_t ext_len, int for_create) /* {{{ */
1754 {
1755 	php_stream_statbuf ssb;
1756 	char *realpath;
1757 	char *filename = estrndup(fname, (ext - fname) + ext_len);
1758 
1759 	if ((realpath = expand_filepath(filename, NULL))) {
1760 #ifdef PHP_WIN32
1761 		phar_unixify_path_separators(realpath, strlen(realpath));
1762 #endif
1763 		if (zend_hash_str_exists(&(PHAR_G(phar_fname_map)), realpath, strlen(realpath))) {
1764 			efree(realpath);
1765 			efree(filename);
1766 			return SUCCESS;
1767 		}
1768 
1769 		if (PHAR_G(manifest_cached) && zend_hash_str_exists(&cached_phars, realpath, strlen(realpath))) {
1770 			efree(realpath);
1771 			efree(filename);
1772 			return SUCCESS;
1773 		}
1774 		efree(realpath);
1775 	}
1776 
1777 	if (SUCCESS == php_stream_stat_path((char *) filename, &ssb)) {
1778 
1779 		efree(filename);
1780 
1781 		if (ssb.sb.st_mode & S_IFDIR) {
1782 			return FAILURE;
1783 		}
1784 
1785 		if (for_create == 1) {
1786 			return FAILURE;
1787 		}
1788 
1789 		return SUCCESS;
1790 	} else {
1791 		char *slash;
1792 
1793 		if (!for_create) {
1794 			efree(filename);
1795 			return FAILURE;
1796 		}
1797 
1798 		slash = (char *) strrchr(filename, '/');
1799 
1800 		if (slash) {
1801 			*slash = '\0';
1802 		}
1803 
1804 		if (SUCCESS != php_stream_stat_path((char *) filename, &ssb)) {
1805 			if (!slash) {
1806 				if (!(realpath = expand_filepath(filename, NULL))) {
1807 					efree(filename);
1808 					return FAILURE;
1809 				}
1810 #ifdef PHP_WIN32
1811 				phar_unixify_path_separators(realpath, strlen(realpath));
1812 #endif
1813 				slash = strstr(realpath, filename);
1814 				if (slash) {
1815 					slash += ((ext - fname) + ext_len);
1816 					*slash = '\0';
1817 				}
1818 				slash = strrchr(realpath, '/');
1819 
1820 				if (slash) {
1821 					*slash = '\0';
1822 				} else {
1823 					efree(realpath);
1824 					efree(filename);
1825 					return FAILURE;
1826 				}
1827 
1828 				if (SUCCESS != php_stream_stat_path(realpath, &ssb)) {
1829 					efree(realpath);
1830 					efree(filename);
1831 					return FAILURE;
1832 				}
1833 
1834 				efree(realpath);
1835 
1836 				if (ssb.sb.st_mode & S_IFDIR) {
1837 					efree(filename);
1838 					return SUCCESS;
1839 				}
1840 			}
1841 
1842 			efree(filename);
1843 			return FAILURE;
1844 		}
1845 
1846 		efree(filename);
1847 
1848 		if (ssb.sb.st_mode & S_IFDIR) {
1849 			return SUCCESS;
1850 		}
1851 
1852 		return FAILURE;
1853 	}
1854 }
1855 /* }}} */
1856 
1857 /* check for ".phar" in extension */
phar_check_str(const char * fname,const char * ext_str,size_t ext_len,int executable,int for_create)1858 static int phar_check_str(const char *fname, const char *ext_str, size_t ext_len, int executable, int for_create) /* {{{ */
1859 {
1860 	const char *pos;
1861 
1862 	if (ext_len >= 50) {
1863 		return FAILURE;
1864 	}
1865 	if (executable == 1) {
1866 		/* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */
1867 		/* (phar://hi/there/.phar/oops is also invalid) */
1868 		pos = strstr(ext_str, ".phar");
1869 
1870 		if (!pos
1871 			|| (pos != ext_str && (*(pos - 1) == '/'))
1872 			|| (ext_len - (pos - ext_str)) < 5
1873 			|| !(pos += 5)
1874 			|| !(*pos == '\0' || *pos == '/' || *pos == '.')) {
1875 			return FAILURE;
1876 		}
1877 		return phar_analyze_path(fname, ext_str, ext_len, for_create);
1878 	}
1879 
1880 	/* data phars need only contain a single non-"." to be valid */
1881 	if (!executable) {
1882 		pos = strstr(ext_str, ".phar");
1883 		if (!(pos && (*(pos - 1) != '/')
1884 					&& (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) && *(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1885 			return phar_analyze_path(fname, ext_str, ext_len, for_create);
1886 		}
1887 	} else {
1888 		if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1889 			return phar_analyze_path(fname, ext_str, ext_len, for_create);
1890 		}
1891 	}
1892 
1893 	return FAILURE;
1894 }
1895 /* }}} */
1896 
1897 /*
1898  * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions
1899  * if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats
1900  * the first extension as the filename extension
1901  *
1902  * if an extension is found, it sets ext_str to the location of the file extension in filename,
1903  * and ext_len to the length of the extension.
1904  * for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells
1905  * the calling function to use "alias" as the phar alias
1906  *
1907  * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the
1908  * extension rules, not to iterate.
1909  */
phar_detect_phar_fname_ext(const char * filename,size_t filename_len,const char ** ext_str,size_t * ext_len,int executable,int for_create,int is_complete)1910 int phar_detect_phar_fname_ext(const char *filename, size_t filename_len, const char **ext_str, size_t *ext_len, int executable, int for_create, int is_complete) /* {{{ */
1911 {
1912 	const char *pos, *slash;
1913 
1914 	*ext_str = NULL;
1915 	*ext_len = 0;
1916 
1917 	if (!filename_len || filename_len == 1) {
1918 		return FAILURE;
1919 	}
1920 
1921 	phar_request_initialize();
1922 	/* first check for alias in first segment */
1923 	pos = memchr(filename, '/', filename_len);
1924 
1925 	if (pos && pos != filename) {
1926 		/* check for url like http:// or phar:// */
1927 		if (*(pos - 1) == ':' && (size_t)(pos - filename) < filename_len - 1 && *(pos + 1) == '/') {
1928 			*ext_len = -2;
1929 			*ext_str = NULL;
1930 			return FAILURE;
1931 		}
1932 		if (zend_hash_str_exists(&(PHAR_G(phar_alias_map)), (char *) filename, pos - filename)) {
1933 			*ext_str = pos;
1934 			*ext_len = -1;
1935 			return FAILURE;
1936 		}
1937 
1938 		if (PHAR_G(manifest_cached) && zend_hash_str_exists(&cached_alias, (char *) filename, pos - filename)) {
1939 			*ext_str = pos;
1940 			*ext_len = -1;
1941 			return FAILURE;
1942 		}
1943 	}
1944 
1945 	if (zend_hash_num_elements(&(PHAR_G(phar_fname_map))) || PHAR_G(manifest_cached)) {
1946 		phar_archive_data *pphar;
1947 
1948 		if (is_complete) {
1949 			if (NULL != (pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), (char *) filename, filename_len))) {
1950 				*ext_str = filename + (filename_len - pphar->ext_len);
1951 woohoo:
1952 				*ext_len = pphar->ext_len;
1953 
1954 				if (executable == 2) {
1955 					return SUCCESS;
1956 				}
1957 
1958 				if (executable == 1 && !pphar->is_data) {
1959 					return SUCCESS;
1960 				}
1961 
1962 				if (!executable && pphar->is_data) {
1963 					return SUCCESS;
1964 				}
1965 
1966 				return FAILURE;
1967 			}
1968 
1969 			if (PHAR_G(manifest_cached) && NULL != (pphar = zend_hash_str_find_ptr(&cached_phars, (char *) filename, filename_len))) {
1970 				*ext_str = filename + (filename_len - pphar->ext_len);
1971 				goto woohoo;
1972 			}
1973 		} else {
1974 			zend_string *str_key;
1975 
1976 			ZEND_HASH_FOREACH_STR_KEY_PTR(&PHAR_G(phar_fname_map), str_key, pphar) {
1977 				if (ZSTR_LEN(str_key) > (uint32_t) filename_len) {
1978 					continue;
1979 				}
1980 
1981 				if (!memcmp(filename, ZSTR_VAL(str_key), ZSTR_LEN(str_key)) && ((uint32_t)filename_len == ZSTR_LEN(str_key)
1982 					|| filename[ZSTR_LEN(str_key)] == '/' || filename[ZSTR_LEN(str_key)] == '\0')) {
1983 					*ext_str = filename + (ZSTR_LEN(str_key) - pphar->ext_len);
1984 					goto woohoo;
1985 				}
1986 			} ZEND_HASH_FOREACH_END();
1987 
1988 			if (PHAR_G(manifest_cached)) {
1989 				ZEND_HASH_FOREACH_STR_KEY_PTR(&cached_phars, str_key, pphar) {
1990 					if (ZSTR_LEN(str_key) > (uint32_t) filename_len) {
1991 						continue;
1992 					}
1993 
1994 					if (!memcmp(filename, ZSTR_VAL(str_key), ZSTR_LEN(str_key)) && ((uint32_t)filename_len == ZSTR_LEN(str_key)
1995 						|| filename[ZSTR_LEN(str_key)] == '/' || filename[ZSTR_LEN(str_key)] == '\0')) {
1996 						*ext_str = filename + (ZSTR_LEN(str_key) - pphar->ext_len);
1997 						goto woohoo;
1998 					}
1999 				} ZEND_HASH_FOREACH_END();
2000 			}
2001 		}
2002 	}
2003 
2004 	pos = memchr(filename + 1, '.', filename_len);
2005 next_extension:
2006 	if (!pos) {
2007 		return FAILURE;
2008 	}
2009 
2010 	while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
2011 		pos = memchr(pos + 1, '.', filename_len - (pos - filename) - 1);
2012 		if (!pos) {
2013 			return FAILURE;
2014 		}
2015 	}
2016 
2017 	slash = memchr(pos, '/', filename_len - (pos - filename));
2018 
2019 	if (!slash) {
2020 		/* this is a url like "phar://blah.phar" with no directory */
2021 		*ext_str = pos;
2022 		*ext_len = strlen(pos);
2023 
2024 		/* file extension must contain "phar" */
2025 		switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create)) {
2026 			case SUCCESS:
2027 				return SUCCESS;
2028 			case FAILURE:
2029 				/* we are at the end of the string, so we fail */
2030 				return FAILURE;
2031 		}
2032 	}
2033 
2034 	/* we've found an extension that ends at a directory separator */
2035 	*ext_str = pos;
2036 	*ext_len = slash - pos;
2037 
2038 	switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create)) {
2039 		case SUCCESS:
2040 			return SUCCESS;
2041 		case FAILURE:
2042 			/* look for more extensions */
2043 			pos = strchr(pos + 1, '.');
2044 			if (pos) {
2045 				*ext_str = NULL;
2046 				*ext_len = 0;
2047 			}
2048 			goto next_extension;
2049 	}
2050 
2051 	return FAILURE;
2052 }
2053 /* }}} */
2054 
php_check_dots(const char * element,size_t n)2055 static int php_check_dots(const char *element, size_t n) /* {{{ */
2056 {
2057 	for(n-- ; n != SIZE_MAX; --n) {
2058 		if (element[n] != '.') {
2059 			return 1;
2060 		}
2061 	}
2062 	return 0;
2063 }
2064 /* }}} */
2065 
2066 #define IS_DIRECTORY_UP(element, len) \
2067 	(len >= 2 && !php_check_dots(element, len))
2068 
2069 #define IS_DIRECTORY_CURRENT(element, len) \
2070 	(len == 1 && element[0] == '.')
2071 
2072 #define IS_BACKSLASH(c) ((c) == '/')
2073 
2074 /**
2075  * Remove .. and . references within a phar filename
2076  */
phar_fix_filepath(char * path,size_t * new_len,int use_cwd)2077 char *phar_fix_filepath(char *path, size_t *new_len, int use_cwd) /* {{{ */
2078 {
2079 	char *newpath;
2080 	size_t newpath_len;
2081 	char *ptr;
2082 	char *tok;
2083 	size_t ptr_length, path_length = *new_len;
2084 
2085 	if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') {
2086 		newpath_len = PHAR_G(cwd_len);
2087 		newpath = emalloc(strlen(path) + newpath_len + 1);
2088 		memcpy(newpath, PHAR_G(cwd), newpath_len);
2089 	} else {
2090 		newpath = emalloc(strlen(path) + 2);
2091 		newpath[0] = '/';
2092 		newpath_len = 1;
2093 	}
2094 
2095 	ptr = path;
2096 
2097 	if (*ptr == '/') {
2098 		++ptr;
2099 	}
2100 
2101 	tok = ptr;
2102 
2103 	do {
2104 		ptr = memchr(ptr, '/', path_length - (ptr - path));
2105 	} while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2106 
2107 	if (!ptr && (path_length - (tok - path))) {
2108 		switch (path_length - (tok - path)) {
2109 			case 1:
2110 				if (*tok == '.') {
2111 					efree(path);
2112 					*new_len = 1;
2113 					efree(newpath);
2114 					return estrndup("/", 1);
2115 				}
2116 				break;
2117 			case 2:
2118 				if (tok[0] == '.' && tok[1] == '.') {
2119 					efree(path);
2120 					*new_len = 1;
2121 					efree(newpath);
2122 					return estrndup("/", 1);
2123 				}
2124 		}
2125 		efree(newpath);
2126 		return path;
2127 	}
2128 
2129 	while (ptr) {
2130 		ptr_length = ptr - tok;
2131 last_time:
2132 		if (IS_DIRECTORY_UP(tok, ptr_length)) {
2133 #define PREVIOUS newpath[newpath_len - 1]
2134 
2135 			while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) {
2136 				newpath_len--;
2137 			}
2138 
2139 			if (newpath[0] != '/') {
2140 				newpath[newpath_len] = '\0';
2141 			} else if (newpath_len > 1) {
2142 				--newpath_len;
2143 			}
2144 		} else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) {
2145 			if (newpath_len > 1) {
2146 				newpath[newpath_len++] = '/';
2147 				memcpy(newpath + newpath_len, tok, ptr_length+1);
2148 			} else {
2149 				memcpy(newpath + newpath_len, tok, ptr_length+1);
2150 			}
2151 
2152 			newpath_len += ptr_length;
2153 		}
2154 
2155 		if (ptr == path + path_length) {
2156 			break;
2157 		}
2158 
2159 		tok = ++ptr;
2160 
2161 		do {
2162 			ptr = memchr(ptr, '/', path_length - (ptr - path));
2163 		} while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2164 
2165 		if (!ptr && (path_length - (tok - path))) {
2166 			ptr_length = path_length - (tok - path);
2167 			ptr = path + path_length;
2168 			goto last_time;
2169 		}
2170 	}
2171 
2172 	efree(path);
2173 	*new_len = newpath_len;
2174 	newpath[newpath_len] = '\0';
2175 	return erealloc(newpath, newpath_len + 1);
2176 }
2177 /* }}} */
2178 
2179 /**
2180  * Process a phar stream name, ensuring we can handle any of:
2181  *
2182  * - whatever.phar
2183  * - whatever.phar.gz
2184  * - whatever.phar.bz2
2185  * - whatever.phar.php
2186  *
2187  * Optionally the name might start with 'phar://'
2188  *
2189  * This is used by phar_parse_url()
2190  */
phar_split_fname(const char * filename,size_t filename_len,char ** arch,size_t * arch_len,char ** entry,size_t * entry_len,int executable,int for_create)2191 int phar_split_fname(const char *filename, size_t filename_len, char **arch, size_t *arch_len, char **entry, size_t *entry_len, int executable, int for_create) /* {{{ */
2192 {
2193 	const char *ext_str;
2194 #ifdef PHP_WIN32
2195 	char *save;
2196 #endif
2197 	size_t ext_len;
2198 
2199 	if (CHECK_NULL_PATH(filename, filename_len)) {
2200 		return FAILURE;
2201 	}
2202 
2203 	if (!strncasecmp(filename, "phar://", 7)) {
2204 		filename += 7;
2205 		filename_len -= 7;
2206 	}
2207 
2208 	ext_len = 0;
2209 #ifdef PHP_WIN32
2210 	save = (char *)filename;
2211 	if (memchr(filename, '\\', filename_len)) {
2212 		filename = estrndup(filename, filename_len);
2213 		phar_unixify_path_separators((char *)filename, filename_len);
2214 	}
2215 #endif
2216 	if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0) == FAILURE) {
2217 		if (ext_len != -1) {
2218 			if (!ext_str) {
2219 				/* no / detected, restore arch for error message */
2220 #ifdef PHP_WIN32
2221 				*arch = save;
2222 #else
2223 				*arch = (char*)filename;
2224 #endif
2225 			}
2226 
2227 #ifdef PHP_WIN32
2228 			if (filename != save) {
2229 				efree((char *)filename);
2230 			}
2231 #endif
2232 			return FAILURE;
2233 		}
2234 
2235 		ext_len = 0;
2236 		/* no extension detected - instead we are dealing with an alias */
2237 	}
2238 
2239 	*arch_len = ext_str - filename + ext_len;
2240 	*arch = estrndup(filename, *arch_len);
2241 
2242 	if (ext_str[ext_len]) {
2243 		*entry_len = filename_len - *arch_len;
2244 		*entry = estrndup(ext_str+ext_len, *entry_len);
2245 #ifdef PHP_WIN32
2246 		phar_unixify_path_separators(*entry, *entry_len);
2247 #endif
2248 		*entry = phar_fix_filepath(*entry, entry_len, 0);
2249 	} else {
2250 		*entry_len = 1;
2251 		*entry = estrndup("/", 1);
2252 	}
2253 
2254 #ifdef PHP_WIN32
2255 	if (filename != save) {
2256 		efree((char *)filename);
2257 	}
2258 #endif
2259 
2260 	return SUCCESS;
2261 }
2262 /* }}} */
2263 
2264 /**
2265  * Invoked when a user calls Phar::mapPhar() from within an executing .phar
2266  * to set up its manifest directly
2267  */
phar_open_executed_filename(char * alias,size_t alias_len,char ** error)2268 int phar_open_executed_filename(char *alias, size_t alias_len, char **error) /* {{{ */
2269 {
2270 	char *fname;
2271 	php_stream *fp;
2272 	size_t fname_len;
2273 	zend_string *actual = NULL;
2274 	int ret;
2275 
2276 	if (error) {
2277 		*error = NULL;
2278 	}
2279 
2280 	fname = (char*)zend_get_executed_filename();
2281 	fname_len = strlen(fname);
2282 
2283 	if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, 0, REPORT_ERRORS, NULL, 0) == SUCCESS) {
2284 		return SUCCESS;
2285 	}
2286 
2287 	if (!strcmp(fname, "[no active file]")) {
2288 		if (error) {
2289 			spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
2290 		}
2291 		return FAILURE;
2292 	}
2293 
2294 	if (0 == zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) {
2295 		if (error) {
2296 			spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
2297 		}
2298 		return FAILURE;
2299 	}
2300 
2301 	if (php_check_open_basedir(fname)) {
2302 		return FAILURE;
2303 	}
2304 
2305 	fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
2306 
2307 	if (!fp) {
2308 		if (error) {
2309 			spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
2310 		}
2311 		if (actual) {
2312 			zend_string_release_ex(actual, 0);
2313 		}
2314 		return FAILURE;
2315 	}
2316 
2317 	if (actual) {
2318 		fname = ZSTR_VAL(actual);
2319 		fname_len = ZSTR_LEN(actual);
2320 	}
2321 
2322 	ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0, error);
2323 
2324 	if (actual) {
2325 		zend_string_release_ex(actual, 0);
2326 	}
2327 
2328 	return ret;
2329 }
2330 /* }}} */
2331 
2332 /**
2333  * Validate the CRC32 of a file opened from within the phar
2334  */
phar_postprocess_file(phar_entry_data * idata,uint32_t crc32,char ** error,int process_zip)2335 int phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip) /* {{{ */
2336 {
2337 	uint32_t crc = ~0;
2338 	int len = idata->internal_file->uncompressed_filesize;
2339 	php_stream *fp = idata->fp;
2340 	phar_entry_info *entry = idata->internal_file;
2341 
2342 	if (error) {
2343 		*error = NULL;
2344 	}
2345 
2346 	if (entry->is_zip && process_zip > 0) {
2347 		/* verify local file header */
2348 		phar_zip_file_header local;
2349 		phar_zip_data_desc desc;
2350 
2351 		if (SUCCESS != phar_open_archive_fp(idata->phar)) {
2352 			spprintf(error, 0, "phar error: unable to open zip-based phar archive \"%s\" to verify local file header for file \"%s\"", idata->phar->fname, entry->filename);
2353 			return FAILURE;
2354 		}
2355 		php_stream_seek(phar_get_entrypfp(idata->internal_file), entry->header_offset, SEEK_SET);
2356 
2357 		if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file), (char *) &local, sizeof(local))) {
2358 
2359 			spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, entry->filename);
2360 			return FAILURE;
2361 		}
2362 
2363 		/* check for data descriptor */
2364 		if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
2365 			php_stream_seek(phar_get_entrypfp(idata->internal_file),
2366 					entry->header_offset + sizeof(local) +
2367 					PHAR_ZIP_16(local.filename_len) +
2368 					PHAR_ZIP_16(local.extra_len) +
2369 					entry->compressed_filesize, SEEK_SET);
2370 			if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file),
2371 							    (char *) &desc, sizeof(desc))) {
2372 				spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local data descriptor for file \"%s\")", idata->phar->fname, entry->filename);
2373 				return FAILURE;
2374 			}
2375 			if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
2376 				memcpy(&(local.crc32), &(desc.crc32), 12);
2377 			} else {
2378 				/* old data descriptors have no signature */
2379 				memcpy(&(local.crc32), &desc, 12);
2380 			}
2381 		}
2382 		/* verify local header */
2383 		if (entry->filename_len != PHAR_ZIP_16(local.filename_len) || entry->crc32 != PHAR_ZIP_32(local.crc32) || entry->uncompressed_filesize != PHAR_ZIP_32(local.uncompsize) || entry->compressed_filesize != PHAR_ZIP_32(local.compsize)) {
2384 			spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (local header of file \"%s\" does not match central directory)", idata->phar->fname, entry->filename);
2385 			return FAILURE;
2386 		}
2387 
2388 		/* construct actual offset to file start - local extra_len can be different from central extra_len */
2389 		entry->offset = entry->offset_abs =
2390 			sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
2391 
2392 		if (idata->zero && idata->zero != entry->offset_abs) {
2393 			idata->zero = entry->offset_abs;
2394 		}
2395 	}
2396 
2397 	if (process_zip == 1) {
2398 		return SUCCESS;
2399 	}
2400 
2401 	php_stream_seek(fp, idata->zero, SEEK_SET);
2402 
2403 	while (len--) {
2404 		CRC32(crc, php_stream_getc(fp));
2405 	}
2406 
2407 	php_stream_seek(fp, idata->zero, SEEK_SET);
2408 
2409 	if (~crc == crc32) {
2410 		entry->is_crc_checked = 1;
2411 		return SUCCESS;
2412 	} else {
2413 		spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
2414 		return FAILURE;
2415 	}
2416 }
2417 /* }}} */
2418 
phar_set_32(char * buffer,uint32_t var)2419 static inline void phar_set_32(char *buffer, uint32_t var) /* {{{ */
2420 {
2421 #ifdef WORDS_BIGENDIAN
2422 	*((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
2423 	*((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
2424 	*((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
2425 	*((buffer) + 0) = (unsigned char) ((var) & 0xFF);
2426 #else
2427 	 memcpy(buffer, &var, sizeof(var));
2428 #endif
2429 } /* }}} */
2430 
phar_flush_clean_deleted_apply(zval * zv)2431 static int phar_flush_clean_deleted_apply(zval *zv) /* {{{ */
2432 {
2433 	phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
2434 
2435 	if (entry->fp_refcount <= 0 && entry->is_deleted) {
2436 		return ZEND_HASH_APPLY_REMOVE;
2437 	} else {
2438 		return ZEND_HASH_APPLY_KEEP;
2439 	}
2440 }
2441 /* }}} */
2442 
2443 #include "stub.h"
2444 
phar_create_default_stub(const char * index_php,const char * web_index,char ** error)2445 zend_string *phar_create_default_stub(const char *index_php, const char *web_index, char **error) /* {{{ */
2446 {
2447 	size_t index_len, web_len;
2448 
2449 	if (error) {
2450 		*error = NULL;
2451 	}
2452 
2453 	if (!index_php) {
2454 		index_php = "index.php";
2455 	}
2456 
2457 	if (!web_index) {
2458 		web_index = "index.php";
2459 	}
2460 
2461 	index_len = strlen(index_php);
2462 	web_len = strlen(web_index);
2463 
2464 	if (index_len > 400) {
2465 		/* ridiculous size not allowed for index.php startup filename */
2466 		if (error) {
2467 			spprintf(error, 0, "Illegal filename passed in for stub creation, was %zd characters long, and only 400 or less is allowed", index_len);
2468 			return NULL;
2469 		}
2470 	}
2471 
2472 	if (web_len > 400) {
2473 		/* ridiculous size not allowed for index.php startup filename */
2474 		if (error) {
2475 			spprintf(error, 0, "Illegal web filename passed in for stub creation, was %zd characters long, and only 400 or less is allowed", web_len);
2476 			return NULL;
2477 		}
2478 	}
2479 
2480 	return phar_get_stub(index_php, web_index, index_len+1, web_len+1);
2481 }
2482 /* }}} */
2483 
2484 /**
2485  * Save phar contents to disk
2486  *
2487  * user_stub contains either a string, or a resource pointer, if len is a negative length.
2488  * user_stub and len should be both 0 if the default or existing stub should be used
2489  */
phar_flush(phar_archive_data * phar,char * user_stub,zend_long len,int convert,char ** error)2490 int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int convert, char **error) /* {{{ */
2491 {
2492 	char halt_stub[] = "__HALT_COMPILER();";
2493 	zend_string *newstub;
2494 	char *tmp;
2495 	phar_entry_info *entry, *newentry;
2496 	size_t halt_offset;
2497 	int restore_alias_len, global_flags = 0, closeoldfile;
2498 	char *pos, has_dirs = 0;
2499 	char manifest[18], entry_buffer[24];
2500 	zend_off_t manifest_ftell;
2501 	zend_long offset;
2502 	size_t wrote;
2503 	uint32_t manifest_len, mytime, loc, new_manifest_count;
2504 	uint32_t newcrc32;
2505 	php_stream *file, *oldfile, *newfile, *stubfile;
2506 	php_stream_filter *filter;
2507 	php_serialize_data_t metadata_hash;
2508 	smart_str main_metadata_str = {0};
2509 	int free_user_stub, free_fp = 1, free_ufp = 1;
2510 	int manifest_hack = 0;
2511 	php_stream *shared_cfp = NULL;
2512 
2513 	if (phar->is_persistent) {
2514 		if (error) {
2515 			spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
2516 		}
2517 		return EOF;
2518 	}
2519 
2520 	if (error) {
2521 		*error = NULL;
2522 	}
2523 
2524 	if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
2525 		return EOF;
2526 	}
2527 
2528 	zend_hash_clean(&phar->virtual_dirs);
2529 
2530 	if (phar->is_zip) {
2531 		return phar_zip_flush(phar, user_stub, len, convert, error);
2532 	}
2533 
2534 	if (phar->is_tar) {
2535 		return phar_tar_flush(phar, user_stub, len, convert, error);
2536 	}
2537 
2538 	if (PHAR_G(readonly)) {
2539 		return EOF;
2540 	}
2541 
2542 	if (phar->fp && !phar->is_brandnew) {
2543 		oldfile = phar->fp;
2544 		closeoldfile = 0;
2545 		php_stream_rewind(oldfile);
2546 	} else {
2547 		oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
2548 		closeoldfile = oldfile != NULL;
2549 	}
2550 	newfile = php_stream_fopen_tmpfile();
2551 	if (!newfile) {
2552 		if (error) {
2553 			spprintf(error, 0, "unable to create temporary file");
2554 		}
2555 		if (closeoldfile) {
2556 			php_stream_close(oldfile);
2557 		}
2558 		return EOF;
2559 	}
2560 
2561 	if (user_stub) {
2562 		zend_string *suser_stub;
2563 		if (len < 0) {
2564 			/* resource passed in */
2565 			if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
2566 				if (closeoldfile) {
2567 					php_stream_close(oldfile);
2568 				}
2569 				php_stream_close(newfile);
2570 				if (error) {
2571 					spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
2572 				}
2573 				return EOF;
2574 			}
2575 			if (len == -1) {
2576 				len = PHP_STREAM_COPY_ALL;
2577 			} else {
2578 				len = -len;
2579 			}
2580 			user_stub = 0;
2581 
2582 			if (!(suser_stub = php_stream_copy_to_mem(stubfile, len, 0))) {
2583 				if (closeoldfile) {
2584 					php_stream_close(oldfile);
2585 				}
2586 				php_stream_close(newfile);
2587 				if (error) {
2588 					spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
2589 				}
2590 				return EOF;
2591 			}
2592 			free_user_stub = 1;
2593 			user_stub = ZSTR_VAL(suser_stub);
2594 			len = ZSTR_LEN(suser_stub);
2595 		} else {
2596 			free_user_stub = 0;
2597 		}
2598 		tmp = estrndup(user_stub, len);
2599 		if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
2600 			efree(tmp);
2601 			if (closeoldfile) {
2602 				php_stream_close(oldfile);
2603 			}
2604 			php_stream_close(newfile);
2605 			if (error) {
2606 				spprintf(error, 0, "illegal stub for phar \"%s\" (__HALT_COMPILER(); is missing)", phar->fname);
2607 			}
2608 			if (free_user_stub) {
2609 				zend_string_free(suser_stub);
2610 			}
2611 			return EOF;
2612 		}
2613 		pos = user_stub + (pos - tmp);
2614 		efree(tmp);
2615 		len = pos - user_stub + 18;
2616 		if ((size_t)len != php_stream_write(newfile, user_stub, len)
2617 		||			  5 != php_stream_write(newfile, " ?>\r\n", 5)) {
2618 			if (closeoldfile) {
2619 				php_stream_close(oldfile);
2620 			}
2621 			php_stream_close(newfile);
2622 			if (error) {
2623 				spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
2624 			}
2625 			if (free_user_stub) {
2626 				zend_string_free(suser_stub);
2627 			}
2628 			return EOF;
2629 		}
2630 		phar->halt_offset = len + 5;
2631 		if (free_user_stub) {
2632 			zend_string_free(suser_stub);
2633 		}
2634 	} else {
2635 		size_t written;
2636 
2637 		if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
2638 			php_stream_copy_to_stream_ex(oldfile, newfile, phar->halt_offset, &written);
2639 			newstub = NULL;
2640 		} else {
2641 			/* this is either a brand new phar or a default stub overwrite */
2642 			newstub = phar_create_default_stub(NULL, NULL, NULL);
2643 			phar->halt_offset = ZSTR_LEN(newstub);
2644 			written = php_stream_write(newfile, ZSTR_VAL(newstub), phar->halt_offset);
2645 		}
2646 		if (phar->halt_offset != written) {
2647 			if (closeoldfile) {
2648 				php_stream_close(oldfile);
2649 			}
2650 			php_stream_close(newfile);
2651 			if (error) {
2652 				if (newstub) {
2653 					spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
2654 				} else {
2655 					spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
2656 				}
2657 			}
2658 			if (newstub) {
2659 				zend_string_free(newstub);
2660 			}
2661 			return EOF;
2662 		}
2663 		if (newstub) {
2664 			zend_string_free(newstub);
2665 		}
2666 	}
2667 	manifest_ftell = php_stream_tell(newfile);
2668 	halt_offset = manifest_ftell;
2669 
2670 	/* Check whether we can get rid of some of the deleted entries which are
2671 	 * unused. However some might still be in use so even after this clean-up
2672 	 * we need to skip entries marked is_deleted. */
2673 	zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply);
2674 
2675 	/* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
2676 	main_metadata_str.s = NULL;
2677 	if (Z_TYPE(phar->metadata) != IS_UNDEF) {
2678 		PHP_VAR_SERIALIZE_INIT(metadata_hash);
2679 		php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash);
2680 		PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2681 	}
2682 	new_manifest_count = 0;
2683 	offset = 0;
2684 	ZEND_HASH_FOREACH_PTR(&phar->manifest, entry) {
2685 		if (entry->cfp) {
2686 			/* did we forget to get rid of cfp last time? */
2687 			php_stream_close(entry->cfp);
2688 			entry->cfp = 0;
2689 		}
2690 		if (entry->is_deleted || entry->is_mounted) {
2691 			/* remove this from the new phar */
2692 			continue;
2693 		}
2694 		if (!entry->is_modified && entry->fp_refcount) {
2695 			/* open file pointers refer to this fp, do not free the stream */
2696 			switch (entry->fp_type) {
2697 				case PHAR_FP:
2698 					free_fp = 0;
2699 					break;
2700 				case PHAR_UFP:
2701 					free_ufp = 0;
2702 				default:
2703 					break;
2704 			}
2705 		}
2706 		/* after excluding deleted files, calculate manifest size in bytes and number of entries */
2707 		++new_manifest_count;
2708 		phar_add_virtual_dirs(phar, entry->filename, entry->filename_len);
2709 
2710 		if (entry->is_dir) {
2711 			/* we use this to calculate API version, 1.1.1 is used for phars with directories */
2712 			has_dirs = 1;
2713 		}
2714 		if (Z_TYPE(entry->metadata) != IS_UNDEF) {
2715 			if (entry->metadata_str.s) {
2716 				smart_str_free(&entry->metadata_str);
2717 			}
2718 			entry->metadata_str.s = NULL;
2719 			PHP_VAR_SERIALIZE_INIT(metadata_hash);
2720 			php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash);
2721 			PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2722 		} else {
2723 			if (entry->metadata_str.s) {
2724 				smart_str_free(&entry->metadata_str);
2725 			}
2726 			entry->metadata_str.s = NULL;
2727 		}
2728 
2729 		/* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
2730 		offset += 4 + entry->filename_len + sizeof(entry_buffer) + (entry->metadata_str.s ? ZSTR_LEN(entry->metadata_str.s) : 0) + (entry->is_dir ? 1 : 0);
2731 
2732 		/* compress and rehash as necessary */
2733 		if ((oldfile && !entry->is_modified) || entry->is_dir) {
2734 			if (entry->fp_type == PHAR_UFP) {
2735 				/* reset so we can copy the compressed data over */
2736 				entry->fp_type = PHAR_FP;
2737 			}
2738 			continue;
2739 		}
2740 		if (!phar_get_efp(entry, 0)) {
2741 			/* re-open internal file pointer just-in-time */
2742 			newentry = phar_open_jit(phar, entry, error);
2743 			if (!newentry) {
2744 				/* major problem re-opening, so we ignore this file and the error */
2745 				efree(*error);
2746 				*error = NULL;
2747 				continue;
2748 			}
2749 			entry = newentry;
2750 		}
2751 		file = phar_get_efp(entry, 0);
2752 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1)) {
2753 			if (closeoldfile) {
2754 				php_stream_close(oldfile);
2755 			}
2756 			php_stream_close(newfile);
2757 			if (error) {
2758 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2759 			}
2760 			return EOF;
2761 		}
2762 		newcrc32 = ~0;
2763 		mytime = entry->uncompressed_filesize;
2764 		for (loc = 0;loc < mytime; ++loc) {
2765 			CRC32(newcrc32, php_stream_getc(file));
2766 		}
2767 		entry->crc32 = ~newcrc32;
2768 		entry->is_crc_checked = 1;
2769 		if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
2770 			/* not compressed */
2771 			entry->compressed_filesize = entry->uncompressed_filesize;
2772 			continue;
2773 		}
2774 		filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
2775 		if (!filter) {
2776 			if (closeoldfile) {
2777 				php_stream_close(oldfile);
2778 			}
2779 			php_stream_close(newfile);
2780 			if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
2781 				if (error) {
2782 					spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2783 				}
2784 			} else {
2785 				if (error) {
2786 					spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2787 				}
2788 			}
2789 			return EOF;
2790 		}
2791 
2792 		/* create new file that holds the compressed versions */
2793 		/* work around inability to specify freedom in write and strictness
2794 		in read count */
2795 		if (shared_cfp == NULL) {
2796 			shared_cfp = php_stream_fopen_tmpfile();
2797 		}
2798 		entry->cfp = shared_cfp;
2799 		if (!entry->cfp) {
2800 			if (error) {
2801 				spprintf(error, 0, "unable to create temporary file");
2802 			}
2803 			if (closeoldfile) {
2804 				php_stream_close(oldfile);
2805 			}
2806 			php_stream_close(newfile);
2807 			goto cleanup;
2808 		}
2809 		/* for real phars, header_offset is unused; we misuse it here to store the offset in the temp file */
2810 		ZEND_ASSERT(entry->header_offset == 0);
2811 		entry->header_offset = php_stream_tell(entry->cfp);
2812 		php_stream_flush(file);
2813 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
2814 			if (closeoldfile) {
2815 				php_stream_close(oldfile);
2816 			}
2817 			php_stream_close(newfile);
2818 			if (error) {
2819 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2820 			}
2821 			goto cleanup;
2822 		}
2823 		php_stream_filter_append((&entry->cfp->writefilters), filter);
2824 		if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
2825 			if (closeoldfile) {
2826 				php_stream_close(oldfile);
2827 			}
2828 			php_stream_close(newfile);
2829 			if (error) {
2830 				spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2831 			}
2832 			goto cleanup;
2833 		}
2834 		php_stream_filter_flush(filter, 1);
2835 		php_stream_flush(entry->cfp);
2836 		php_stream_filter_remove(filter, 1);
2837 		php_stream_seek(entry->cfp, 0, SEEK_END);
2838 		entry->compressed_filesize = ((uint32_t) php_stream_tell(entry->cfp)) - entry->header_offset;
2839 		/* generate crc on compressed file */
2840 		entry->old_flags = entry->flags;
2841 		entry->is_modified = 1;
2842 		global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
2843 	} ZEND_HASH_FOREACH_END();
2844 	global_flags |= PHAR_HDR_SIGNATURE;
2845 
2846 	/* write out manifest pre-header */
2847 	/*  4: manifest length
2848 	 *  4: manifest entry count
2849 	 *  2: phar version
2850 	 *  4: phar global flags
2851 	 *  4: alias length
2852 	 *  ?: the alias itself
2853 	 *  4: phar metadata length
2854 	 *  ?: phar metadata
2855 	 */
2856 	restore_alias_len = phar->alias_len;
2857 	if (phar->is_temporary_alias) {
2858 		phar->alias_len = 0;
2859 	}
2860 
2861 	manifest_len = offset + phar->alias_len + sizeof(manifest) + (main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2862 	phar_set_32(manifest, manifest_len);
2863 	/* Hack - see bug #65028, add padding byte to the end of the manifest */
2864 	if(manifest[0] == '\r' || manifest[0] == '\n') {
2865 		manifest_len++;
2866 		phar_set_32(manifest, manifest_len);
2867 		manifest_hack = 1;
2868 	}
2869 	phar_set_32(manifest+4, new_manifest_count);
2870 	if (has_dirs) {
2871 		*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
2872 		*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
2873 	} else {
2874 		*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
2875 		*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
2876 	}
2877 	phar_set_32(manifest+10, global_flags);
2878 	phar_set_32(manifest+14, phar->alias_len);
2879 
2880 	/* write the manifest header */
2881 	if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
2882 	|| (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
2883 
2884 		if (closeoldfile) {
2885 			php_stream_close(oldfile);
2886 		}
2887 
2888 		php_stream_close(newfile);
2889 		phar->alias_len = restore_alias_len;
2890 
2891 		if (error) {
2892 			spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
2893 		}
2894 
2895 		goto cleanup;
2896 	}
2897 
2898 	phar->alias_len = restore_alias_len;
2899 
2900 	phar_set_32(manifest, main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2901 	if (4 != php_stream_write(newfile, manifest, 4) || ((main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0)
2902 	&& ZSTR_LEN(main_metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(main_metadata_str.s), ZSTR_LEN(main_metadata_str.s)))) {
2903 		smart_str_free(&main_metadata_str);
2904 
2905 		if (closeoldfile) {
2906 			php_stream_close(oldfile);
2907 		}
2908 
2909 		php_stream_close(newfile);
2910 		phar->alias_len = restore_alias_len;
2911 
2912 		if (error) {
2913 			spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
2914 		}
2915 
2916 		goto cleanup;
2917 	}
2918 	smart_str_free(&main_metadata_str);
2919 
2920 	/* re-calculate the manifest location to simplify later code */
2921 	manifest_ftell = php_stream_tell(newfile);
2922 
2923 	/* now write the manifest */
2924 	ZEND_HASH_FOREACH_PTR(&phar->manifest, entry) {
2925 		if (entry->is_deleted || entry->is_mounted) {
2926 			/* remove this from the new phar if deleted, ignore if mounted */
2927 			continue;
2928 		}
2929 
2930 		if (entry->is_dir) {
2931 			/* add 1 for trailing slash */
2932 			phar_set_32(entry_buffer, entry->filename_len + 1);
2933 		} else {
2934 			phar_set_32(entry_buffer, entry->filename_len);
2935 		}
2936 
2937 		if (4 != php_stream_write(newfile, entry_buffer, 4)
2938 		|| entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
2939 		|| (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
2940 			if (closeoldfile) {
2941 				php_stream_close(oldfile);
2942 			}
2943 			php_stream_close(newfile);
2944 			if (error) {
2945 				if (entry->is_dir) {
2946 					spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2947 				} else {
2948 					spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2949 				}
2950 			}
2951 			goto cleanup;
2952 		}
2953 
2954 		/* set the manifest meta-data:
2955 			4: uncompressed filesize
2956 			4: creation timestamp
2957 			4: compressed filesize
2958 			4: crc32
2959 			4: flags
2960 			4: metadata-len
2961 			+: metadata
2962 		*/
2963 		mytime = time(NULL);
2964 		phar_set_32(entry_buffer, entry->uncompressed_filesize);
2965 		phar_set_32(entry_buffer+4, mytime);
2966 		phar_set_32(entry_buffer+8, entry->compressed_filesize);
2967 		phar_set_32(entry_buffer+12, entry->crc32);
2968 		phar_set_32(entry_buffer+16, entry->flags);
2969 		phar_set_32(entry_buffer+20, entry->metadata_str.s ? ZSTR_LEN(entry->metadata_str.s) : 0);
2970 
2971 		if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
2972 		|| (entry->metadata_str.s &&
2973 		    ZSTR_LEN(entry->metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(entry->metadata_str.s), ZSTR_LEN(entry->metadata_str.s)))) {
2974 			if (closeoldfile) {
2975 				php_stream_close(oldfile);
2976 			}
2977 
2978 			php_stream_close(newfile);
2979 
2980 			if (error) {
2981 				spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2982 			}
2983 
2984 			goto cleanup;
2985 		}
2986 	} ZEND_HASH_FOREACH_END();
2987 	/* Hack - see bug #65028, add padding byte to the end of the manifest */
2988 	if(manifest_hack) {
2989 		if(1 != php_stream_write(newfile, manifest, 1)) {
2990 			if (closeoldfile) {
2991 				php_stream_close(oldfile);
2992 			}
2993 
2994 			php_stream_close(newfile);
2995 
2996 			if (error) {
2997 				spprintf(error, 0, "unable to write manifest padding byte");
2998 			}
2999 
3000 			goto cleanup;
3001 		}
3002 	}
3003 
3004 	/* now copy the actual file data to the new phar */
3005 	offset = php_stream_tell(newfile);
3006 	ZEND_HASH_FOREACH_PTR(&phar->manifest, entry) {
3007 		if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
3008 			continue;
3009 		}
3010 
3011 		if (entry->cfp) {
3012 			file = entry->cfp;
3013 			php_stream_seek(file, entry->header_offset, SEEK_SET);
3014 		} else {
3015 			file = phar_get_efp(entry, 0);
3016 			if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
3017 				if (closeoldfile) {
3018 					php_stream_close(oldfile);
3019 				}
3020 				php_stream_close(newfile);
3021 				if (error) {
3022 					spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3023 				}
3024 				goto cleanup;
3025 			}
3026 		}
3027 
3028 		if (!file) {
3029 			if (closeoldfile) {
3030 				php_stream_close(oldfile);
3031 			}
3032 			php_stream_close(newfile);
3033 			if (error) {
3034 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3035 			}
3036 			goto cleanup;
3037 		}
3038 
3039 		/* this will have changed for all files that have either changed compression or been modified */
3040 		entry->offset = entry->offset_abs = offset;
3041 		offset += entry->compressed_filesize;
3042 		if (php_stream_copy_to_stream_ex(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
3043 			if (closeoldfile) {
3044 				php_stream_close(oldfile);
3045 			}
3046 
3047 			php_stream_close(newfile);
3048 
3049 			if (error) {
3050 				spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
3051 			}
3052 
3053 			goto cleanup;
3054 		}
3055 
3056 		entry->is_modified = 0;
3057 
3058 		if (entry->cfp) {
3059 			entry->cfp = NULL;
3060 			entry->header_offset = 0;
3061 		}
3062 
3063 		if (entry->fp_type == PHAR_MOD) {
3064 			/* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed when the phar_entry_data is phar_entry_delref'ed */
3065 			if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
3066 				php_stream_close(entry->fp);
3067 			}
3068 
3069 			entry->fp = NULL;
3070 			entry->fp_type = PHAR_FP;
3071 		} else if (entry->fp_type == PHAR_UFP) {
3072 			entry->fp_type = PHAR_FP;
3073 		}
3074 	} ZEND_HASH_FOREACH_END();
3075 
3076 	if (shared_cfp != NULL) {
3077 		php_stream_close(shared_cfp);
3078 		shared_cfp = NULL;
3079 	}
3080 
3081 	/* append signature */
3082 	if (global_flags & PHAR_HDR_SIGNATURE) {
3083 		char sig_buf[4];
3084 
3085 		php_stream_rewind(newfile);
3086 
3087 		if (phar->signature) {
3088 			efree(phar->signature);
3089 			phar->signature = NULL;
3090 		}
3091 
3092 		switch(phar->sig_flags) {
3093 			default: {
3094 				char *digest = NULL;
3095 				size_t digest_len;
3096 
3097 				if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error)) {
3098 					if (error) {
3099 						char *save = *error;
3100 						spprintf(error, 0, "phar error: unable to write signature: %s", save);
3101 						efree(save);
3102 					}
3103 					if (digest) {
3104 						efree(digest);
3105 					}
3106 					if (closeoldfile) {
3107 						php_stream_close(oldfile);
3108 					}
3109 					php_stream_close(newfile);
3110 					return EOF;
3111 				}
3112 
3113 				php_stream_write(newfile, digest, digest_len);
3114 				efree(digest);
3115 				if (phar->sig_flags == PHAR_SIG_OPENSSL) {
3116 					phar_set_32(sig_buf, digest_len);
3117 					php_stream_write(newfile, sig_buf, 4);
3118 				}
3119 				break;
3120 			}
3121 		}
3122 		phar_set_32(sig_buf, phar->sig_flags);
3123 		php_stream_write(newfile, sig_buf, 4);
3124 		php_stream_write(newfile, "GBMB", 4);
3125 	}
3126 
3127 	/* finally, close the temp file, rename the original phar,
3128 	   move the temp to the old phar, unlink the old phar, and reload it into memory
3129 	*/
3130 	if (phar->fp && free_fp) {
3131 		php_stream_close(phar->fp);
3132 	}
3133 
3134 	if (phar->ufp) {
3135 		if (free_ufp) {
3136 			php_stream_close(phar->ufp);
3137 		}
3138 		phar->ufp = NULL;
3139 	}
3140 
3141 	if (closeoldfile) {
3142 		php_stream_close(oldfile);
3143 	}
3144 
3145 	phar->internal_file_start = halt_offset + manifest_len + 4;
3146 	phar->halt_offset = halt_offset;
3147 	phar->is_brandnew = 0;
3148 
3149 	php_stream_rewind(newfile);
3150 
3151 	if (phar->donotflush) {
3152 		/* deferred flush */
3153 		phar->fp = newfile;
3154 	} else {
3155 		phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
3156 		if (!phar->fp) {
3157 			phar->fp = newfile;
3158 			if (error) {
3159 				spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
3160 			}
3161 			return EOF;
3162 		}
3163 
3164 		if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
3165 			/* to properly compress, we have to tell zlib to add a zlib header */
3166 			zval filterparams;
3167 
3168 			array_init(&filterparams);
3169 			add_assoc_long(&filterparams, "window", MAX_WBITS+16);
3170 			filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp));
3171 			zend_array_destroy(Z_ARR(filterparams));
3172 
3173 			if (!filter) {
3174 				if (error) {
3175 					spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname);
3176 				}
3177 				return EOF;
3178 			}
3179 
3180 			php_stream_filter_append(&phar->fp->writefilters, filter);
3181 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3182 			php_stream_filter_flush(filter, 1);
3183 			php_stream_filter_remove(filter, 1);
3184 			php_stream_close(phar->fp);
3185 			/* use the temp stream as our base */
3186 			phar->fp = newfile;
3187 		} else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
3188 			filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp));
3189 			php_stream_filter_append(&phar->fp->writefilters, filter);
3190 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3191 			php_stream_filter_flush(filter, 1);
3192 			php_stream_filter_remove(filter, 1);
3193 			php_stream_close(phar->fp);
3194 			/* use the temp stream as our base */
3195 			phar->fp = newfile;
3196 		} else {
3197 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3198 			/* we could also reopen the file in "rb" mode but there is no need for that */
3199 			php_stream_close(newfile);
3200 		}
3201 	}
3202 
3203 	if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
3204 		if (error) {
3205 			spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
3206 		}
3207 		return EOF;
3208 	}
3209 
3210 	return EOF;
3211 
3212 cleanup:
3213 	if (shared_cfp != NULL) {
3214 		php_stream_close(shared_cfp);
3215 	}
3216 	ZEND_HASH_FOREACH_PTR(&phar->manifest, entry) {
3217 		if (entry->cfp) {
3218 			entry->cfp = NULL;
3219 			entry->header_offset = 0;
3220 		}
3221 	} ZEND_HASH_FOREACH_END();
3222 
3223 	return EOF;
3224 }
3225 /* }}} */
3226 
3227 #ifdef COMPILE_DL_PHAR
3228 #ifdef ZTS
3229 ZEND_TSRMLS_CACHE_DEFINE()
3230 #endif
3231 ZEND_GET_MODULE(phar)
3232 #endif
3233 
3234 /* {{{ phar_functions[]
3235  *
3236  * Every user visible function must have an entry in phar_functions[].
3237  */
3238 static const zend_function_entry phar_functions[] = {
3239 	PHP_FE_END
3240 };
3241 /* }}}*/
3242 
phar_zend_stream_reader(void * handle,char * buf,size_t len)3243 static ssize_t phar_zend_stream_reader(void *handle, char *buf, size_t len) /* {{{ */
3244 {
3245 	return php_stream_read(phar_get_pharfp((phar_archive_data*)handle), buf, len);
3246 }
3247 /* }}} */
3248 
phar_zend_stream_fsizer(void * handle)3249 static size_t phar_zend_stream_fsizer(void *handle) /* {{{ */
3250 {
3251 	return ((phar_archive_data*)handle)->halt_offset + 32;
3252 } /* }}} */
3253 
3254 zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type);
3255 #define phar_orig_zend_open zend_stream_open_function
3256 
phar_resolve_path(const char * filename,size_t filename_len)3257 static zend_string *phar_resolve_path(const char *filename, size_t filename_len)
3258 {
3259 	return phar_find_in_include_path((char *) filename, filename_len, NULL);
3260 }
3261 
phar_compile_file(zend_file_handle * file_handle,int type)3262 static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type) /* {{{ */
3263 {
3264 	zend_op_array *res;
3265 	char *name = NULL;
3266 	int failed;
3267 	phar_archive_data *phar;
3268 
3269 	if (!file_handle || !file_handle->filename) {
3270 		return phar_orig_compile_file(file_handle, type);
3271 	}
3272 	if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {
3273 		if (SUCCESS == phar_open_from_filename((char*)file_handle->filename, strlen(file_handle->filename), NULL, 0, 0, &phar, NULL)) {
3274 			if (phar->is_zip || phar->is_tar) {
3275 				zend_file_handle f = *file_handle;
3276 
3277 				/* zip or tar-based phar */
3278 				spprintf(&name, 4096, "phar://%s/%s", file_handle->filename, ".phar/stub.php");
3279 				if (SUCCESS == phar_orig_zend_open((const char *)name, &f)) {
3280 
3281 					efree(name);
3282 					name = NULL;
3283 
3284 					f.filename = file_handle->filename;
3285 					if (f.opened_path) {
3286 						efree(f.opened_path);
3287 					}
3288 					f.opened_path = file_handle->opened_path;
3289 					f.free_filename = file_handle->free_filename;
3290 
3291 					switch (file_handle->type) {
3292 						case ZEND_HANDLE_STREAM:
3293 							if (file_handle->handle.stream.closer && file_handle->handle.stream.handle) {
3294 								file_handle->handle.stream.closer(file_handle->handle.stream.handle);
3295 							}
3296 							file_handle->handle.stream.handle = NULL;
3297 							break;
3298 						default:
3299 							break;
3300 					}
3301 					*file_handle = f;
3302 				}
3303 			} else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
3304 				zend_file_handle_dtor(file_handle);
3305 				/* compressed phar */
3306 				file_handle->type = ZEND_HANDLE_STREAM;
3307 				/* we do our own reading directly from the phar, don't change the next line */
3308 				file_handle->handle.stream.handle  = phar;
3309 				file_handle->handle.stream.reader  = phar_zend_stream_reader;
3310 				file_handle->handle.stream.closer  = NULL;
3311 				file_handle->handle.stream.fsizer  = phar_zend_stream_fsizer;
3312 				file_handle->handle.stream.isatty  = 0;
3313 				phar->is_persistent ?
3314 					php_stream_rewind(PHAR_G(cached_fp)[phar->phar_pos].fp) :
3315 					php_stream_rewind(phar->fp);
3316 			}
3317 		}
3318 	}
3319 
3320 	zend_try {
3321 		failed = 0;
3322 		CG(zend_lineno) = 0;
3323 		res = phar_orig_compile_file(file_handle, type);
3324 	} zend_catch {
3325 		failed = 1;
3326 		res = NULL;
3327 	} zend_end_try();
3328 
3329 	if (name) {
3330 		efree(name);
3331 	}
3332 
3333 	if (failed) {
3334 		zend_bailout();
3335 	}
3336 
3337 	return res;
3338 }
3339 /* }}} */
3340 
3341 typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int);
3342 typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
3343 
mime_type_dtor(zval * zv)3344 static void mime_type_dtor(zval *zv)
3345 {
3346 	free(Z_PTR_P(zv));
3347 }
3348 
PHP_GINIT_FUNCTION(phar)3349 PHP_GINIT_FUNCTION(phar) /* {{{ */
3350 {
3351 #if defined(COMPILE_DL_PHAR) && defined(ZTS)
3352 	ZEND_TSRMLS_CACHE_UPDATE();
3353 #endif
3354 	phar_mime_type mime;
3355 
3356 	memset(phar_globals, 0, sizeof(zend_phar_globals));
3357 	HT_INVALIDATE(&phar_globals->phar_persist_map);
3358 	HT_INVALIDATE(&phar_globals->phar_fname_map);
3359 	HT_INVALIDATE(&phar_globals->phar_alias_map);
3360 	phar_globals->readonly = 1;
3361 
3362 	zend_hash_init(&phar_globals->mime_types, 0, NULL, mime_type_dtor, 1);
3363 
3364 #define PHAR_SET_MIME(mimetype, ret, fileext) \
3365 		mime.mime = mimetype; \
3366 		mime.len = sizeof((mimetype))+1; \
3367 		mime.type = ret; \
3368 		zend_hash_str_add_mem(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type)); \
3369 
3370 	PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
3371 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
3372 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
3373 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
3374 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
3375 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
3376 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
3377 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
3378 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
3379 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
3380 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
3381 	PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
3382 	PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
3383 	PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
3384 	PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
3385 	PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
3386 	PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
3387 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
3388 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
3389 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
3390 	PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
3391 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
3392 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
3393 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
3394 	PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
3395 	PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
3396 	PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
3397 	PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
3398 	PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
3399 	PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
3400 	PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
3401 	PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
3402 	PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
3403 	PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
3404 	PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
3405 	PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
3406 	PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
3407 	PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
3408 	PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
3409 	PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
3410 
3411 	phar_restore_orig_functions();
3412 }
3413 /* }}} */
3414 
PHP_GSHUTDOWN_FUNCTION(phar)3415 PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
3416 {
3417 	zend_hash_destroy(&phar_globals->mime_types);
3418 }
3419 /* }}} */
3420 
PHP_MINIT_FUNCTION(phar)3421 PHP_MINIT_FUNCTION(phar) /* {{{ */
3422 {
3423 	REGISTER_INI_ENTRIES();
3424 
3425 	phar_orig_compile_file = zend_compile_file;
3426 	zend_compile_file = phar_compile_file;
3427 
3428 	phar_save_resolve_path = zend_resolve_path;
3429 	zend_resolve_path = phar_resolve_path;
3430 
3431 	phar_object_init();
3432 
3433 	phar_intercept_functions_init();
3434 	phar_save_orig_functions();
3435 
3436 	return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper);
3437 }
3438 /* }}} */
3439 
PHP_MSHUTDOWN_FUNCTION(phar)3440 PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
3441 {
3442 	php_unregister_url_stream_wrapper("phar");
3443 
3444 	phar_intercept_functions_shutdown();
3445 
3446 	if (zend_compile_file == phar_compile_file) {
3447 		zend_compile_file = phar_orig_compile_file;
3448 	}
3449 
3450 	if (PHAR_G(manifest_cached)) {
3451 		zend_hash_destroy(&(cached_phars));
3452 		zend_hash_destroy(&(cached_alias));
3453 	}
3454 
3455 	UNREGISTER_INI_ENTRIES();
3456 	return SUCCESS;
3457 }
3458 /* }}} */
3459 
phar_request_initialize(void)3460 void phar_request_initialize(void) /* {{{ */
3461 {
3462 	if (!PHAR_G(request_init))
3463 	{
3464 		PHAR_G(last_phar) = NULL;
3465 		PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
3466 		PHAR_G(has_bz2) = zend_hash_str_exists(&module_registry, "bz2", sizeof("bz2")-1);
3467 		PHAR_G(has_zlib) = zend_hash_str_exists(&module_registry, "zlib", sizeof("zlib")-1);
3468 		PHAR_G(request_init) = 1;
3469 		PHAR_G(request_ends) = 0;
3470 		PHAR_G(request_done) = 0;
3471 		zend_hash_init(&(PHAR_G(phar_fname_map)), 5, zend_get_hash_value, destroy_phar_data,  0);
3472 		zend_hash_init(&(PHAR_G(phar_persist_map)), 5, zend_get_hash_value, NULL,  0);
3473 		zend_hash_init(&(PHAR_G(phar_alias_map)), 5, zend_get_hash_value, NULL, 0);
3474 
3475 		if (PHAR_G(manifest_cached)) {
3476 			phar_archive_data *pphar;
3477 			phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
3478 
3479 			ZEND_HASH_FOREACH_PTR(&cached_phars, pphar) {
3480 				stuff[pphar->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar->manifest)), sizeof(phar_entry_fp_info));
3481 			} ZEND_HASH_FOREACH_END();
3482 
3483 			PHAR_G(cached_fp) = stuff;
3484 		}
3485 
3486 		PHAR_G(phar_SERVER_mung_list) = 0;
3487 		PHAR_G(cwd) = NULL;
3488 		PHAR_G(cwd_len) = 0;
3489 		PHAR_G(cwd_init) = 0;
3490 	}
3491 }
3492 /* }}} */
3493 
PHP_RSHUTDOWN_FUNCTION(phar)3494 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
3495 {
3496 	uint32_t i;
3497 
3498 	PHAR_G(request_ends) = 1;
3499 
3500 	if (PHAR_G(request_init))
3501 	{
3502 		phar_release_functions();
3503 		zend_hash_destroy(&(PHAR_G(phar_alias_map)));
3504 		HT_INVALIDATE(&PHAR_G(phar_alias_map));
3505 		zend_hash_destroy(&(PHAR_G(phar_fname_map)));
3506 		HT_INVALIDATE(&PHAR_G(phar_fname_map));
3507 		zend_hash_destroy(&(PHAR_G(phar_persist_map)));
3508 		HT_INVALIDATE(&PHAR_G(phar_persist_map));
3509 		PHAR_G(phar_SERVER_mung_list) = 0;
3510 
3511 		if (PHAR_G(cached_fp)) {
3512 			for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
3513 				if (PHAR_G(cached_fp)[i].fp) {
3514 					php_stream_close(PHAR_G(cached_fp)[i].fp);
3515 				}
3516 				if (PHAR_G(cached_fp)[i].ufp) {
3517 					php_stream_close(PHAR_G(cached_fp)[i].ufp);
3518 				}
3519 				efree(PHAR_G(cached_fp)[i].manifest);
3520 			}
3521 			efree(PHAR_G(cached_fp));
3522 			PHAR_G(cached_fp) = 0;
3523 		}
3524 
3525 		PHAR_G(request_init) = 0;
3526 
3527 		if (PHAR_G(cwd)) {
3528 			efree(PHAR_G(cwd));
3529 		}
3530 
3531 		PHAR_G(cwd) = NULL;
3532 		PHAR_G(cwd_len) = 0;
3533 		PHAR_G(cwd_init) = 0;
3534 	}
3535 
3536 	PHAR_G(request_done) = 1;
3537 	return SUCCESS;
3538 }
3539 /* }}} */
3540 
PHP_MINFO_FUNCTION(phar)3541 PHP_MINFO_FUNCTION(phar) /* {{{ */
3542 {
3543 	phar_request_initialize();
3544 	php_info_print_table_start();
3545 	php_info_print_table_header(2, "Phar: PHP Archive support", "enabled");
3546 	php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
3547 	php_info_print_table_row(2, "Phar-based phar archives", "enabled");
3548 	php_info_print_table_row(2, "Tar-based phar archives", "enabled");
3549 	php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
3550 
3551 	if (PHAR_G(has_zlib)) {
3552 		php_info_print_table_row(2, "gzip compression", "enabled");
3553 	} else {
3554 		php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
3555 	}
3556 
3557 	if (PHAR_G(has_bz2)) {
3558 		php_info_print_table_row(2, "bzip2 compression", "enabled");
3559 	} else {
3560 		php_info_print_table_row(2, "bzip2 compression", "disabled (install ext/bz2)");
3561 	}
3562 #ifdef PHAR_HAVE_OPENSSL
3563 	php_info_print_table_row(2, "Native OpenSSL support", "enabled");
3564 #else
3565 	if (zend_hash_str_exists(&module_registry, "openssl", sizeof("openssl")-1)) {
3566 		php_info_print_table_row(2, "OpenSSL support", "enabled");
3567 	} else {
3568 		php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
3569 	}
3570 #endif
3571 	php_info_print_table_end();
3572 
3573 	php_info_print_box_start(0);
3574 	PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
3575 	PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3576 	PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
3577 	PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3578 	PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
3579 	php_info_print_box_end();
3580 
3581 	DISPLAY_INI_ENTRIES();
3582 }
3583 /* }}} */
3584 
3585 /* {{{ phar_module_entry
3586  */
3587 static const zend_module_dep phar_deps[] = {
3588 	ZEND_MOD_OPTIONAL("apc")
3589 	ZEND_MOD_OPTIONAL("bz2")
3590 	ZEND_MOD_OPTIONAL("openssl")
3591 	ZEND_MOD_OPTIONAL("zlib")
3592 	ZEND_MOD_OPTIONAL("standard")
3593 	ZEND_MOD_REQUIRED("hash")
3594 	ZEND_MOD_REQUIRED("spl")
3595 	ZEND_MOD_END
3596 };
3597 
3598 zend_module_entry phar_module_entry = {
3599 	STANDARD_MODULE_HEADER_EX, NULL,
3600 	phar_deps,
3601 	"Phar",
3602 	phar_functions,
3603 	PHP_MINIT(phar),
3604 	PHP_MSHUTDOWN(phar),
3605 	NULL,
3606 	PHP_RSHUTDOWN(phar),
3607 	PHP_MINFO(phar),
3608 	PHP_PHAR_VERSION,
3609 	PHP_MODULE_GLOBALS(phar),   /* globals descriptor */
3610 	PHP_GINIT(phar),            /* globals ctor */
3611 	PHP_GSHUTDOWN(phar),        /* globals dtor */
3612 	NULL,                       /* post deactivate */
3613 	STANDARD_MODULE_PROPERTIES_EX
3614 };
3615 /* }}} */
3616