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