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