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