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