1 /*
2 +----------------------------------------------------------------------+
3 | phar php single-file executable PHP extension |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2005-2017 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: 308c1e92e8ad12e51f5db846d3366fdf3487eb21 $ */
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 = *(php_uint16*)(buffer); \
522 buffer += 2
523 #endif
524 #define PHAR_ZIP_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
525 (((php_uint16)var[1]) & 0xff) << 8))
526 #define PHAR_ZIP_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
527 (((php_uint32)var[1]) & 0xff) << 8 | \
528 (((php_uint32)var[2]) & 0xff) << 16 | \
529 (((php_uint32)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, php_uint32 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, php_uint32 compression, char **error) /* {{{ */
667 {
668 char b32[4], *buffer, *endbuffer, *savebuf;
669 phar_archive_data *mydata = NULL;
670 phar_entry_info entry;
671 php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
672 php_uint16 manifest_ver;
673 php_uint32 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 php_uint32 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 > 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 > 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 > 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_API_VERSION < 20100412
1343 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
1344 return FAILURE;
1345 }
1346 #endif
1347 if (php_check_open_basedir(fname)) {
1348 return FAILURE;
1349 }
1350
1351 /* first open readonly so it won't be created if not present */
1352 fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual);
1353
1354 if (actual) {
1355 fname = ZSTR_VAL(actual);
1356 fname_len = ZSTR_LEN(actual);
1357 }
1358
1359 if (fp) {
1360 if (phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error) == SUCCESS) {
1361 if ((*pphar)->is_data || !PHAR_G(readonly)) {
1362 (*pphar)->is_writeable = 1;
1363 }
1364 if (actual) {
1365 zend_string_release(actual);
1366 }
1367 return SUCCESS;
1368 } else {
1369 /* file exists, but is either corrupt or not a phar archive */
1370 if (actual) {
1371 zend_string_release(actual);
1372 }
1373 return FAILURE;
1374 }
1375 }
1376
1377 if (actual) {
1378 zend_string_release(actual);
1379 }
1380
1381 if (PHAR_G(readonly) && !is_data) {
1382 if (options & REPORT_ERRORS) {
1383 if (error) {
1384 spprintf(error, 0, "creating archive \"%s\" disabled by the php.ini setting phar.readonly", fname);
1385 }
1386 }
1387 return FAILURE;
1388 }
1389
1390 /* set up our manifest */
1391 mydata = ecalloc(1, sizeof(phar_archive_data));
1392 mydata->fname = expand_filepath(fname, NULL);
1393 fname_len = strlen(mydata->fname);
1394 #ifdef PHP_WIN32
1395 phar_unixify_path_separators(mydata->fname, fname_len);
1396 #endif
1397 p = strrchr(mydata->fname, '/');
1398
1399 if (p) {
1400 mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p);
1401 if (mydata->ext == p) {
1402 mydata->ext = memchr(p + 1, '.', (mydata->fname + fname_len) - p - 1);
1403 }
1404 if (mydata->ext) {
1405 mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
1406 }
1407 }
1408
1409 if (pphar) {
1410 *pphar = mydata;
1411 }
1412
1413 zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
1414 zend_get_hash_value, destroy_phar_manifest_entry, 0);
1415 zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
1416 zend_get_hash_value, NULL, 0);
1417 zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
1418 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1419 mydata->fname_len = fname_len;
1420 snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
1421 mydata->is_temporary_alias = alias ? 0 : 1;
1422 mydata->internal_file_start = -1;
1423 mydata->fp = NULL;
1424 mydata->is_writeable = 1;
1425 mydata->is_brandnew = 1;
1426 phar_request_initialize();
1427 zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len, mydata);
1428
1429 if (is_data) {
1430 alias = NULL;
1431 alias_len = 0;
1432 mydata->is_data = 1;
1433 /* assume tar format, PharData can specify other */
1434 mydata->is_tar = 1;
1435 } else {
1436 phar_archive_data *fd_ptr;
1437
1438 if (alias && NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
1439 if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
1440 if (error) {
1441 spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias);
1442 }
1443
1444 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
1445
1446 if (pphar) {
1447 *pphar = NULL;
1448 }
1449
1450 return FAILURE;
1451 }
1452 }
1453
1454 mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len);
1455 mydata->alias_len = alias ? alias_len : fname_len;
1456 }
1457
1458 if (alias_len && alias) {
1459 if (NULL == zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, mydata)) {
1460 if (options & REPORT_ERRORS) {
1461 if (error) {
1462 spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias);
1463 }
1464 }
1465
1466 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
1467
1468 if (pphar) {
1469 *pphar = NULL;
1470 }
1471
1472 return FAILURE;
1473 }
1474 }
1475
1476 return SUCCESS;
1477 }
1478 /* }}}*/
1479
1480 /**
1481 * Return an already opened filename.
1482 *
1483 * Or scan a phar file for the required __HALT_COMPILER(); ?> token and verify
1484 * that the manifest is proper, then pass it to phar_parse_pharfile(). SUCCESS
1485 * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1486 */
1487 int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error) /* {{{ */
1488 {
1489 php_stream *fp;
1490 zend_string *actual;
1491 int ret, is_data = 0;
1492
1493 if (error) {
1494 *error = NULL;
1495 }
1496
1497 if (!strstr(fname, ".phar")) {
1498 is_data = 1;
1499 }
1500
1501 if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error) == SUCCESS) {
1502 return SUCCESS;
1503 } else if (error && *error) {
1504 return FAILURE;
1505 }
1506 #if PHP_API_VERSION < 20100412
1507 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
1508 return FAILURE;
1509 }
1510 #endif
1511 if (php_check_open_basedir(fname)) {
1512 return FAILURE;
1513 }
1514
1515 fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
1516
1517 if (!fp) {
1518 if (options & REPORT_ERRORS) {
1519 if (error) {
1520 spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
1521 }
1522 }
1523 if (actual) {
1524 zend_string_release(actual);
1525 }
1526 return FAILURE;
1527 }
1528
1529 if (actual) {
1530 fname = ZSTR_VAL(actual);
1531 fname_len = ZSTR_LEN(actual);
1532 }
1533
1534 ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error);
1535
1536 if (actual) {
1537 zend_string_release(actual);
1538 }
1539
1540 return ret;
1541 }
1542 /* }}}*/
1543
1544 static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) /* {{{ */
1545 {
1546 const char *c;
1547 ptrdiff_t so_far = 0;
1548
1549 if (buf_len < search_len) {
1550 return NULL;
1551 }
1552
1553 c = buf - 1;
1554
1555 do {
1556 if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) {
1557 return (char *) NULL;
1558 }
1559
1560 so_far = c - buf;
1561
1562 if (so_far >= (buf_len - search_len)) {
1563 return (char *) NULL;
1564 }
1565
1566 if (!memcmp(c, search, search_len)) {
1567 return (char *) c;
1568 }
1569 } while (1);
1570 }
1571 /* }}} */
1572
1573 /**
1574 * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify
1575 * that the manifest is proper, then pass it to phar_parse_pharfile(). SUCCESS
1576 * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1577 */
1578 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) /* {{{ */
1579 {
1580 const char token[] = "__HALT_COMPILER();";
1581 const char zip_magic[] = "PK\x03\x04";
1582 const char gz_magic[] = "\x1f\x8b\x08";
1583 const char bz_magic[] = "BZh";
1584 char *pos, test = '\0';
1585 const int window_size = 1024;
1586 char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */
1587 const zend_long readsize = sizeof(buffer) - sizeof(token);
1588 const zend_long tokenlen = sizeof(token) - 1;
1589 zend_long halt_offset;
1590 size_t got;
1591 php_uint32 compression = PHAR_FILE_COMPRESSED_NONE;
1592
1593 if (error) {
1594 *error = NULL;
1595 }
1596
1597 if (-1 == php_stream_rewind(fp)) {
1598 MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"")
1599 }
1600
1601 buffer[sizeof(buffer)-1] = '\0';
1602 memset(buffer, 32, sizeof(token));
1603 halt_offset = 0;
1604
1605 /* Maybe it's better to compile the file instead of just searching, */
1606 /* but we only want the offset. So we want a .re scanner to find it. */
1607 while(!php_stream_eof(fp)) {
1608 if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) {
1609 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
1610 }
1611
1612 if (!test) {
1613 test = '\1';
1614 pos = buffer+tokenlen;
1615 if (!memcmp(pos, gz_magic, 3)) {
1616 char err = 0;
1617 php_stream_filter *filter;
1618 php_stream *temp;
1619 /* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
1620 zval filterparams;
1621
1622 if (!PHAR_G(has_zlib)) {
1623 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini")
1624 }
1625 array_init(&filterparams);
1626 /* this is defined in zlib's zconf.h */
1627 #ifndef MAX_WBITS
1628 #define MAX_WBITS 15
1629 #endif
1630 add_assoc_long_ex(&filterparams, "window", sizeof("window") - 1, MAX_WBITS + 32);
1631
1632 /* entire file is gzip-compressed, uncompress to temporary file */
1633 if (!(temp = php_stream_fopen_tmpfile())) {
1634 MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"")
1635 }
1636
1637 php_stream_rewind(fp);
1638 filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp));
1639
1640 if (!filter) {
1641 err = 1;
1642 add_assoc_long_ex(&filterparams, "window", sizeof("window") - 1, MAX_WBITS);
1643 filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp));
1644 zval_dtor(&filterparams);
1645
1646 if (!filter) {
1647 php_stream_close(temp);
1648 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1649 }
1650 } else {
1651 zval_dtor(&filterparams);
1652 }
1653
1654 php_stream_filter_append(&temp->writefilters, filter);
1655
1656 if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1657 if (err) {
1658 php_stream_close(temp);
1659 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1660 }
1661 php_stream_close(temp);
1662 MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file")
1663 }
1664
1665 php_stream_filter_flush(filter, 1);
1666 php_stream_filter_remove(filter, 1);
1667 php_stream_close(fp);
1668 fp = temp;
1669 php_stream_rewind(fp);
1670 compression = PHAR_FILE_COMPRESSED_GZ;
1671
1672 /* now, start over */
1673 test = '\0';
1674 continue;
1675 } else if (!memcmp(pos, bz_magic, 3)) {
1676 php_stream_filter *filter;
1677 php_stream *temp;
1678
1679 if (!PHAR_G(has_bz2)) {
1680 MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini")
1681 }
1682
1683 /* entire file is bzip-compressed, uncompress to temporary file */
1684 if (!(temp = php_stream_fopen_tmpfile())) {
1685 MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"")
1686 }
1687
1688 php_stream_rewind(fp);
1689 filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp));
1690
1691 if (!filter) {
1692 php_stream_close(temp);
1693 MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed")
1694 }
1695
1696 php_stream_filter_append(&temp->writefilters, filter);
1697
1698 if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1699 php_stream_close(temp);
1700 MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file")
1701 }
1702
1703 php_stream_filter_flush(filter, 1);
1704 php_stream_filter_remove(filter, 1);
1705 php_stream_close(fp);
1706 fp = temp;
1707 php_stream_rewind(fp);
1708 compression = PHAR_FILE_COMPRESSED_BZ2;
1709
1710 /* now, start over */
1711 test = '\0';
1712 continue;
1713 }
1714
1715 if (!memcmp(pos, zip_magic, 4)) {
1716 php_stream_seek(fp, 0, SEEK_END);
1717 return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error);
1718 }
1719
1720 if (got > 512) {
1721 if (phar_is_tar(pos, fname)) {
1722 php_stream_rewind(fp);
1723 return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error);
1724 }
1725 }
1726 }
1727
1728 if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) {
1729 halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */
1730 return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error);
1731 }
1732
1733 halt_offset += got;
1734 memmove(buffer, buffer + window_size, tokenlen); /* move the memory buffer by the size of the window */
1735 }
1736
1737 MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
1738 }
1739 /* }}} */
1740
1741 /*
1742 * given the location of the file extension and the start of the file path,
1743 * determine the end of the portion of the path (i.e. /path/to/file.ext/blah
1744 * grabs "/path/to/file.ext" as does the straight /path/to/file.ext),
1745 * stat it to determine if it exists.
1746 * if so, check to see if it is a directory and fail if so
1747 * if not, check to see if its dirname() exists (i.e. "/path/to") and is a directory
1748 * succeed if we are creating the file, otherwise fail.
1749 */
1750 static int phar_analyze_path(const char *fname, const char *ext, int ext_len, int for_create) /* {{{ */
1751 {
1752 php_stream_statbuf ssb;
1753 char *realpath;
1754 char *filename = estrndup(fname, (ext - fname) + ext_len);
1755
1756 if ((realpath = expand_filepath(filename, NULL))) {
1757 #ifdef PHP_WIN32
1758 phar_unixify_path_separators(realpath, strlen(realpath));
1759 #endif
1760 if (zend_hash_str_exists(&(PHAR_G(phar_fname_map)), realpath, strlen(realpath))) {
1761 efree(realpath);
1762 efree(filename);
1763 return SUCCESS;
1764 }
1765
1766 if (PHAR_G(manifest_cached) && zend_hash_str_exists(&cached_phars, realpath, strlen(realpath))) {
1767 efree(realpath);
1768 efree(filename);
1769 return SUCCESS;
1770 }
1771 efree(realpath);
1772 }
1773
1774 if (SUCCESS == php_stream_stat_path((char *) filename, &ssb)) {
1775
1776 efree(filename);
1777
1778 if (ssb.sb.st_mode & S_IFDIR) {
1779 return FAILURE;
1780 }
1781
1782 if (for_create == 1) {
1783 return FAILURE;
1784 }
1785
1786 return SUCCESS;
1787 } else {
1788 char *slash;
1789
1790 if (!for_create) {
1791 efree(filename);
1792 return FAILURE;
1793 }
1794
1795 slash = (char *) strrchr(filename, '/');
1796
1797 if (slash) {
1798 *slash = '\0';
1799 }
1800
1801 if (SUCCESS != php_stream_stat_path((char *) filename, &ssb)) {
1802 if (!slash) {
1803 if (!(realpath = expand_filepath(filename, NULL))) {
1804 efree(filename);
1805 return FAILURE;
1806 }
1807 #ifdef PHP_WIN32
1808 phar_unixify_path_separators(realpath, strlen(realpath));
1809 #endif
1810 slash = strstr(realpath, filename);
1811 if (slash) {
1812 slash += ((ext - fname) + ext_len);
1813 *slash = '\0';
1814 }
1815 slash = strrchr(realpath, '/');
1816
1817 if (slash) {
1818 *slash = '\0';
1819 } else {
1820 efree(realpath);
1821 efree(filename);
1822 return FAILURE;
1823 }
1824
1825 if (SUCCESS != php_stream_stat_path(realpath, &ssb)) {
1826 efree(realpath);
1827 efree(filename);
1828 return FAILURE;
1829 }
1830
1831 efree(realpath);
1832
1833 if (ssb.sb.st_mode & S_IFDIR) {
1834 efree(filename);
1835 return SUCCESS;
1836 }
1837 }
1838
1839 efree(filename);
1840 return FAILURE;
1841 }
1842
1843 efree(filename);
1844
1845 if (ssb.sb.st_mode & S_IFDIR) {
1846 return SUCCESS;
1847 }
1848
1849 return FAILURE;
1850 }
1851 }
1852 /* }}} */
1853
1854 /* check for ".phar" in extension */
1855 static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create) /* {{{ */
1856 {
1857 char test[51];
1858 const char *pos;
1859
1860 if (ext_len >= 50) {
1861 return FAILURE;
1862 }
1863
1864 if (executable == 1) {
1865 /* copy "." as well */
1866 memcpy(test, ext_str - 1, ext_len + 1);
1867 test[ext_len + 1] = '\0';
1868 /* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */
1869 /* (phar://hi/there/.phar/oops is also invalid) */
1870 pos = strstr(test, ".phar");
1871
1872 if (pos && (*(pos - 1) != '/')
1873 && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) {
1874 return phar_analyze_path(fname, ext_str, ext_len, for_create);
1875 } else {
1876 return FAILURE;
1877 }
1878 }
1879
1880 /* data phars need only contain a single non-"." to be valid */
1881 if (!executable) {
1882 pos = strstr(ext_str, ".phar");
1883 if (!(pos && (*(pos - 1) != '/')
1884 && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) && *(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1885 return phar_analyze_path(fname, ext_str, ext_len, for_create);
1886 }
1887 } else {
1888 if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1889 return phar_analyze_path(fname, ext_str, ext_len, for_create);
1890 }
1891 }
1892
1893 return FAILURE;
1894 }
1895 /* }}} */
1896
1897 /*
1898 * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions
1899 * if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats
1900 * the first extension as the filename extension
1901 *
1902 * if an extension is found, it sets ext_str to the location of the file extension in filename,
1903 * and ext_len to the length of the extension.
1904 * for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells
1905 * the calling function to use "alias" as the phar alias
1906 *
1907 * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the
1908 * extension rules, not to iterate.
1909 */
1910 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) /* {{{ */
1911 {
1912 const char *pos, *slash;
1913
1914 *ext_str = NULL;
1915 *ext_len = 0;
1916
1917 if (!filename_len || filename_len == 1) {
1918 return FAILURE;
1919 }
1920
1921 phar_request_initialize();
1922 /* first check for alias in first segment */
1923 pos = memchr(filename, '/', filename_len);
1924
1925 if (pos && pos != filename) {
1926 /* check for url like http:// or phar:// */
1927 if (*(pos - 1) == ':' && (pos - filename) < filename_len - 1 && *(pos + 1) == '/') {
1928 *ext_len = -2;
1929 *ext_str = NULL;
1930 return FAILURE;
1931 }
1932 if (zend_hash_str_exists(&(PHAR_G(phar_alias_map)), (char *) filename, pos - filename)) {
1933 *ext_str = pos;
1934 *ext_len = -1;
1935 return FAILURE;
1936 }
1937
1938 if (PHAR_G(manifest_cached) && zend_hash_str_exists(&cached_alias, (char *) filename, pos - filename)) {
1939 *ext_str = pos;
1940 *ext_len = -1;
1941 return FAILURE;
1942 }
1943 }
1944
1945 if (zend_hash_num_elements(&(PHAR_G(phar_fname_map))) || PHAR_G(manifest_cached)) {
1946 phar_archive_data *pphar;
1947
1948 if (is_complete) {
1949 if (NULL != (pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), (char *) filename, filename_len))) {
1950 *ext_str = filename + (filename_len - pphar->ext_len);
1951 woohoo:
1952 *ext_len = pphar->ext_len;
1953
1954 if (executable == 2) {
1955 return SUCCESS;
1956 }
1957
1958 if (executable == 1 && !pphar->is_data) {
1959 return SUCCESS;
1960 }
1961
1962 if (!executable && pphar->is_data) {
1963 return SUCCESS;
1964 }
1965
1966 return FAILURE;
1967 }
1968
1969 if (PHAR_G(manifest_cached) && NULL != (pphar = zend_hash_str_find_ptr(&cached_phars, (char *) filename, filename_len))) {
1970 *ext_str = filename + (filename_len - pphar->ext_len);
1971 goto woohoo;
1972 }
1973 } else {
1974 zend_string *str_key;
1975 zend_ulong unused;
1976
1977 for (zend_hash_internal_pointer_reset(&(PHAR_G(phar_fname_map)));
1978 HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(&(PHAR_G(phar_fname_map)), &str_key, &unused);
1979 zend_hash_move_forward(&(PHAR_G(phar_fname_map)))
1980 ) {
1981 if (ZSTR_LEN(str_key) > (uint) filename_len) {
1982 continue;
1983 }
1984
1985 if (!memcmp(filename, ZSTR_VAL(str_key), ZSTR_LEN(str_key)) && ((uint)filename_len == ZSTR_LEN(str_key)
1986 || filename[ZSTR_LEN(str_key)] == '/' || filename[ZSTR_LEN(str_key)] == '\0')) {
1987 if (NULL == (pphar = zend_hash_get_current_data_ptr(&(PHAR_G(phar_fname_map))))) {
1988 break;
1989 }
1990 *ext_str = filename + (ZSTR_LEN(str_key) - pphar->ext_len);
1991 goto woohoo;
1992 }
1993 }
1994
1995 if (PHAR_G(manifest_cached)) {
1996 for (zend_hash_internal_pointer_reset(&cached_phars);
1997 HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(&cached_phars, &str_key, &unused);
1998 zend_hash_move_forward(&cached_phars)
1999 ) {
2000 if (ZSTR_LEN(str_key) > (uint) filename_len) {
2001 continue;
2002 }
2003
2004 if (!memcmp(filename, ZSTR_VAL(str_key), ZSTR_LEN(str_key)) && ((uint)filename_len == ZSTR_LEN(str_key)
2005 || filename[ZSTR_LEN(str_key)] == '/' || filename[ZSTR_LEN(str_key)] == '\0')) {
2006 if (NULL == (pphar = zend_hash_get_current_data_ptr(&cached_phars))) {
2007 break;
2008 }
2009 *ext_str = filename + (ZSTR_LEN(str_key) - pphar->ext_len);
2010 goto woohoo;
2011 }
2012 }
2013 }
2014 }
2015 }
2016
2017 pos = memchr(filename + 1, '.', filename_len);
2018 next_extension:
2019 if (!pos) {
2020 return FAILURE;
2021 }
2022
2023 while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
2024 pos = memchr(pos + 1, '.', filename_len - (pos - filename) + 1);
2025 if (!pos) {
2026 return FAILURE;
2027 }
2028 }
2029
2030 slash = memchr(pos, '/', filename_len - (pos - filename));
2031
2032 if (!slash) {
2033 /* this is a url like "phar://blah.phar" with no directory */
2034 *ext_str = pos;
2035 *ext_len = strlen(pos);
2036
2037 /* file extension must contain "phar" */
2038 switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create)) {
2039 case SUCCESS:
2040 return SUCCESS;
2041 case FAILURE:
2042 /* we are at the end of the string, so we fail */
2043 return FAILURE;
2044 }
2045 }
2046
2047 /* we've found an extension that ends at a directory separator */
2048 *ext_str = pos;
2049 *ext_len = slash - pos;
2050
2051 switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create)) {
2052 case SUCCESS:
2053 return SUCCESS;
2054 case FAILURE:
2055 /* look for more extensions */
2056 pos = strchr(pos + 1, '.');
2057 if (pos) {
2058 *ext_str = NULL;
2059 *ext_len = 0;
2060 }
2061 goto next_extension;
2062 }
2063
2064 return FAILURE;
2065 }
2066 /* }}} */
2067
2068 static int php_check_dots(const char *element, int n) /* {{{ */
2069 {
2070 for(n--; n >= 0; --n) {
2071 if (element[n] != '.') {
2072 return 1;
2073 }
2074 }
2075 return 0;
2076 }
2077 /* }}} */
2078
2079 #define IS_DIRECTORY_UP(element, len) \
2080 (len >= 2 && !php_check_dots(element, len))
2081
2082 #define IS_DIRECTORY_CURRENT(element, len) \
2083 (len == 1 && element[0] == '.')
2084
2085 #define IS_BACKSLASH(c) ((c) == '/')
2086
2087 /**
2088 * Remove .. and . references within a phar filename
2089 */
2090 char *phar_fix_filepath(char *path, int *new_len, int use_cwd) /* {{{ */
2091 {
2092 char *newpath;
2093 int newpath_len;
2094 char *ptr;
2095 char *tok;
2096 int ptr_length, path_length = *new_len;
2097
2098 if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') {
2099 newpath_len = PHAR_G(cwd_len);
2100 newpath = emalloc(strlen(path) + newpath_len + 1);
2101 memcpy(newpath, PHAR_G(cwd), newpath_len);
2102 } else {
2103 newpath = emalloc(strlen(path) + 2);
2104 newpath[0] = '/';
2105 newpath_len = 1;
2106 }
2107
2108 ptr = path;
2109
2110 if (*ptr == '/') {
2111 ++ptr;
2112 }
2113
2114 tok = ptr;
2115
2116 do {
2117 ptr = memchr(ptr, '/', path_length - (ptr - path));
2118 } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2119
2120 if (!ptr && (path_length - (tok - path))) {
2121 switch (path_length - (tok - path)) {
2122 case 1:
2123 if (*tok == '.') {
2124 efree(path);
2125 *new_len = 1;
2126 efree(newpath);
2127 return estrndup("/", 1);
2128 }
2129 break;
2130 case 2:
2131 if (tok[0] == '.' && tok[1] == '.') {
2132 efree(path);
2133 *new_len = 1;
2134 efree(newpath);
2135 return estrndup("/", 1);
2136 }
2137 }
2138 efree(newpath);
2139 return path;
2140 }
2141
2142 while (ptr) {
2143 ptr_length = ptr - tok;
2144 last_time:
2145 if (IS_DIRECTORY_UP(tok, ptr_length)) {
2146 #define PREVIOUS newpath[newpath_len - 1]
2147
2148 while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) {
2149 newpath_len--;
2150 }
2151
2152 if (newpath[0] != '/') {
2153 newpath[newpath_len] = '\0';
2154 } else if (newpath_len > 1) {
2155 --newpath_len;
2156 }
2157 } else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) {
2158 if (newpath_len > 1) {
2159 newpath[newpath_len++] = '/';
2160 memcpy(newpath + newpath_len, tok, ptr_length+1);
2161 } else {
2162 memcpy(newpath + newpath_len, tok, ptr_length+1);
2163 }
2164
2165 newpath_len += ptr_length;
2166 }
2167
2168 if (ptr == path + path_length) {
2169 break;
2170 }
2171
2172 tok = ++ptr;
2173
2174 do {
2175 ptr = memchr(ptr, '/', path_length - (ptr - path));
2176 } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2177
2178 if (!ptr && (path_length - (tok - path))) {
2179 ptr_length = path_length - (tok - path);
2180 ptr = path + path_length;
2181 goto last_time;
2182 }
2183 }
2184
2185 efree(path);
2186 *new_len = newpath_len;
2187 newpath[newpath_len] = '\0';
2188 return erealloc(newpath, newpath_len + 1);
2189 }
2190 /* }}} */
2191
2192 /**
2193 * Process a phar stream name, ensuring we can handle any of:
2194 *
2195 * - whatever.phar
2196 * - whatever.phar.gz
2197 * - whatever.phar.bz2
2198 * - whatever.phar.php
2199 *
2200 * Optionally the name might start with 'phar://'
2201 *
2202 * This is used by phar_parse_url()
2203 */
2204 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) /* {{{ */
2205 {
2206 const char *ext_str;
2207 #ifdef PHP_WIN32
2208 char *save;
2209 #endif
2210 int ext_len;
2211
2212 if (CHECK_NULL_PATH(filename, filename_len)) {
2213 return FAILURE;
2214 }
2215
2216 if (CHECK_NULL_PATH(filename, filename_len)) {
2217 return FAILURE;
2218 }
2219
2220 if (!strncasecmp(filename, "phar://", 7)) {
2221 filename += 7;
2222 filename_len -= 7;
2223 }
2224
2225 ext_len = 0;
2226 #ifdef PHP_WIN32
2227 save = filename;
2228 filename = estrndup(filename, filename_len);
2229 phar_unixify_path_separators(filename, filename_len);
2230 #endif
2231 if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0) == FAILURE) {
2232 if (ext_len != -1) {
2233 if (!ext_str) {
2234 /* no / detected, restore arch for error message */
2235 #ifdef PHP_WIN32
2236 *arch = save;
2237 #else
2238 *arch = (char*)filename;
2239 #endif
2240 }
2241
2242 #ifdef PHP_WIN32
2243 efree(filename);
2244 #endif
2245 return FAILURE;
2246 }
2247
2248 ext_len = 0;
2249 /* no extension detected - instead we are dealing with an alias */
2250 }
2251
2252 *arch_len = ext_str - filename + ext_len;
2253 *arch = estrndup(filename, *arch_len);
2254
2255 if (ext_str[ext_len]) {
2256 *entry_len = filename_len - *arch_len;
2257 *entry = estrndup(ext_str+ext_len, *entry_len);
2258 #ifdef PHP_WIN32
2259 phar_unixify_path_separators(*entry, *entry_len);
2260 #endif
2261 *entry = phar_fix_filepath(*entry, entry_len, 0);
2262 } else {
2263 *entry_len = 1;
2264 *entry = estrndup("/", 1);
2265 }
2266
2267 #ifdef PHP_WIN32
2268 efree(filename);
2269 #endif
2270
2271 return SUCCESS;
2272 }
2273 /* }}} */
2274
2275 /**
2276 * Invoked when a user calls Phar::mapPhar() from within an executing .phar
2277 * to set up its manifest directly
2278 */
2279 int phar_open_executed_filename(char *alias, int alias_len, char **error) /* {{{ */
2280 {
2281 char *fname;
2282 php_stream *fp;
2283 int fname_len;
2284 zend_string *actual = NULL;
2285 int ret;
2286
2287 if (error) {
2288 *error = NULL;
2289 }
2290
2291 fname = (char*)zend_get_executed_filename();
2292 fname_len = strlen(fname);
2293
2294 if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, 0, REPORT_ERRORS, NULL, 0) == SUCCESS) {
2295 return SUCCESS;
2296 }
2297
2298 if (!strcmp(fname, "[no active file]")) {
2299 if (error) {
2300 spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
2301 }
2302 return FAILURE;
2303 }
2304
2305 if (0 == zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) {
2306 if (error) {
2307 spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
2308 }
2309 return FAILURE;
2310 }
2311
2312
2313 #if PHP_API_VERSION < 20100412
2314 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
2315 return FAILURE;
2316 }
2317 #endif
2318
2319 if (php_check_open_basedir(fname)) {
2320 return FAILURE;
2321 }
2322
2323 fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
2324
2325 if (!fp) {
2326 if (error) {
2327 spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
2328 }
2329 if (actual) {
2330 zend_string_release(actual);
2331 }
2332 return FAILURE;
2333 }
2334
2335 if (actual) {
2336 fname = ZSTR_VAL(actual);
2337 fname_len = ZSTR_LEN(actual);
2338 }
2339
2340 ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0, error);
2341
2342 if (actual) {
2343 zend_string_release(actual);
2344 }
2345
2346 return ret;
2347 }
2348 /* }}} */
2349
2350 /**
2351 * Validate the CRC32 of a file opened from within the phar
2352 */
2353 int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip) /* {{{ */
2354 {
2355 php_uint32 crc = ~0;
2356 int len = idata->internal_file->uncompressed_filesize;
2357 php_stream *fp = idata->fp;
2358 phar_entry_info *entry = idata->internal_file;
2359
2360 if (error) {
2361 *error = NULL;
2362 }
2363
2364 if (entry->is_zip && process_zip > 0) {
2365 /* verify local file header */
2366 phar_zip_file_header local;
2367 phar_zip_data_desc desc;
2368
2369 if (SUCCESS != phar_open_archive_fp(idata->phar)) {
2370 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);
2371 return FAILURE;
2372 }
2373 php_stream_seek(phar_get_entrypfp(idata->internal_file), entry->header_offset, SEEK_SET);
2374
2375 if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file), (char *) &local, sizeof(local))) {
2376
2377 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);
2378 return FAILURE;
2379 }
2380
2381 /* check for data descriptor */
2382 if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
2383 php_stream_seek(phar_get_entrypfp(idata->internal_file),
2384 entry->header_offset + sizeof(local) +
2385 PHAR_ZIP_16(local.filename_len) +
2386 PHAR_ZIP_16(local.extra_len) +
2387 entry->compressed_filesize, SEEK_SET);
2388 if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file),
2389 (char *) &desc, sizeof(desc))) {
2390 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);
2391 return FAILURE;
2392 }
2393 if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
2394 memcpy(&(local.crc32), &(desc.crc32), 12);
2395 } else {
2396 /* old data descriptors have no signature */
2397 memcpy(&(local.crc32), &desc, 12);
2398 }
2399 }
2400 /* verify local header */
2401 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)) {
2402 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);
2403 return FAILURE;
2404 }
2405
2406 /* construct actual offset to file start - local extra_len can be different from central extra_len */
2407 entry->offset = entry->offset_abs =
2408 sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
2409
2410 if (idata->zero && idata->zero != entry->offset_abs) {
2411 idata->zero = entry->offset_abs;
2412 }
2413 }
2414
2415 if (process_zip == 1) {
2416 return SUCCESS;
2417 }
2418
2419 php_stream_seek(fp, idata->zero, SEEK_SET);
2420
2421 while (len--) {
2422 CRC32(crc, php_stream_getc(fp));
2423 }
2424
2425 php_stream_seek(fp, idata->zero, SEEK_SET);
2426
2427 if (~crc == crc32) {
2428 entry->is_crc_checked = 1;
2429 return SUCCESS;
2430 } else {
2431 spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
2432 return FAILURE;
2433 }
2434 }
2435 /* }}} */
2436
2437 static inline void phar_set_32(char *buffer, int var) /* {{{ */
2438 {
2439 #ifdef WORDS_BIGENDIAN
2440 *((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
2441 *((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
2442 *((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
2443 *((buffer) + 0) = (unsigned char) ((var) & 0xFF);
2444 #else
2445 memcpy(buffer, &var, sizeof(var));
2446 #endif
2447 } /* }}} */
2448
2449 static int phar_flush_clean_deleted_apply(zval *zv) /* {{{ */
2450 {
2451 phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
2452
2453 if (entry->fp_refcount <= 0 && entry->is_deleted) {
2454 return ZEND_HASH_APPLY_REMOVE;
2455 } else {
2456 return ZEND_HASH_APPLY_KEEP;
2457 }
2458 }
2459 /* }}} */
2460
2461 #include "stub.h"
2462
2463 zend_string *phar_create_default_stub(const char *index_php, const char *web_index, char **error) /* {{{ */
2464 {
2465 int index_len, web_len;
2466
2467 if (error) {
2468 *error = NULL;
2469 }
2470
2471 if (!index_php) {
2472 index_php = "index.php";
2473 }
2474
2475 if (!web_index) {
2476 web_index = "index.php";
2477 }
2478
2479 index_len = strlen(index_php);
2480 web_len = strlen(web_index);
2481
2482 if (index_len > 400) {
2483 /* ridiculous size not allowed for index.php startup filename */
2484 if (error) {
2485 spprintf(error, 0, "Illegal filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", index_len);
2486 return NULL;
2487 }
2488 }
2489
2490 if (web_len > 400) {
2491 /* ridiculous size not allowed for index.php startup filename */
2492 if (error) {
2493 spprintf(error, 0, "Illegal web filename passed in for stub creation, was %d characters long, and only 400 or less is allowed", web_len);
2494 return NULL;
2495 }
2496 }
2497
2498 return phar_get_stub(index_php, web_index, index_len+1, web_len+1);
2499 }
2500 /* }}} */
2501
2502 /**
2503 * Save phar contents to disk
2504 *
2505 * user_stub contains either a string, or a resource pointer, if len is a negative length.
2506 * user_stub and len should be both 0 if the default or existing stub should be used
2507 */
2508 int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int convert, char **error) /* {{{ */
2509 {
2510 char halt_stub[] = "__HALT_COMPILER();";
2511 zend_string *newstub;
2512 char *tmp;
2513 phar_entry_info *entry, *newentry;
2514 int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
2515 char *pos, has_dirs = 0;
2516 char manifest[18], entry_buffer[24];
2517 zend_off_t manifest_ftell;
2518 zend_long offset;
2519 size_t wrote;
2520 php_uint32 manifest_len, mytime, loc, new_manifest_count;
2521 php_uint32 newcrc32;
2522 php_stream *file, *oldfile, *newfile, *stubfile;
2523 php_stream_filter *filter;
2524 php_serialize_data_t metadata_hash;
2525 smart_str main_metadata_str = {0};
2526 int free_user_stub, free_fp = 1, free_ufp = 1;
2527 int manifest_hack = 0;
2528
2529 if (phar->is_persistent) {
2530 if (error) {
2531 spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
2532 }
2533 return EOF;
2534 }
2535
2536 if (error) {
2537 *error = NULL;
2538 }
2539
2540 if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
2541 return EOF;
2542 }
2543
2544 zend_hash_clean(&phar->virtual_dirs);
2545
2546 if (phar->is_zip) {
2547 return phar_zip_flush(phar, user_stub, len, convert, error);
2548 }
2549
2550 if (phar->is_tar) {
2551 return phar_tar_flush(phar, user_stub, len, convert, error);
2552 }
2553
2554 if (PHAR_G(readonly)) {
2555 return EOF;
2556 }
2557
2558 if (phar->fp && !phar->is_brandnew) {
2559 oldfile = phar->fp;
2560 closeoldfile = 0;
2561 php_stream_rewind(oldfile);
2562 } else {
2563 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
2564 closeoldfile = oldfile != NULL;
2565 }
2566 newfile = php_stream_fopen_tmpfile();
2567 if (!newfile) {
2568 if (error) {
2569 spprintf(error, 0, "unable to create temporary file");
2570 }
2571 if (closeoldfile) {
2572 php_stream_close(oldfile);
2573 }
2574 return EOF;
2575 }
2576
2577 if (user_stub) {
2578 zend_string *suser_stub;
2579 if (len < 0) {
2580 /* resource passed in */
2581 if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
2582 if (closeoldfile) {
2583 php_stream_close(oldfile);
2584 }
2585 php_stream_close(newfile);
2586 if (error) {
2587 spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
2588 }
2589 return EOF;
2590 }
2591 if (len == -1) {
2592 len = PHP_STREAM_COPY_ALL;
2593 } else {
2594 len = -len;
2595 }
2596 user_stub = 0;
2597
2598 if (!(suser_stub = php_stream_copy_to_mem(stubfile, len, 0))) {
2599 if (closeoldfile) {
2600 php_stream_close(oldfile);
2601 }
2602 php_stream_close(newfile);
2603 if (error) {
2604 spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
2605 }
2606 return EOF;
2607 }
2608 free_user_stub = 1;
2609 user_stub = ZSTR_VAL(suser_stub);
2610 len = ZSTR_LEN(suser_stub);
2611 } else {
2612 free_user_stub = 0;
2613 }
2614 tmp = estrndup(user_stub, len);
2615 if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
2616 efree(tmp);
2617 if (closeoldfile) {
2618 php_stream_close(oldfile);
2619 }
2620 php_stream_close(newfile);
2621 if (error) {
2622 spprintf(error, 0, "illegal stub for phar \"%s\"", phar->fname);
2623 }
2624 if (free_user_stub) {
2625 zend_string_free(suser_stub);
2626 }
2627 return EOF;
2628 }
2629 pos = user_stub + (pos - tmp);
2630 efree(tmp);
2631 len = pos - user_stub + 18;
2632 if ((size_t)len != php_stream_write(newfile, user_stub, len)
2633 || 5 != php_stream_write(newfile, " ?>\r\n", 5)) {
2634 if (closeoldfile) {
2635 php_stream_close(oldfile);
2636 }
2637 php_stream_close(newfile);
2638 if (error) {
2639 spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
2640 }
2641 if (free_user_stub) {
2642 zend_string_free(suser_stub);
2643 }
2644 return EOF;
2645 }
2646 phar->halt_offset = len + 5;
2647 if (free_user_stub) {
2648 zend_string_free(suser_stub);
2649 }
2650 } else {
2651 size_t written;
2652
2653 if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
2654 php_stream_copy_to_stream_ex(oldfile, newfile, phar->halt_offset, &written);
2655 newstub = NULL;
2656 } else {
2657 /* this is either a brand new phar or a default stub overwrite */
2658 newstub = phar_create_default_stub(NULL, NULL, NULL);
2659 phar->halt_offset = ZSTR_LEN(newstub);
2660 written = php_stream_write(newfile, ZSTR_VAL(newstub), phar->halt_offset);
2661 }
2662 if (phar->halt_offset != written) {
2663 if (closeoldfile) {
2664 php_stream_close(oldfile);
2665 }
2666 php_stream_close(newfile);
2667 if (error) {
2668 if (newstub) {
2669 spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
2670 } else {
2671 spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
2672 }
2673 }
2674 if (newstub) {
2675 zend_string_free(newstub);
2676 }
2677 return EOF;
2678 }
2679 if (newstub) {
2680 zend_string_free(newstub);
2681 }
2682 }
2683 manifest_ftell = php_stream_tell(newfile);
2684 halt_offset = manifest_ftell;
2685
2686 /* Check whether we can get rid of some of the deleted entries which are
2687 * unused. However some might still be in use so even after this clean-up
2688 * we need to skip entries marked is_deleted. */
2689 zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply);
2690
2691 /* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
2692 main_metadata_str.s = NULL;
2693 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
2694 PHP_VAR_SERIALIZE_INIT(metadata_hash);
2695 php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash);
2696 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2697 }
2698 new_manifest_count = 0;
2699 offset = 0;
2700 for (zend_hash_internal_pointer_reset(&phar->manifest);
2701 zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2702 zend_hash_move_forward(&phar->manifest)) {
2703 if ((entry = zend_hash_get_current_data_ptr(&phar->manifest)) == NULL) {
2704 continue;
2705 }
2706 if (entry->cfp) {
2707 /* did we forget to get rid of cfp last time? */
2708 php_stream_close(entry->cfp);
2709 entry->cfp = 0;
2710 }
2711 if (entry->is_deleted || entry->is_mounted) {
2712 /* remove this from the new phar */
2713 continue;
2714 }
2715 if (!entry->is_modified && entry->fp_refcount) {
2716 /* open file pointers refer to this fp, do not free the stream */
2717 switch (entry->fp_type) {
2718 case PHAR_FP:
2719 free_fp = 0;
2720 break;
2721 case PHAR_UFP:
2722 free_ufp = 0;
2723 default:
2724 break;
2725 }
2726 }
2727 /* after excluding deleted files, calculate manifest size in bytes and number of entries */
2728 ++new_manifest_count;
2729 phar_add_virtual_dirs(phar, entry->filename, entry->filename_len);
2730
2731 if (entry->is_dir) {
2732 /* we use this to calculate API version, 1.1.1 is used for phars with directories */
2733 has_dirs = 1;
2734 }
2735 if (Z_TYPE(entry->metadata) != IS_UNDEF) {
2736 if (entry->metadata_str.s) {
2737 smart_str_free(&entry->metadata_str);
2738 }
2739 entry->metadata_str.s = NULL;
2740 PHP_VAR_SERIALIZE_INIT(metadata_hash);
2741 php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash);
2742 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2743 } else {
2744 if (entry->metadata_str.s) {
2745 smart_str_free(&entry->metadata_str);
2746 }
2747 entry->metadata_str.s = NULL;
2748 }
2749
2750 /* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
2751 offset += 4 + entry->filename_len + sizeof(entry_buffer) + (entry->metadata_str.s ? ZSTR_LEN(entry->metadata_str.s) : 0) + (entry->is_dir ? 1 : 0);
2752
2753 /* compress and rehash as necessary */
2754 if ((oldfile && !entry->is_modified) || entry->is_dir) {
2755 if (entry->fp_type == PHAR_UFP) {
2756 /* reset so we can copy the compressed data over */
2757 entry->fp_type = PHAR_FP;
2758 }
2759 continue;
2760 }
2761 if (!phar_get_efp(entry, 0)) {
2762 /* re-open internal file pointer just-in-time */
2763 newentry = phar_open_jit(phar, entry, error);
2764 if (!newentry) {
2765 /* major problem re-opening, so we ignore this file and the error */
2766 efree(*error);
2767 *error = NULL;
2768 continue;
2769 }
2770 entry = newentry;
2771 }
2772 file = phar_get_efp(entry, 0);
2773 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1)) {
2774 if (closeoldfile) {
2775 php_stream_close(oldfile);
2776 }
2777 php_stream_close(newfile);
2778 if (error) {
2779 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2780 }
2781 return EOF;
2782 }
2783 newcrc32 = ~0;
2784 mytime = entry->uncompressed_filesize;
2785 for (loc = 0;loc < mytime; ++loc) {
2786 CRC32(newcrc32, php_stream_getc(file));
2787 }
2788 entry->crc32 = ~newcrc32;
2789 entry->is_crc_checked = 1;
2790 if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
2791 /* not compressed */
2792 entry->compressed_filesize = entry->uncompressed_filesize;
2793 continue;
2794 }
2795 filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
2796 if (!filter) {
2797 if (closeoldfile) {
2798 php_stream_close(oldfile);
2799 }
2800 php_stream_close(newfile);
2801 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
2802 if (error) {
2803 spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2804 }
2805 } else {
2806 if (error) {
2807 spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2808 }
2809 }
2810 return EOF;
2811 }
2812
2813 /* create new file that holds the compressed version */
2814 /* work around inability to specify freedom in write and strictness
2815 in read count */
2816 entry->cfp = php_stream_fopen_tmpfile();
2817 if (!entry->cfp) {
2818 if (error) {
2819 spprintf(error, 0, "unable to create temporary file");
2820 }
2821 if (closeoldfile) {
2822 php_stream_close(oldfile);
2823 }
2824 php_stream_close(newfile);
2825 return EOF;
2826 }
2827 php_stream_flush(file);
2828 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
2829 if (closeoldfile) {
2830 php_stream_close(oldfile);
2831 }
2832 php_stream_close(newfile);
2833 if (error) {
2834 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2835 }
2836 return EOF;
2837 }
2838 php_stream_filter_append((&entry->cfp->writefilters), filter);
2839 if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
2840 if (closeoldfile) {
2841 php_stream_close(oldfile);
2842 }
2843 php_stream_close(newfile);
2844 if (error) {
2845 spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2846 }
2847 return EOF;
2848 }
2849 php_stream_filter_flush(filter, 1);
2850 php_stream_flush(entry->cfp);
2851 php_stream_filter_remove(filter, 1);
2852 php_stream_seek(entry->cfp, 0, SEEK_END);
2853 entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
2854 /* generate crc on compressed file */
2855 php_stream_rewind(entry->cfp);
2856 entry->old_flags = entry->flags;
2857 entry->is_modified = 1;
2858 global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
2859 }
2860 global_flags |= PHAR_HDR_SIGNATURE;
2861
2862 /* write out manifest pre-header */
2863 /* 4: manifest length
2864 * 4: manifest entry count
2865 * 2: phar version
2866 * 4: phar global flags
2867 * 4: alias length
2868 * ?: the alias itself
2869 * 4: phar metadata length
2870 * ?: phar metadata
2871 */
2872 restore_alias_len = phar->alias_len;
2873 if (phar->is_temporary_alias) {
2874 phar->alias_len = 0;
2875 }
2876
2877 manifest_len = offset + phar->alias_len + sizeof(manifest) + (main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2878 phar_set_32(manifest, manifest_len);
2879 /* Hack - see bug #65028, add padding byte to the end of the manifest */
2880 if(manifest[0] == '\r' || manifest[0] == '\n') {
2881 manifest_len++;
2882 phar_set_32(manifest, manifest_len);
2883 manifest_hack = 1;
2884 }
2885 phar_set_32(manifest+4, new_manifest_count);
2886 if (has_dirs) {
2887 *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
2888 *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
2889 } else {
2890 *(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
2891 *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
2892 }
2893 phar_set_32(manifest+10, global_flags);
2894 phar_set_32(manifest+14, phar->alias_len);
2895
2896 /* write the manifest header */
2897 if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
2898 || (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
2899
2900 if (closeoldfile) {
2901 php_stream_close(oldfile);
2902 }
2903
2904 php_stream_close(newfile);
2905 phar->alias_len = restore_alias_len;
2906
2907 if (error) {
2908 spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
2909 }
2910
2911 return EOF;
2912 }
2913
2914 phar->alias_len = restore_alias_len;
2915
2916 phar_set_32(manifest, main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2917 if (4 != php_stream_write(newfile, manifest, 4) || ((main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0)
2918 && ZSTR_LEN(main_metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(main_metadata_str.s), ZSTR_LEN(main_metadata_str.s)))) {
2919 smart_str_free(&main_metadata_str);
2920
2921 if (closeoldfile) {
2922 php_stream_close(oldfile);
2923 }
2924
2925 php_stream_close(newfile);
2926 phar->alias_len = restore_alias_len;
2927
2928 if (error) {
2929 spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
2930 }
2931
2932 return EOF;
2933 }
2934 smart_str_free(&main_metadata_str);
2935
2936 /* re-calculate the manifest location to simplify later code */
2937 manifest_ftell = php_stream_tell(newfile);
2938
2939 /* now write the manifest */
2940 for (zend_hash_internal_pointer_reset(&phar->manifest);
2941 zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2942 zend_hash_move_forward(&phar->manifest)) {
2943
2944 if ((entry = zend_hash_get_current_data_ptr(&phar->manifest)) == NULL) {
2945 continue;
2946 }
2947
2948 if (entry->is_deleted || entry->is_mounted) {
2949 /* remove this from the new phar if deleted, ignore if mounted */
2950 continue;
2951 }
2952
2953 if (entry->is_dir) {
2954 /* add 1 for trailing slash */
2955 phar_set_32(entry_buffer, entry->filename_len + 1);
2956 } else {
2957 phar_set_32(entry_buffer, entry->filename_len);
2958 }
2959
2960 if (4 != php_stream_write(newfile, entry_buffer, 4)
2961 || entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
2962 || (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
2963 if (closeoldfile) {
2964 php_stream_close(oldfile);
2965 }
2966 php_stream_close(newfile);
2967 if (error) {
2968 if (entry->is_dir) {
2969 spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2970 } else {
2971 spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2972 }
2973 }
2974 return EOF;
2975 }
2976
2977 /* set the manifest meta-data:
2978 4: uncompressed filesize
2979 4: creation timestamp
2980 4: compressed filesize
2981 4: crc32
2982 4: flags
2983 4: metadata-len
2984 +: metadata
2985 */
2986 mytime = time(NULL);
2987 phar_set_32(entry_buffer, entry->uncompressed_filesize);
2988 phar_set_32(entry_buffer+4, mytime);
2989 phar_set_32(entry_buffer+8, entry->compressed_filesize);
2990 phar_set_32(entry_buffer+12, entry->crc32);
2991 phar_set_32(entry_buffer+16, entry->flags);
2992 phar_set_32(entry_buffer+20, entry->metadata_str.s ? ZSTR_LEN(entry->metadata_str.s) : 0);
2993
2994 if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
2995 || (entry->metadata_str.s &&
2996 ZSTR_LEN(entry->metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(entry->metadata_str.s), ZSTR_LEN(entry->metadata_str.s)))) {
2997 if (closeoldfile) {
2998 php_stream_close(oldfile);
2999 }
3000
3001 php_stream_close(newfile);
3002
3003 if (error) {
3004 spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3005 }
3006
3007 return EOF;
3008 }
3009 }
3010 /* Hack - see bug #65028, add padding byte to the end of the manifest */
3011 if(manifest_hack) {
3012 if(1 != php_stream_write(newfile, manifest, 1)) {
3013 if (closeoldfile) {
3014 php_stream_close(oldfile);
3015 }
3016
3017 php_stream_close(newfile);
3018
3019 if (error) {
3020 spprintf(error, 0, "unable to write manifest padding byte");
3021 }
3022
3023 return EOF;
3024 }
3025 }
3026
3027 /* now copy the actual file data to the new phar */
3028 offset = php_stream_tell(newfile);
3029 for (zend_hash_internal_pointer_reset(&phar->manifest);
3030 zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
3031 zend_hash_move_forward(&phar->manifest)) {
3032
3033 if ((entry = zend_hash_get_current_data_ptr(&phar->manifest)) == NULL) {
3034 continue;
3035 }
3036
3037 if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
3038 continue;
3039 }
3040
3041 if (entry->cfp) {
3042 file = entry->cfp;
3043 php_stream_rewind(file);
3044 } else {
3045 file = phar_get_efp(entry, 0);
3046 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
3047 if (closeoldfile) {
3048 php_stream_close(oldfile);
3049 }
3050 php_stream_close(newfile);
3051 if (error) {
3052 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3053 }
3054 return EOF;
3055 }
3056 }
3057
3058 if (!file) {
3059 if (closeoldfile) {
3060 php_stream_close(oldfile);
3061 }
3062 php_stream_close(newfile);
3063 if (error) {
3064 spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3065 }
3066 return EOF;
3067 }
3068
3069 /* this will have changed for all files that have either changed compression or been modified */
3070 entry->offset = entry->offset_abs = offset;
3071 offset += entry->compressed_filesize;
3072 if (php_stream_copy_to_stream_ex(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
3073 if (closeoldfile) {
3074 php_stream_close(oldfile);
3075 }
3076
3077 php_stream_close(newfile);
3078
3079 if (error) {
3080 spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
3081 }
3082
3083 return EOF;
3084 }
3085
3086 entry->is_modified = 0;
3087
3088 if (entry->cfp) {
3089 php_stream_close(entry->cfp);
3090 entry->cfp = NULL;
3091 }
3092
3093 if (entry->fp_type == PHAR_MOD) {
3094 /* 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 */
3095 if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
3096 php_stream_close(entry->fp);
3097 }
3098
3099 entry->fp = NULL;
3100 entry->fp_type = PHAR_FP;
3101 } else if (entry->fp_type == PHAR_UFP) {
3102 entry->fp_type = PHAR_FP;
3103 }
3104 }
3105
3106 /* append signature */
3107 if (global_flags & PHAR_HDR_SIGNATURE) {
3108 char sig_buf[4];
3109
3110 php_stream_rewind(newfile);
3111
3112 if (phar->signature) {
3113 efree(phar->signature);
3114 phar->signature = NULL;
3115 }
3116
3117 switch(phar->sig_flags) {
3118 #ifndef PHAR_HASH_OK
3119 case PHAR_SIG_SHA512:
3120 case PHAR_SIG_SHA256:
3121 if (closeoldfile) {
3122 php_stream_close(oldfile);
3123 }
3124 php_stream_close(newfile);
3125 if (error) {
3126 spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname);
3127 }
3128 return EOF;
3129 #endif
3130 default: {
3131 char *digest = NULL;
3132 int digest_len;
3133
3134 if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error)) {
3135 if (error) {
3136 char *save = *error;
3137 spprintf(error, 0, "phar error: unable to write signature: %s", save);
3138 efree(save);
3139 }
3140 if (digest) {
3141 efree(digest);
3142 }
3143 if (closeoldfile) {
3144 php_stream_close(oldfile);
3145 }
3146 php_stream_close(newfile);
3147 return EOF;
3148 }
3149
3150 php_stream_write(newfile, digest, digest_len);
3151 efree(digest);
3152 if (phar->sig_flags == PHAR_SIG_OPENSSL) {
3153 phar_set_32(sig_buf, digest_len);
3154 php_stream_write(newfile, sig_buf, 4);
3155 }
3156 break;
3157 }
3158 }
3159 phar_set_32(sig_buf, phar->sig_flags);
3160 php_stream_write(newfile, sig_buf, 4);
3161 php_stream_write(newfile, "GBMB", 4);
3162 }
3163
3164 /* finally, close the temp file, rename the original phar,
3165 move the temp to the old phar, unlink the old phar, and reload it into memory
3166 */
3167 if (phar->fp && free_fp) {
3168 php_stream_close(phar->fp);
3169 }
3170
3171 if (phar->ufp) {
3172 if (free_ufp) {
3173 php_stream_close(phar->ufp);
3174 }
3175 phar->ufp = NULL;
3176 }
3177
3178 if (closeoldfile) {
3179 php_stream_close(oldfile);
3180 }
3181
3182 phar->internal_file_start = halt_offset + manifest_len + 4;
3183 phar->halt_offset = halt_offset;
3184 phar->is_brandnew = 0;
3185
3186 php_stream_rewind(newfile);
3187
3188 if (phar->donotflush) {
3189 /* deferred flush */
3190 phar->fp = newfile;
3191 } else {
3192 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
3193 if (!phar->fp) {
3194 phar->fp = newfile;
3195 if (error) {
3196 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
3197 }
3198 return EOF;
3199 }
3200
3201 if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
3202 /* to properly compress, we have to tell zlib to add a zlib header */
3203 zval filterparams;
3204
3205 array_init(&filterparams);
3206 add_assoc_long(&filterparams, "window", MAX_WBITS+16);
3207 filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp));
3208 zval_dtor(&filterparams);
3209
3210 if (!filter) {
3211 if (error) {
3212 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);
3213 }
3214 return EOF;
3215 }
3216
3217 php_stream_filter_append(&phar->fp->writefilters, filter);
3218 php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3219 php_stream_filter_flush(filter, 1);
3220 php_stream_filter_remove(filter, 1);
3221 php_stream_close(phar->fp);
3222 /* use the temp stream as our base */
3223 phar->fp = newfile;
3224 } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
3225 filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp));
3226 php_stream_filter_append(&phar->fp->writefilters, filter);
3227 php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3228 php_stream_filter_flush(filter, 1);
3229 php_stream_filter_remove(filter, 1);
3230 php_stream_close(phar->fp);
3231 /* use the temp stream as our base */
3232 phar->fp = newfile;
3233 } else {
3234 php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3235 /* we could also reopen the file in "rb" mode but there is no need for that */
3236 php_stream_close(newfile);
3237 }
3238 }
3239
3240 if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
3241 if (error) {
3242 spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
3243 }
3244 return EOF;
3245 }
3246
3247 return EOF;
3248 }
3249 /* }}} */
3250
3251 #ifdef COMPILE_DL_PHAR
3252 #ifdef ZTS
3253 ZEND_TSRMLS_CACHE_DEFINE()
3254 #endif
3255 ZEND_GET_MODULE(phar)
3256 #endif
3257
3258 /* {{{ phar_functions[]
3259 *
3260 * Every user visible function must have an entry in phar_functions[].
3261 */
3262 zend_function_entry phar_functions[] = {
3263 PHP_FE_END
3264 };
3265 /* }}}*/
3266
3267 static size_t phar_zend_stream_reader(void *handle, char *buf, size_t len) /* {{{ */
3268 {
3269 return php_stream_read(phar_get_pharfp((phar_archive_data*)handle), buf, len);
3270 }
3271 /* }}} */
3272
3273 static size_t phar_zend_stream_fsizer(void *handle) /* {{{ */
3274 {
3275 return ((phar_archive_data*)handle)->halt_offset + 32;
3276 } /* }}} */
3277
3278 zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type);
3279 #define phar_orig_zend_open zend_stream_open_function
3280
3281 static zend_string *phar_resolve_path(const char *filename, int filename_len)
3282 {
3283 return phar_find_in_include_path((char *) filename, filename_len, NULL);
3284 }
3285
3286 static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type) /* {{{ */
3287 {
3288 zend_op_array *res;
3289 char *name = NULL;
3290 int failed;
3291 phar_archive_data *phar;
3292
3293 if (!file_handle || !file_handle->filename) {
3294 return phar_orig_compile_file(file_handle, type);
3295 }
3296 if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {
3297 if (SUCCESS == phar_open_from_filename((char*)file_handle->filename, strlen(file_handle->filename), NULL, 0, 0, &phar, NULL)) {
3298 if (phar->is_zip || phar->is_tar) {
3299 zend_file_handle f = *file_handle;
3300
3301 /* zip or tar-based phar */
3302 spprintf(&name, 4096, "phar://%s/%s", file_handle->filename, ".phar/stub.php");
3303 if (SUCCESS == phar_orig_zend_open((const char *)name, &f)) {
3304
3305 efree(name);
3306 name = NULL;
3307
3308 f.filename = file_handle->filename;
3309 if (f.opened_path) {
3310 efree(f.opened_path);
3311 }
3312 f.opened_path = file_handle->opened_path;
3313 f.free_filename = file_handle->free_filename;
3314
3315 switch (file_handle->type) {
3316 case ZEND_HANDLE_STREAM:
3317 case ZEND_HANDLE_MAPPED:
3318 if (file_handle->handle.stream.closer && file_handle->handle.stream.handle) {
3319 file_handle->handle.stream.closer(file_handle->handle.stream.handle);
3320 }
3321 file_handle->handle.stream.handle = NULL;
3322 break;
3323 default:
3324 break;
3325 }
3326 *file_handle = f;
3327 }
3328 } else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
3329 zend_file_handle_dtor(file_handle);
3330 /* compressed phar */
3331 file_handle->type = ZEND_HANDLE_STREAM;
3332 /* we do our own reading directly from the phar, don't change the next line */
3333 file_handle->handle.stream.handle = phar;
3334 file_handle->handle.stream.reader = phar_zend_stream_reader;
3335 file_handle->handle.stream.closer = NULL;
3336 file_handle->handle.stream.fsizer = phar_zend_stream_fsizer;
3337 file_handle->handle.stream.isatty = 0;
3338 phar->is_persistent ?
3339 php_stream_rewind(PHAR_G(cached_fp)[phar->phar_pos].fp) :
3340 php_stream_rewind(phar->fp);
3341 memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
3342 }
3343 }
3344 }
3345
3346 zend_try {
3347 failed = 0;
3348 CG(zend_lineno) = 0;
3349 res = phar_orig_compile_file(file_handle, type);
3350 } zend_catch {
3351 failed = 1;
3352 res = NULL;
3353 } zend_end_try();
3354
3355 if (name) {
3356 efree(name);
3357 }
3358
3359 if (failed) {
3360 zend_bailout();
3361 }
3362
3363 return res;
3364 }
3365 /* }}} */
3366
3367 typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int);
3368 typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
3369
3370 static void mime_type_dtor(zval *zv)
3371 {
3372 free(Z_PTR_P(zv));
3373 }
3374
3375 PHP_GINIT_FUNCTION(phar) /* {{{ */
3376 {
3377 #if defined(COMPILE_DL_PHAR) && defined(ZTS)
3378 ZEND_TSRMLS_CACHE_UPDATE();
3379 #endif
3380 phar_mime_type mime;
3381
3382 memset(phar_globals, 0, sizeof(zend_phar_globals));
3383 phar_globals->readonly = 1;
3384
3385 zend_hash_init(&phar_globals->mime_types, 0, NULL, mime_type_dtor, 1);
3386
3387 #define PHAR_SET_MIME(mimetype, ret, fileext) \
3388 mime.mime = mimetype; \
3389 mime.len = sizeof((mimetype))+1; \
3390 mime.type = ret; \
3391 zend_hash_str_add_mem(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type)); \
3392
3393 PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
3394 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
3395 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
3396 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
3397 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
3398 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
3399 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
3400 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
3401 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
3402 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
3403 PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
3404 PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
3405 PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
3406 PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
3407 PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
3408 PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
3409 PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
3410 PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
3411 PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
3412 PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
3413 PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
3414 PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
3415 PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
3416 PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
3417 PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
3418 PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
3419 PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
3420 PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
3421 PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
3422 PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
3423 PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
3424 PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
3425 PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
3426 PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
3427 PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
3428 PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
3429 PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
3430 PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
3431 PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
3432 PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
3433
3434 phar_restore_orig_functions();
3435 }
3436 /* }}} */
3437
3438 PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
3439 {
3440 zend_hash_destroy(&phar_globals->mime_types);
3441 }
3442 /* }}} */
3443
3444 PHP_MINIT_FUNCTION(phar) /* {{{ */
3445 {
3446 REGISTER_INI_ENTRIES();
3447
3448 phar_orig_compile_file = zend_compile_file;
3449 zend_compile_file = phar_compile_file;
3450
3451 phar_save_resolve_path = zend_resolve_path;
3452 zend_resolve_path = phar_resolve_path;
3453
3454 phar_object_init();
3455
3456 phar_intercept_functions_init();
3457 phar_save_orig_functions();
3458
3459 return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper);
3460 }
3461 /* }}} */
3462
3463 PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
3464 {
3465 php_unregister_url_stream_wrapper("phar");
3466
3467 phar_intercept_functions_shutdown();
3468
3469 if (zend_compile_file == phar_compile_file) {
3470 zend_compile_file = phar_orig_compile_file;
3471 }
3472
3473 if (PHAR_G(manifest_cached)) {
3474 zend_hash_destroy(&(cached_phars));
3475 zend_hash_destroy(&(cached_alias));
3476 }
3477
3478 return SUCCESS;
3479 }
3480 /* }}} */
3481
3482 void phar_request_initialize(void) /* {{{ */
3483 {
3484 if (!PHAR_G(request_init))
3485 {
3486 PHAR_G(last_phar) = NULL;
3487 PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
3488 PHAR_G(has_bz2) = zend_hash_str_exists(&module_registry, "bz2", sizeof("bz2")-1);
3489 PHAR_G(has_zlib) = zend_hash_str_exists(&module_registry, "zlib", sizeof("zlib")-1);
3490 PHAR_G(request_init) = 1;
3491 PHAR_G(request_ends) = 0;
3492 PHAR_G(request_done) = 0;
3493 zend_hash_init(&(PHAR_G(phar_fname_map)), 5, zend_get_hash_value, destroy_phar_data, 0);
3494 zend_hash_init(&(PHAR_G(phar_persist_map)), 5, zend_get_hash_value, NULL, 0);
3495 zend_hash_init(&(PHAR_G(phar_alias_map)), 5, zend_get_hash_value, NULL, 0);
3496
3497 if (PHAR_G(manifest_cached)) {
3498 phar_archive_data *pphar;
3499 phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
3500
3501 for (zend_hash_internal_pointer_reset(&cached_phars);
3502 (pphar = zend_hash_get_current_data_ptr(&cached_phars)) != NULL;
3503 zend_hash_move_forward(&cached_phars)) {
3504 stuff[pphar->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar->manifest)), sizeof(phar_entry_fp_info));
3505 }
3506
3507 PHAR_G(cached_fp) = stuff;
3508 }
3509
3510 PHAR_G(phar_SERVER_mung_list) = 0;
3511 PHAR_G(cwd) = NULL;
3512 PHAR_G(cwd_len) = 0;
3513 PHAR_G(cwd_init) = 0;
3514 }
3515 }
3516 /* }}} */
3517
3518 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
3519 {
3520 int i;
3521
3522 PHAR_G(request_ends) = 1;
3523
3524 if (PHAR_G(request_init))
3525 {
3526 phar_release_functions();
3527 zend_hash_destroy(&(PHAR_G(phar_alias_map)));
3528 PHAR_G(phar_alias_map.u.flags) = 0;
3529 zend_hash_destroy(&(PHAR_G(phar_fname_map)));
3530 PHAR_G(phar_fname_map.u.flags) = 0;
3531 zend_hash_destroy(&(PHAR_G(phar_persist_map)));
3532 PHAR_G(phar_persist_map.u.flags) = 0;
3533 PHAR_G(phar_SERVER_mung_list) = 0;
3534
3535 if (PHAR_G(cached_fp)) {
3536 for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
3537 if (PHAR_G(cached_fp)[i].fp) {
3538 php_stream_close(PHAR_G(cached_fp)[i].fp);
3539 }
3540 if (PHAR_G(cached_fp)[i].ufp) {
3541 php_stream_close(PHAR_G(cached_fp)[i].ufp);
3542 }
3543 efree(PHAR_G(cached_fp)[i].manifest);
3544 }
3545 efree(PHAR_G(cached_fp));
3546 PHAR_G(cached_fp) = 0;
3547 }
3548
3549 PHAR_G(request_init) = 0;
3550
3551 if (PHAR_G(cwd)) {
3552 efree(PHAR_G(cwd));
3553 }
3554
3555 PHAR_G(cwd) = NULL;
3556 PHAR_G(cwd_len) = 0;
3557 PHAR_G(cwd_init) = 0;
3558 }
3559
3560 PHAR_G(request_done) = 1;
3561 return SUCCESS;
3562 }
3563 /* }}} */
3564
3565 PHP_MINFO_FUNCTION(phar) /* {{{ */
3566 {
3567 phar_request_initialize();
3568 php_info_print_table_start();
3569 php_info_print_table_header(2, "Phar: PHP Archive support", "enabled");
3570 php_info_print_table_row(2, "Phar EXT version", PHP_PHAR_VERSION);
3571 php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
3572 php_info_print_table_row(2, "SVN revision", "$Id: 308c1e92e8ad12e51f5db846d3366fdf3487eb21 $");
3573 php_info_print_table_row(2, "Phar-based phar archives", "enabled");
3574 php_info_print_table_row(2, "Tar-based phar archives", "enabled");
3575 php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
3576
3577 if (PHAR_G(has_zlib)) {
3578 php_info_print_table_row(2, "gzip compression", "enabled");
3579 } else {
3580 php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
3581 }
3582
3583 if (PHAR_G(has_bz2)) {
3584 php_info_print_table_row(2, "bzip2 compression", "enabled");
3585 } else {
3586 php_info_print_table_row(2, "bzip2 compression", "disabled (install pecl/bz2)");
3587 }
3588 #ifdef PHAR_HAVE_OPENSSL
3589 php_info_print_table_row(2, "Native OpenSSL support", "enabled");
3590 #else
3591 if (zend_hash_str_exists(&module_registry, "openssl", sizeof("openssl")-1)) {
3592 php_info_print_table_row(2, "OpenSSL support", "enabled");
3593 } else {
3594 php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
3595 }
3596 #endif
3597 php_info_print_table_end();
3598
3599 php_info_print_box_start(0);
3600 PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
3601 PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3602 PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
3603 PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3604 PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
3605 php_info_print_box_end();
3606
3607 DISPLAY_INI_ENTRIES();
3608 }
3609 /* }}} */
3610
3611 /* {{{ phar_module_entry
3612 */
3613 static const zend_module_dep phar_deps[] = {
3614 ZEND_MOD_OPTIONAL("apc")
3615 ZEND_MOD_OPTIONAL("bz2")
3616 ZEND_MOD_OPTIONAL("openssl")
3617 ZEND_MOD_OPTIONAL("zlib")
3618 ZEND_MOD_OPTIONAL("standard")
3619 #if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
3620 ZEND_MOD_REQUIRED("hash")
3621 #endif
3622 #if HAVE_SPL
3623 ZEND_MOD_REQUIRED("spl")
3624 #endif
3625 ZEND_MOD_END
3626 };
3627
3628 zend_module_entry phar_module_entry = {
3629 STANDARD_MODULE_HEADER_EX, NULL,
3630 phar_deps,
3631 "Phar",
3632 phar_functions,
3633 PHP_MINIT(phar),
3634 PHP_MSHUTDOWN(phar),
3635 NULL,
3636 PHP_RSHUTDOWN(phar),
3637 PHP_MINFO(phar),
3638 PHP_PHAR_VERSION,
3639 PHP_MODULE_GLOBALS(phar), /* globals descriptor */
3640 PHP_GINIT(phar), /* globals ctor */
3641 PHP_GSHUTDOWN(phar), /* globals dtor */
3642 NULL, /* post deactivate */
3643 STANDARD_MODULE_PROPERTIES_EX
3644 };
3645 /* }}} */
3646
3647 /*
3648 * Local variables:
3649 * tab-width: 4
3650 * c-basic-offset: 4
3651 * End:
3652 * vim600: noet sw=4 ts=4 fdm=marker
3653 * vim<600: noet sw=4 ts=4
3654 */
3655