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 static const char token[] = "__HALT_COMPILER();";
1629 static const char zip_magic[] = "PK\x03\x04";
1630 static const char gz_magic[] = "\x1f\x8b\x08";
1631 static 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 <= 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 if (error) {
2325 *error = NULL;
2326 }
2327
2328 zend_string *fname = zend_get_executed_filename_ex();
2329
2330 if (!fname) {
2331 if (error) {
2332 spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
2333 }
2334 return FAILURE;
2335 }
2336
2337 if (phar_open_parsed_phar(ZSTR_VAL(fname), ZSTR_LEN(fname), alias, alias_len, 0, REPORT_ERRORS, NULL, 0) == SUCCESS) {
2338 return SUCCESS;
2339 }
2340
2341 if (0 == zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) {
2342 if (error) {
2343 spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
2344 }
2345 return FAILURE;
2346 }
2347
2348 if (php_check_open_basedir(ZSTR_VAL(fname))) {
2349 return FAILURE;
2350 }
2351
2352 zend_string *actual = NULL;
2353 php_stream *fp;
2354 fp = php_stream_open_wrapper(ZSTR_VAL(fname), "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
2355
2356 if (!fp) {
2357 if (error) {
2358 spprintf(error, 0, "unable to open phar for reading \"%s\"", ZSTR_VAL(fname));
2359 }
2360 if (actual) {
2361 zend_string_release_ex(actual, 0);
2362 }
2363 return FAILURE;
2364 }
2365
2366 if (actual) {
2367 fname = actual;
2368 }
2369
2370 int ret = phar_open_from_fp(fp, ZSTR_VAL(fname), ZSTR_LEN(fname), alias, alias_len, REPORT_ERRORS, NULL, 0, error);
2371
2372 if (actual) {
2373 zend_string_release_ex(actual, 0);
2374 }
2375
2376 return ret;
2377 }
2378 /* }}} */
2379
2380 /**
2381 * Validate the CRC32 of a file opened from within the phar
2382 */
phar_postprocess_file(phar_entry_data * idata,uint32_t crc32,char ** error,int process_zip)2383 int phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip) /* {{{ */
2384 {
2385 uint32_t crc = php_crc32_bulk_init();
2386 int len = idata->internal_file->uncompressed_filesize, ret;
2387 php_stream *fp = idata->fp;
2388 phar_entry_info *entry = idata->internal_file;
2389
2390 if (error) {
2391 *error = NULL;
2392 }
2393
2394 if (entry->is_zip && process_zip > 0) {
2395 /* verify local file header */
2396 phar_zip_file_header local;
2397 phar_zip_data_desc desc;
2398
2399 if (SUCCESS != phar_open_archive_fp(idata->phar)) {
2400 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);
2401 return FAILURE;
2402 }
2403 php_stream_seek(phar_get_entrypfp(idata->internal_file), entry->header_offset, SEEK_SET);
2404
2405 if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file), (char *) &local, sizeof(local))) {
2406
2407 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);
2408 return FAILURE;
2409 }
2410
2411 /* check for data descriptor */
2412 if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
2413 php_stream_seek(phar_get_entrypfp(idata->internal_file),
2414 entry->header_offset + sizeof(local) +
2415 PHAR_ZIP_16(local.filename_len) +
2416 PHAR_ZIP_16(local.extra_len) +
2417 entry->compressed_filesize, SEEK_SET);
2418 if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file),
2419 (char *) &desc, sizeof(desc))) {
2420 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);
2421 return FAILURE;
2422 }
2423 if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
2424 memcpy(&(local.crc32), &(desc.crc32), 12);
2425 } else {
2426 /* old data descriptors have no signature */
2427 memcpy(&(local.crc32), &desc, 12);
2428 }
2429 }
2430 /* verify local header */
2431 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)) {
2432 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);
2433 return FAILURE;
2434 }
2435
2436 /* construct actual offset to file start - local extra_len can be different from central extra_len */
2437 entry->offset = entry->offset_abs =
2438 sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
2439
2440 if (idata->zero && idata->zero != entry->offset_abs) {
2441 idata->zero = entry->offset_abs;
2442 }
2443 }
2444
2445 if (process_zip == 1) {
2446 return SUCCESS;
2447 }
2448
2449 php_stream_seek(fp, idata->zero, SEEK_SET);
2450
2451 ret = php_crc32_stream_bulk_update(&crc, fp, len);
2452
2453 php_stream_seek(fp, idata->zero, SEEK_SET);
2454
2455 if (SUCCESS == ret && php_crc32_bulk_end(crc) == crc32) {
2456 entry->is_crc_checked = 1;
2457 return SUCCESS;
2458 } else {
2459 spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
2460 return FAILURE;
2461 }
2462 }
2463 /* }}} */
2464
phar_set_32(char * buffer,uint32_t var)2465 static inline void phar_set_32(char *buffer, uint32_t var) /* {{{ */
2466 {
2467 #ifdef WORDS_BIGENDIAN
2468 *((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
2469 *((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
2470 *((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
2471 *((buffer) + 0) = (unsigned char) ((var) & 0xFF);
2472 #else
2473 memcpy(buffer, &var, sizeof(var));
2474 #endif
2475 } /* }}} */
2476
phar_flush_clean_deleted_apply(zval * zv)2477 static int phar_flush_clean_deleted_apply(zval *zv) /* {{{ */
2478 {
2479 phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
2480
2481 if (entry->fp_refcount <= 0 && entry->is_deleted) {
2482 return ZEND_HASH_APPLY_REMOVE;
2483 } else {
2484 return ZEND_HASH_APPLY_KEEP;
2485 }
2486 }
2487 /* }}} */
2488
2489 #include "stub.h"
2490
phar_create_default_stub(const char * index_php,const char * web_index,char ** error)2491 zend_string *phar_create_default_stub(const char *index_php, const char *web_index, char **error) /* {{{ */
2492 {
2493 size_t index_len, web_len;
2494
2495 if (error) {
2496 *error = NULL;
2497 }
2498
2499 if (!index_php) {
2500 index_php = "index.php";
2501 }
2502
2503 if (!web_index) {
2504 web_index = "index.php";
2505 }
2506
2507 index_len = strlen(index_php);
2508 web_len = strlen(web_index);
2509
2510 if (index_len > 400) {
2511 /* ridiculous size not allowed for index.php startup filename */
2512 if (error) {
2513 spprintf(error, 0, "Illegal filename passed in for stub creation, was %zd characters long, and only 400 or less is allowed", index_len);
2514 return NULL;
2515 }
2516 }
2517
2518 if (web_len > 400) {
2519 /* ridiculous size not allowed for index.php startup filename */
2520 if (error) {
2521 spprintf(error, 0, "Illegal web filename passed in for stub creation, was %zd characters long, and only 400 or less is allowed", web_len);
2522 return NULL;
2523 }
2524 }
2525
2526 return phar_get_stub(index_php, web_index, index_len+1, web_len+1);
2527 }
2528 /* }}} */
2529
2530 /**
2531 * Save phar contents to disk
2532 *
2533 * user_stub contains either a string, or a resource pointer, if len is a negative length.
2534 * user_stub and len should be both 0 if the default or existing stub should be used
2535 */
phar_flush(phar_archive_data * phar,char * user_stub,zend_long len,int convert,char ** error)2536 int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int convert, char **error) /* {{{ */
2537 {
2538 char halt_stub[] = "__HALT_COMPILER();";
2539 zend_string *newstub;
2540 phar_entry_info *entry, *newentry;
2541 size_t halt_offset;
2542 int restore_alias_len, global_flags = 0, closeoldfile;
2543 char *pos, has_dirs = 0;
2544 char manifest[18], entry_buffer[24];
2545 zend_off_t manifest_ftell;
2546 zend_long offset;
2547 size_t wrote;
2548 uint32_t manifest_len, mytime, new_manifest_count;
2549 uint32_t newcrc32;
2550 php_stream *file, *oldfile, *newfile, *stubfile;
2551 php_stream_filter *filter;
2552 php_serialize_data_t metadata_hash;
2553 smart_str main_metadata_str = {0};
2554 int free_user_stub, free_fp = 1, free_ufp = 1;
2555 int manifest_hack = 0;
2556 php_stream *shared_cfp = NULL;
2557
2558 if (phar->is_persistent) {
2559 if (error) {
2560 spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
2561 }
2562 return EOF;
2563 }
2564
2565 if (error) {
2566 *error = NULL;
2567 }
2568
2569 if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
2570 return EOF;
2571 }
2572
2573 zend_hash_clean(&phar->virtual_dirs);
2574
2575 if (phar->is_zip) {
2576 return phar_zip_flush(phar, user_stub, len, convert, error);
2577 }
2578
2579 if (phar->is_tar) {
2580 return phar_tar_flush(phar, user_stub, len, convert, error);
2581 }
2582
2583 if (PHAR_G(readonly)) {
2584 return EOF;
2585 }
2586
2587 if (phar->fp && !phar->is_brandnew) {
2588 oldfile = phar->fp;
2589 closeoldfile = 0;
2590 php_stream_rewind(oldfile);
2591 } else {
2592 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
2593 closeoldfile = oldfile != NULL;
2594 }
2595 newfile = php_stream_fopen_tmpfile();
2596 if (!newfile) {
2597 if (error) {
2598 spprintf(error, 0, "unable to create temporary file");
2599 }
2600 if (closeoldfile) {
2601 php_stream_close(oldfile);
2602 }
2603 return EOF;
2604 }
2605
2606 if (user_stub) {
2607 zend_string *suser_stub;
2608 if (len < 0) {
2609 /* resource passed in */
2610 if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
2611 if (closeoldfile) {
2612 php_stream_close(oldfile);
2613 }
2614 php_stream_close(newfile);
2615 if (error) {
2616 spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
2617 }
2618 return EOF;
2619 }
2620 if (len == -1) {
2621 len = PHP_STREAM_COPY_ALL;
2622 } else {
2623 len = -len;
2624 }
2625 user_stub = 0;
2626
2627 if (!(suser_stub = php_stream_copy_to_mem(stubfile, len, 0))) {
2628 if (closeoldfile) {
2629 php_stream_close(oldfile);
2630 }
2631 php_stream_close(newfile);
2632 if (error) {
2633 spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
2634 }
2635 return EOF;
2636 }
2637 free_user_stub = 1;
2638 user_stub = ZSTR_VAL(suser_stub);
2639 len = ZSTR_LEN(suser_stub);
2640 } else {
2641 free_user_stub = 0;
2642 }
2643 if ((pos = php_stristr(user_stub, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
2644 if (closeoldfile) {
2645 php_stream_close(oldfile);
2646 }
2647 php_stream_close(newfile);
2648 if (error) {
2649 spprintf(error, 0, "illegal stub for phar \"%s\" (__HALT_COMPILER(); is missing)", phar->fname);
2650 }
2651 if (free_user_stub) {
2652 zend_string_free(suser_stub);
2653 }
2654 return EOF;
2655 }
2656 len = pos - user_stub + 18;
2657 if ((size_t)len != php_stream_write(newfile, user_stub, len)
2658 || 5 != php_stream_write(newfile, " ?>\r\n", 5)) {
2659 if (closeoldfile) {
2660 php_stream_close(oldfile);
2661 }
2662 php_stream_close(newfile);
2663 if (error) {
2664 spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
2665 }
2666 if (free_user_stub) {
2667 zend_string_free(suser_stub);
2668 }
2669 return EOF;
2670 }
2671 phar->halt_offset = len + 5;
2672 if (free_user_stub) {
2673 zend_string_free(suser_stub);
2674 }
2675 } else {
2676 size_t written;
2677
2678 if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
2679 php_stream_copy_to_stream_ex(oldfile, newfile, phar->halt_offset, &written);
2680 newstub = NULL;
2681 } else {
2682 /* this is either a brand new phar or a default stub overwrite */
2683 newstub = phar_create_default_stub(NULL, NULL, NULL);
2684 phar->halt_offset = ZSTR_LEN(newstub);
2685 written = php_stream_write(newfile, ZSTR_VAL(newstub), phar->halt_offset);
2686 }
2687 if (phar->halt_offset != written) {
2688 if (closeoldfile) {
2689 php_stream_close(oldfile);
2690 }
2691 php_stream_close(newfile);
2692 if (error) {
2693 if (newstub) {
2694 spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
2695 } else {
2696 spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
2697 }
2698 }
2699 if (newstub) {
2700 zend_string_free(newstub);
2701 }
2702 return EOF;
2703 }
2704 if (newstub) {
2705 zend_string_free(newstub);
2706 }
2707 }
2708 manifest_ftell = php_stream_tell(newfile);
2709 halt_offset = manifest_ftell;
2710
2711 /* Check whether we can get rid of some of the deleted entries which are
2712 * unused. However some might still be in use so even after this clean-up
2713 * we need to skip entries marked is_deleted. */
2714 zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply);
2715
2716 /* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
2717 main_metadata_str.s = NULL;
2718 if (phar->metadata_tracker.str) {
2719 smart_str_appendl(&main_metadata_str, ZSTR_VAL(phar->metadata_tracker.str), ZSTR_LEN(phar->metadata_tracker.str));
2720 } else if (!Z_ISUNDEF(phar->metadata_tracker.val)) {
2721 PHP_VAR_SERIALIZE_INIT(metadata_hash);
2722 php_var_serialize(&main_metadata_str, &phar->metadata_tracker.val, &metadata_hash);
2723 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2724 }
2725 new_manifest_count = 0;
2726 offset = 0;
2727 ZEND_HASH_MAP_FOREACH_PTR(&phar->manifest, entry) {
2728 if (entry->cfp) {
2729 /* did we forget to get rid of cfp last time? */
2730 php_stream_close(entry->cfp);
2731 entry->cfp = 0;
2732 }
2733 if (entry->is_deleted || entry->is_mounted) {
2734 /* remove this from the new phar */
2735 continue;
2736 }
2737 if (!entry->is_modified && entry->fp_refcount) {
2738 /* open file pointers refer to this fp, do not free the stream */
2739 switch (entry->fp_type) {
2740 case PHAR_FP:
2741 free_fp = 0;
2742 break;
2743 case PHAR_UFP:
2744 free_ufp = 0;
2745 default:
2746 break;
2747 }
2748 }
2749 /* after excluding deleted files, calculate manifest size in bytes and number of entries */
2750 ++new_manifest_count;
2751 phar_add_virtual_dirs(phar, entry->filename, entry->filename_len);
2752
2753 if (entry->is_dir) {
2754 /* we use this to calculate API version, 1.1.1 is used for phars with directories */
2755 has_dirs = 1;
2756 }
2757 if (!Z_ISUNDEF(entry->metadata_tracker.val) && !entry->metadata_tracker.str) {
2758 ZEND_ASSERT(!entry->is_persistent);
2759 /* Assume serialization will succeed. TODO: Set error and throw if EG(exception) != NULL */
2760 smart_str buf = {0};
2761 PHP_VAR_SERIALIZE_INIT(metadata_hash);
2762 php_var_serialize(&buf, &entry->metadata_tracker.val, &metadata_hash);
2763 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2764 entry->metadata_tracker.str = buf.s;
2765 }
2766
2767 /* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
2768 offset += 4 + entry->filename_len + sizeof(entry_buffer) + (entry->metadata_tracker.str ? ZSTR_LEN(entry->metadata_tracker.str) : 0) + (entry->is_dir ? 1 : 0);
2769
2770 /* compress and rehash as necessary */
2771 if ((oldfile && !entry->is_modified) || entry->is_dir) {
2772 if (entry->fp_type == PHAR_UFP) {
2773 /* reset so we can copy the compressed data over */
2774 entry->fp_type = PHAR_FP;
2775 }
2776 continue;
2777 }
2778 if (!phar_get_efp(entry, 0)) {
2779 /* re-open internal file pointer just-in-time */
2780 newentry = phar_open_jit(phar, entry, error);
2781 if (!newentry) {
2782 /* major problem re-opening, so we ignore this file and the error */
2783 efree(*error);
2784 *error = NULL;
2785 continue;
2786 }
2787 entry = newentry;
2788 }
2789 file = phar_get_efp(entry, 0);
2790 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1)) {
2791 if (closeoldfile) {
2792 php_stream_close(oldfile);
2793 }
2794 php_stream_close(newfile);
2795 if (error) {
2796 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2797 }
2798 return EOF;
2799 }
2800 newcrc32 = php_crc32_bulk_init();
2801 php_crc32_stream_bulk_update(&newcrc32, file, entry->uncompressed_filesize);
2802 entry->crc32 = php_crc32_bulk_end(newcrc32);
2803 entry->is_crc_checked = 1;
2804 if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
2805 /* not compressed */
2806 entry->compressed_filesize = entry->uncompressed_filesize;
2807 continue;
2808 }
2809 filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
2810 if (!filter) {
2811 if (closeoldfile) {
2812 php_stream_close(oldfile);
2813 }
2814 php_stream_close(newfile);
2815 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
2816 if (error) {
2817 spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2818 }
2819 } else {
2820 if (error) {
2821 spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2822 }
2823 }
2824 return EOF;
2825 }
2826
2827 /* create new file that holds the compressed versions */
2828 /* work around inability to specify freedom in write and strictness
2829 in read count */
2830 if (shared_cfp == NULL) {
2831 shared_cfp = php_stream_fopen_tmpfile();
2832 }
2833 entry->cfp = shared_cfp;
2834 if (!entry->cfp) {
2835 if (error) {
2836 spprintf(error, 0, "unable to create temporary file");
2837 }
2838 if (closeoldfile) {
2839 php_stream_close(oldfile);
2840 }
2841 php_stream_close(newfile);
2842 goto cleanup;
2843 }
2844 /* for real phars, header_offset is unused; we misuse it here to store the offset in the temp file */
2845 ZEND_ASSERT(entry->header_offset == 0);
2846 entry->header_offset = php_stream_tell(entry->cfp);
2847 php_stream_flush(file);
2848 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
2849 if (closeoldfile) {
2850 php_stream_close(oldfile);
2851 }
2852 php_stream_close(newfile);
2853 if (error) {
2854 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2855 }
2856 goto cleanup;
2857 }
2858 php_stream_filter_append((&entry->cfp->writefilters), filter);
2859 if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
2860 if (closeoldfile) {
2861 php_stream_close(oldfile);
2862 }
2863 php_stream_close(newfile);
2864 if (error) {
2865 spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2866 }
2867 goto cleanup;
2868 }
2869 php_stream_filter_flush(filter, 1);
2870 php_stream_flush(entry->cfp);
2871 php_stream_filter_remove(filter, 1);
2872 php_stream_seek(entry->cfp, 0, SEEK_END);
2873 entry->compressed_filesize = ((uint32_t) php_stream_tell(entry->cfp)) - entry->header_offset;
2874 /* generate crc on compressed file */
2875 entry->old_flags = entry->flags;
2876 entry->is_modified = 1;
2877 global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
2878 } ZEND_HASH_FOREACH_END();
2879 global_flags |= PHAR_HDR_SIGNATURE;
2880
2881 /* write out manifest pre-header */
2882 /* 4: manifest length
2883 * 4: manifest entry count
2884 * 2: phar version
2885 * 4: phar global flags
2886 * 4: alias length
2887 * ?: the alias itself
2888 * 4: phar metadata length
2889 * ?: phar metadata
2890 */
2891 restore_alias_len = phar->alias_len;
2892 if (phar->is_temporary_alias) {
2893 phar->alias_len = 0;
2894 }
2895
2896 manifest_len = offset + phar->alias_len + sizeof(manifest) + (main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2897 phar_set_32(manifest, manifest_len);
2898 /* Hack - see bug #65028, add padding byte to the end of the manifest */
2899 if(manifest[0] == '\r' || manifest[0] == '\n') {
2900 manifest_len++;
2901 phar_set_32(manifest, manifest_len);
2902 manifest_hack = 1;
2903 }
2904 phar_set_32(manifest+4, new_manifest_count);
2905 if (has_dirs) {
2906 *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
2907 *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
2908 } else {
2909 *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
2910 *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
2911 }
2912 phar_set_32(manifest+10, global_flags);
2913 phar_set_32(manifest+14, phar->alias_len);
2914
2915 /* write the manifest header */
2916 if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
2917 || (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
2918
2919 if (closeoldfile) {
2920 php_stream_close(oldfile);
2921 }
2922
2923 php_stream_close(newfile);
2924 phar->alias_len = restore_alias_len;
2925
2926 if (error) {
2927 spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
2928 }
2929
2930 goto cleanup;
2931 }
2932
2933 phar->alias_len = restore_alias_len;
2934
2935 phar_set_32(manifest, main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2936 if (4 != php_stream_write(newfile, manifest, 4) || ((main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0)
2937 && ZSTR_LEN(main_metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(main_metadata_str.s), ZSTR_LEN(main_metadata_str.s)))) {
2938 smart_str_free(&main_metadata_str);
2939
2940 if (closeoldfile) {
2941 php_stream_close(oldfile);
2942 }
2943
2944 php_stream_close(newfile);
2945 phar->alias_len = restore_alias_len;
2946
2947 if (error) {
2948 spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
2949 }
2950
2951 goto cleanup;
2952 }
2953 smart_str_free(&main_metadata_str);
2954
2955 /* re-calculate the manifest location to simplify later code */
2956 manifest_ftell = php_stream_tell(newfile);
2957
2958 /* now write the manifest */
2959 ZEND_HASH_MAP_FOREACH_PTR(&phar->manifest, entry) {
2960 const zend_string *metadata_str;
2961 if (entry->is_deleted || entry->is_mounted) {
2962 /* remove this from the new phar if deleted, ignore if mounted */
2963 continue;
2964 }
2965
2966 if (entry->is_dir) {
2967 /* add 1 for trailing slash */
2968 phar_set_32(entry_buffer, entry->filename_len + 1);
2969 } else {
2970 phar_set_32(entry_buffer, entry->filename_len);
2971 }
2972
2973 if (4 != php_stream_write(newfile, entry_buffer, 4)
2974 || entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
2975 || (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
2976 if (closeoldfile) {
2977 php_stream_close(oldfile);
2978 }
2979 php_stream_close(newfile);
2980 if (error) {
2981 if (entry->is_dir) {
2982 spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2983 } else {
2984 spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2985 }
2986 }
2987 goto cleanup;
2988 }
2989
2990 /* set the manifest meta-data:
2991 4: uncompressed filesize
2992 4: creation timestamp
2993 4: compressed filesize
2994 4: crc32
2995 4: flags
2996 4: metadata-len
2997 +: metadata
2998 */
2999 mytime = time(NULL);
3000 phar_set_32(entry_buffer, entry->uncompressed_filesize);
3001 phar_set_32(entry_buffer+4, mytime);
3002 phar_set_32(entry_buffer+8, entry->compressed_filesize);
3003 phar_set_32(entry_buffer+12, entry->crc32);
3004 phar_set_32(entry_buffer+16, entry->flags);
3005 metadata_str = entry->metadata_tracker.str;
3006 phar_set_32(entry_buffer+20, metadata_str ? ZSTR_LEN(metadata_str) : 0);
3007
3008 if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
3009 || (metadata_str &&
3010 ZSTR_LEN(metadata_str) != php_stream_write(newfile, ZSTR_VAL(metadata_str), ZSTR_LEN(metadata_str)))) {
3011 if (closeoldfile) {
3012 php_stream_close(oldfile);
3013 }
3014
3015 php_stream_close(newfile);
3016
3017 if (error) {
3018 spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3019 }
3020
3021 goto cleanup;
3022 }
3023 } ZEND_HASH_FOREACH_END();
3024 /* Hack - see bug #65028, add padding byte to the end of the manifest */
3025 if(manifest_hack) {
3026 if(1 != php_stream_write(newfile, manifest, 1)) {
3027 if (closeoldfile) {
3028 php_stream_close(oldfile);
3029 }
3030
3031 php_stream_close(newfile);
3032
3033 if (error) {
3034 spprintf(error, 0, "unable to write manifest padding byte");
3035 }
3036
3037 goto cleanup;
3038 }
3039 }
3040
3041 /* now copy the actual file data to the new phar */
3042 offset = php_stream_tell(newfile);
3043 ZEND_HASH_MAP_FOREACH_PTR(&phar->manifest, entry) {
3044 if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
3045 continue;
3046 }
3047
3048 if (entry->cfp) {
3049 file = entry->cfp;
3050 php_stream_seek(file, entry->header_offset, SEEK_SET);
3051 } else {
3052 file = phar_get_efp(entry, 0);
3053 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
3054 if (closeoldfile) {
3055 php_stream_close(oldfile);
3056 }
3057 php_stream_close(newfile);
3058 if (error) {
3059 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3060 }
3061 goto cleanup;
3062 }
3063 }
3064
3065 if (!file) {
3066 if (closeoldfile) {
3067 php_stream_close(oldfile);
3068 }
3069 php_stream_close(newfile);
3070 if (error) {
3071 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3072 }
3073 goto cleanup;
3074 }
3075
3076 /* this will have changed for all files that have either changed compression or been modified */
3077 entry->offset = entry->offset_abs = offset;
3078 offset += entry->compressed_filesize;
3079 if (php_stream_copy_to_stream_ex(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
3080 if (closeoldfile) {
3081 php_stream_close(oldfile);
3082 }
3083
3084 php_stream_close(newfile);
3085
3086 if (error) {
3087 spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
3088 }
3089
3090 goto cleanup;
3091 }
3092
3093 entry->is_modified = 0;
3094
3095 if (entry->cfp) {
3096 entry->cfp = NULL;
3097 entry->header_offset = 0;
3098 }
3099
3100 if (entry->fp_type == PHAR_MOD) {
3101 /* 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 */
3102 if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
3103 php_stream_close(entry->fp);
3104 }
3105
3106 entry->fp = NULL;
3107 entry->fp_type = PHAR_FP;
3108 } else if (entry->fp_type == PHAR_UFP) {
3109 entry->fp_type = PHAR_FP;
3110 }
3111 } ZEND_HASH_FOREACH_END();
3112
3113 if (shared_cfp != NULL) {
3114 php_stream_close(shared_cfp);
3115 shared_cfp = NULL;
3116 }
3117
3118 /* append signature */
3119 if (global_flags & PHAR_HDR_SIGNATURE) {
3120 char sig_buf[4];
3121
3122 php_stream_rewind(newfile);
3123
3124 if (phar->signature) {
3125 efree(phar->signature);
3126 phar->signature = NULL;
3127 }
3128
3129 switch(phar->sig_flags) {
3130 default: {
3131 char *digest = NULL;
3132 size_t digest_len;
3133
3134 if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error)) {
3135 if (error) {
3136 char *save = *error;
3137 spprintf(error, 0, "phar error: unable to write signature: %s", save);
3138 efree(save);
3139 }
3140 if (digest) {
3141 efree(digest);
3142 }
3143 if (closeoldfile) {
3144 php_stream_close(oldfile);
3145 }
3146 php_stream_close(newfile);
3147 return EOF;
3148 }
3149
3150 php_stream_write(newfile, digest, digest_len);
3151 efree(digest);
3152 if (phar->sig_flags == PHAR_SIG_OPENSSL ||
3153 phar->sig_flags == PHAR_SIG_OPENSSL_SHA256 ||
3154 phar->sig_flags == PHAR_SIG_OPENSSL_SHA512) {
3155 phar_set_32(sig_buf, digest_len);
3156 php_stream_write(newfile, sig_buf, 4);
3157 }
3158 break;
3159 }
3160 }
3161 phar_set_32(sig_buf, phar->sig_flags);
3162 php_stream_write(newfile, sig_buf, 4);
3163 php_stream_write(newfile, "GBMB", 4);
3164 }
3165
3166 /* finally, close the temp file, rename the original phar,
3167 move the temp to the old phar, unlink the old phar, and reload it into memory
3168 */
3169 if (phar->fp && free_fp) {
3170 php_stream_close(phar->fp);
3171 }
3172
3173 if (phar->ufp) {
3174 if (free_ufp) {
3175 php_stream_close(phar->ufp);
3176 }
3177 phar->ufp = NULL;
3178 }
3179
3180 if (closeoldfile) {
3181 php_stream_close(oldfile);
3182 }
3183
3184 phar->internal_file_start = halt_offset + manifest_len + 4;
3185 phar->halt_offset = halt_offset;
3186 phar->is_brandnew = 0;
3187
3188 php_stream_rewind(newfile);
3189
3190 if (phar->donotflush) {
3191 /* deferred flush */
3192 phar->fp = newfile;
3193 } else {
3194 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
3195 if (!phar->fp) {
3196 phar->fp = newfile;
3197 if (error) {
3198 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
3199 }
3200 return EOF;
3201 }
3202
3203 if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
3204 /* to properly compress, we have to tell zlib to add a zlib header */
3205 zval filterparams;
3206
3207 array_init(&filterparams);
3208 add_assoc_long(&filterparams, "window", MAX_WBITS+16);
3209 filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp));
3210 zend_array_destroy(Z_ARR(filterparams));
3211
3212 if (!filter) {
3213 if (error) {
3214 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);
3215 }
3216 return EOF;
3217 }
3218
3219 php_stream_filter_append(&phar->fp->writefilters, filter);
3220 php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3221 php_stream_filter_flush(filter, 1);
3222 php_stream_filter_remove(filter, 1);
3223 php_stream_close(phar->fp);
3224 /* use the temp stream as our base */
3225 phar->fp = newfile;
3226 } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
3227 filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp));
3228 php_stream_filter_append(&phar->fp->writefilters, filter);
3229 php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3230 php_stream_filter_flush(filter, 1);
3231 php_stream_filter_remove(filter, 1);
3232 php_stream_close(phar->fp);
3233 /* use the temp stream as our base */
3234 phar->fp = newfile;
3235 } else {
3236 php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3237 /* we could also reopen the file in "rb" mode but there is no need for that */
3238 php_stream_close(newfile);
3239 }
3240 }
3241
3242 if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
3243 if (error) {
3244 spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
3245 }
3246 return EOF;
3247 }
3248
3249 return EOF;
3250
3251 cleanup:
3252 if (shared_cfp != NULL) {
3253 php_stream_close(shared_cfp);
3254 }
3255 ZEND_HASH_MAP_FOREACH_PTR(&phar->manifest, entry) {
3256 if (entry->cfp) {
3257 entry->cfp = NULL;
3258 entry->header_offset = 0;
3259 }
3260 } ZEND_HASH_FOREACH_END();
3261
3262 return EOF;
3263 }
3264 /* }}} */
3265
3266 #ifdef COMPILE_DL_PHAR
3267 #ifdef ZTS
3268 ZEND_TSRMLS_CACHE_DEFINE()
3269 #endif
ZEND_GET_MODULE(phar)3270 ZEND_GET_MODULE(phar)
3271 #endif
3272
3273 static ssize_t phar_zend_stream_reader(void *handle, char *buf, size_t len) /* {{{ */
3274 {
3275 return php_stream_read(phar_get_pharfp((phar_archive_data*)handle), buf, len);
3276 }
3277 /* }}} */
3278
phar_zend_stream_fsizer(void * handle)3279 static size_t phar_zend_stream_fsizer(void *handle) /* {{{ */
3280 {
3281 return ((phar_archive_data*)handle)->halt_offset + 32;
3282 } /* }}} */
3283
3284 zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type);
3285
phar_resolve_path(zend_string * filename)3286 static zend_string *phar_resolve_path(zend_string *filename)
3287 {
3288 zend_string *ret = phar_find_in_include_path(filename, NULL);
3289 if (!ret) {
3290 ret = phar_save_resolve_path(filename);
3291 }
3292 return ret;
3293 }
3294
phar_compile_file(zend_file_handle * file_handle,int type)3295 static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type) /* {{{ */
3296 {
3297 zend_op_array *res;
3298 zend_string *name = NULL;
3299 int failed;
3300 phar_archive_data *phar;
3301
3302 if (!file_handle || !file_handle->filename) {
3303 return phar_orig_compile_file(file_handle, type);
3304 }
3305 if (strstr(ZSTR_VAL(file_handle->filename), ".phar") && !strstr(ZSTR_VAL(file_handle->filename), "://")) {
3306 if (SUCCESS == phar_open_from_filename(ZSTR_VAL(file_handle->filename), ZSTR_LEN(file_handle->filename), NULL, 0, 0, &phar, NULL)) {
3307 if (phar->is_zip || phar->is_tar) {
3308 zend_file_handle f;
3309
3310 /* zip or tar-based phar */
3311 name = zend_strpprintf(4096, "phar://%s/%s", ZSTR_VAL(file_handle->filename), ".phar/stub.php");
3312 zend_stream_init_filename_ex(&f, name);
3313 if (SUCCESS == zend_stream_open_function(&f)) {
3314 zend_string_release(f.filename);
3315 f.filename = file_handle->filename;
3316 if (f.opened_path) {
3317 zend_string_release(f.opened_path);
3318 }
3319 f.opened_path = file_handle->opened_path;
3320
3321 switch (file_handle->type) {
3322 case ZEND_HANDLE_STREAM:
3323 if (file_handle->handle.stream.closer && file_handle->handle.stream.handle) {
3324 file_handle->handle.stream.closer(file_handle->handle.stream.handle);
3325 }
3326 file_handle->handle.stream.handle = NULL;
3327 break;
3328 default:
3329 break;
3330 }
3331 *file_handle = f;
3332 }
3333 } else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
3334 /* compressed phar */
3335 file_handle->type = ZEND_HANDLE_STREAM;
3336 /* we do our own reading directly from the phar, don't change the next line */
3337 file_handle->handle.stream.handle = phar;
3338 file_handle->handle.stream.reader = phar_zend_stream_reader;
3339 file_handle->handle.stream.closer = NULL;
3340 file_handle->handle.stream.fsizer = phar_zend_stream_fsizer;
3341 file_handle->handle.stream.isatty = 0;
3342 phar->is_persistent ?
3343 php_stream_rewind(PHAR_G(cached_fp)[phar->phar_pos].fp) :
3344 php_stream_rewind(phar->fp);
3345 }
3346 }
3347 }
3348
3349 zend_try {
3350 failed = 0;
3351 CG(zend_lineno) = 0;
3352 res = phar_orig_compile_file(file_handle, type);
3353 } zend_catch {
3354 failed = 1;
3355 res = NULL;
3356 } zend_end_try();
3357
3358 if (name) {
3359 zend_string_release(name);
3360 }
3361
3362 if (failed) {
3363 zend_bailout();
3364 }
3365
3366 return res;
3367 }
3368 /* }}} */
3369
3370 typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int);
3371 typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
3372
mime_type_dtor(zval * zv)3373 static void mime_type_dtor(zval *zv)
3374 {
3375 free(Z_PTR_P(zv));
3376 }
3377
PHP_GINIT_FUNCTION(phar)3378 PHP_GINIT_FUNCTION(phar) /* {{{ */
3379 {
3380 #if defined(COMPILE_DL_PHAR) && defined(ZTS)
3381 ZEND_TSRMLS_CACHE_UPDATE();
3382 #endif
3383 phar_mime_type mime;
3384
3385 memset(phar_globals, 0, sizeof(zend_phar_globals));
3386 HT_INVALIDATE(&phar_globals->phar_persist_map);
3387 HT_INVALIDATE(&phar_globals->phar_fname_map);
3388 HT_INVALIDATE(&phar_globals->phar_alias_map);
3389 phar_globals->readonly = 1;
3390
3391 zend_hash_init(&phar_globals->mime_types, 0, NULL, mime_type_dtor, 1);
3392
3393 #define PHAR_SET_MIME(mimetype, ret, fileext) \
3394 mime.mime = mimetype; \
3395 mime.len = sizeof((mimetype))+1; \
3396 mime.type = ret; \
3397 zend_hash_str_add_mem(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type)); \
3398
3399 PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
3400 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
3401 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
3402 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
3403 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
3404 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
3405 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
3406 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
3407 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
3408 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
3409 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
3410 PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
3411 PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
3412 PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
3413 PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
3414 PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
3415 PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
3416 PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
3417 PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
3418 PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
3419 PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
3420 PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
3421 PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
3422 PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
3423 PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
3424 PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
3425 PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
3426 PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
3427 PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
3428 PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
3429 PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
3430 PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
3431 PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
3432 PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
3433 PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
3434 PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
3435 PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
3436 PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
3437 PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
3438 PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
3439
3440 phar_restore_orig_functions();
3441 }
3442 /* }}} */
3443
PHP_GSHUTDOWN_FUNCTION(phar)3444 PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
3445 {
3446 zend_hash_destroy(&phar_globals->mime_types);
3447 }
3448 /* }}} */
3449
PHP_MINIT_FUNCTION(phar)3450 PHP_MINIT_FUNCTION(phar) /* {{{ */
3451 {
3452 REGISTER_INI_ENTRIES();
3453
3454 phar_orig_compile_file = zend_compile_file;
3455 zend_compile_file = phar_compile_file;
3456
3457 phar_save_resolve_path = zend_resolve_path;
3458 zend_resolve_path = phar_resolve_path;
3459
3460 phar_object_init();
3461
3462 phar_intercept_functions_init();
3463 phar_save_orig_functions();
3464
3465 return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper);
3466 }
3467 /* }}} */
3468
PHP_MSHUTDOWN_FUNCTION(phar)3469 PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
3470 {
3471 php_unregister_url_stream_wrapper("phar");
3472
3473 phar_intercept_functions_shutdown();
3474
3475 if (zend_compile_file == phar_compile_file) {
3476 zend_compile_file = phar_orig_compile_file;
3477 }
3478
3479 if (PHAR_G(manifest_cached)) {
3480 zend_hash_destroy(&(cached_phars));
3481 zend_hash_destroy(&(cached_alias));
3482 }
3483
3484 UNREGISTER_INI_ENTRIES();
3485 return SUCCESS;
3486 }
3487 /* }}} */
3488
phar_request_initialize(void)3489 void phar_request_initialize(void) /* {{{ */
3490 {
3491 if (!PHAR_G(request_init))
3492 {
3493 PHAR_G(last_phar) = NULL;
3494 PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
3495 PHAR_G(has_bz2) = zend_hash_str_exists(&module_registry, "bz2", sizeof("bz2")-1);
3496 PHAR_G(has_zlib) = zend_hash_str_exists(&module_registry, "zlib", sizeof("zlib")-1);
3497 PHAR_G(request_init) = 1;
3498 PHAR_G(request_ends) = 0;
3499 PHAR_G(request_done) = 0;
3500 zend_hash_init(&(PHAR_G(phar_fname_map)), 5, zend_get_hash_value, destroy_phar_data, 0);
3501 zend_hash_init(&(PHAR_G(phar_persist_map)), 5, zend_get_hash_value, NULL, 0);
3502 zend_hash_init(&(PHAR_G(phar_alias_map)), 5, zend_get_hash_value, NULL, 0);
3503
3504 if (PHAR_G(manifest_cached)) {
3505 phar_archive_data *pphar;
3506 phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
3507
3508 ZEND_HASH_MAP_FOREACH_PTR(&cached_phars, pphar) {
3509 stuff[pphar->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar->manifest)), sizeof(phar_entry_fp_info));
3510 } ZEND_HASH_FOREACH_END();
3511
3512 PHAR_G(cached_fp) = stuff;
3513 }
3514
3515 PHAR_G(phar_SERVER_mung_list) = 0;
3516 PHAR_G(cwd) = NULL;
3517 PHAR_G(cwd_len) = 0;
3518 PHAR_G(cwd_init) = 0;
3519 }
3520 }
3521 /* }}} */
3522
PHP_RSHUTDOWN_FUNCTION(phar)3523 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
3524 {
3525 uint32_t i;
3526
3527 PHAR_G(request_ends) = 1;
3528
3529 if (PHAR_G(request_init))
3530 {
3531 phar_release_functions();
3532 zend_hash_destroy(&(PHAR_G(phar_alias_map)));
3533 HT_INVALIDATE(&PHAR_G(phar_alias_map));
3534 zend_hash_destroy(&(PHAR_G(phar_fname_map)));
3535 HT_INVALIDATE(&PHAR_G(phar_fname_map));
3536 zend_hash_destroy(&(PHAR_G(phar_persist_map)));
3537 HT_INVALIDATE(&PHAR_G(phar_persist_map));
3538 PHAR_G(phar_SERVER_mung_list) = 0;
3539
3540 if (PHAR_G(cached_fp)) {
3541 for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
3542 if (PHAR_G(cached_fp)[i].fp) {
3543 php_stream_close(PHAR_G(cached_fp)[i].fp);
3544 }
3545 if (PHAR_G(cached_fp)[i].ufp) {
3546 php_stream_close(PHAR_G(cached_fp)[i].ufp);
3547 }
3548 efree(PHAR_G(cached_fp)[i].manifest);
3549 }
3550 efree(PHAR_G(cached_fp));
3551 PHAR_G(cached_fp) = 0;
3552 }
3553
3554 PHAR_G(request_init) = 0;
3555
3556 if (PHAR_G(cwd)) {
3557 efree(PHAR_G(cwd));
3558 }
3559
3560 PHAR_G(cwd) = NULL;
3561 PHAR_G(cwd_len) = 0;
3562 PHAR_G(cwd_init) = 0;
3563 }
3564
3565 PHAR_G(request_done) = 1;
3566 return SUCCESS;
3567 }
3568 /* }}} */
3569
PHP_MINFO_FUNCTION(phar)3570 PHP_MINFO_FUNCTION(phar) /* {{{ */
3571 {
3572 phar_request_initialize();
3573 php_info_print_table_start();
3574 php_info_print_table_row(2, "Phar: PHP Archive support", "enabled");
3575 php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
3576 php_info_print_table_row(2, "Phar-based phar archives", "enabled");
3577 php_info_print_table_row(2, "Tar-based phar archives", "enabled");
3578 php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
3579
3580 if (PHAR_G(has_zlib)) {
3581 php_info_print_table_row(2, "gzip compression", "enabled");
3582 } else {
3583 php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
3584 }
3585
3586 if (PHAR_G(has_bz2)) {
3587 php_info_print_table_row(2, "bzip2 compression", "enabled");
3588 } else {
3589 php_info_print_table_row(2, "bzip2 compression", "disabled (install ext/bz2)");
3590 }
3591 #ifdef PHAR_HAVE_OPENSSL
3592 php_info_print_table_row(2, "Native OpenSSL support", "enabled");
3593 #else
3594 if (zend_hash_str_exists(&module_registry, "openssl", sizeof("openssl")-1)) {
3595 php_info_print_table_row(2, "OpenSSL support", "enabled");
3596 } else {
3597 php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
3598 }
3599 #endif
3600 php_info_print_table_end();
3601
3602 php_info_print_box_start(0);
3603 PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
3604 PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3605 PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
3606 PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3607 PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
3608 php_info_print_box_end();
3609
3610 DISPLAY_INI_ENTRIES();
3611 }
3612 /* }}} */
3613
3614 /* {{{ phar_module_entry */
3615 static const zend_module_dep phar_deps[] = {
3616 ZEND_MOD_OPTIONAL("apc")
3617 ZEND_MOD_OPTIONAL("bz2")
3618 ZEND_MOD_OPTIONAL("openssl")
3619 ZEND_MOD_OPTIONAL("zlib")
3620 ZEND_MOD_OPTIONAL("standard")
3621 ZEND_MOD_REQUIRED("hash")
3622 ZEND_MOD_REQUIRED("spl")
3623 ZEND_MOD_END
3624 };
3625
3626 zend_module_entry phar_module_entry = {
3627 STANDARD_MODULE_HEADER_EX, NULL,
3628 phar_deps,
3629 "Phar",
3630 NULL,
3631 PHP_MINIT(phar),
3632 PHP_MSHUTDOWN(phar),
3633 NULL,
3634 PHP_RSHUTDOWN(phar),
3635 PHP_MINFO(phar),
3636 PHP_PHAR_VERSION,
3637 PHP_MODULE_GLOBALS(phar), /* globals descriptor */
3638 PHP_GINIT(phar), /* globals ctor */
3639 PHP_GSHUTDOWN(phar), /* globals dtor */
3640 NULL, /* post deactivate */
3641 STANDARD_MODULE_PROPERTIES_EX
3642 };
3643 /* }}} */
3644