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