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