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