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