1: /*
2: +----------------------------------------------------------------------+
3: | TAR archive support for Phar |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 2005-2014 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: Dmitry Stogov <dmitry@zend.com> |
16: | Gregory Beaver <cellog@php.net> |
17: +----------------------------------------------------------------------+
18: */
19:
20: #include "phar_internal.h"
21:
22: static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */
23: {
24: php_uint32 num = 0;
25: int i = 0;
26:
27: while (i < len && buf[i] == ' ') {
28: ++i;
29: }
30:
31: while (i < len && buf[i] >= '0' && buf[i] <= '7') {
32: num = num * 8 + (buf[i] - '0');
33: ++i;
34: }
35:
36: return num;
37: }
38: /* }}} */
39:
40: /* adapted from format_octal() in libarchive
41: *
42: * Copyright (c) 2003-2009 Tim Kientzle
43: * All rights reserved.
44: *
45: * Redistribution and use in source and binary forms, with or without
46: * modification, are permitted provided that the following conditions
47: * are met:
48: * 1. Redistributions of source code must retain the above copyright
49: * notice, this list of conditions and the following disclaimer.
50: * 2. Redistributions in binary form must reproduce the above copyright
51: * notice, this list of conditions and the following disclaimer in the
52: * documentation and/or other materials provided with the distribution.
53: *
54: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
55: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
56: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
57: * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
58: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
59: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
60: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
61: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
62: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
63: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64: */
65: static int phar_tar_octal(char *buf, php_uint32 val, int len) /* {{{ */
66: {
67: char *p = buf;
68: int s = len;
69:
70: p += len; /* Start at the end and work backwards. */
71: while (s-- > 0) {
72: *--p = (char)('0' + (val & 7));
73: val >>= 3;
74: }
75:
76: if (val == 0)
77: return SUCCESS;
78:
79: /* If it overflowed, fill field with max value. */
80: while (len-- > 0)
81: *p++ = '7';
82:
83: return FAILURE;
84: }
85: /* }}} */
86:
87: static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */
88: {
89: php_uint32 sum = 0;
90: char *end = buf + len;
91:
92: while (buf != end) {
93: sum += (unsigned char)*buf;
94: ++buf;
95: }
96: return sum;
97: }
98: /* }}} */
99:
100: int phar_is_tar(char *buf, char *fname) /* {{{ */
101: {
102: tar_header *header = (tar_header *) buf;
103: php_uint32 checksum = phar_tar_number(header->checksum, sizeof(header->checksum));
104: php_uint32 ret;
105: char save[sizeof(header->checksum)];
106:
107: /* assume that the first filename in a tar won't begin with <?php */
108: if (!strncmp(buf, "<?php", sizeof("<?php")-1)) {
109: return 0;
110: }
111:
112: memcpy(save, header->checksum, sizeof(header->checksum));
113: memset(header->checksum, ' ', sizeof(header->checksum));
114: ret = (checksum == phar_tar_checksum(buf, 512));
115: memcpy(header->checksum, save, sizeof(header->checksum));
116: if (!ret && strstr(fname, ".tar")) {
117: /* probably a corrupted tar - so we will pretend it is one */
118: return 1;
119: }
120: return ret;
121: }
122: /* }}} */
123:
124: int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
125: {
126: phar_archive_data *phar;
127: int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error TSRMLS_CC);
128:
129: if (FAILURE == ret) {
130: return FAILURE;
131: }
132:
133: if (pphar) {
134: *pphar = phar;
135: }
136:
137: phar->is_data = is_data;
138:
139: if (phar->is_tar) {
140: return ret;
141: }
142:
143: if (phar->is_brandnew) {
144: phar->is_tar = 1;
145: phar->is_zip = 0;
146: phar->internal_file_start = 0;
147: return SUCCESS;
148: }
149:
150: /* we've reached here - the phar exists and is a regular phar */
151: if (error) {
152: spprintf(error, 4096, "phar tar error: \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a tar-based phar", fname);
153: }
154: return FAILURE;
155: }
156: /* }}} */
157:
158: static int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) /* {{{ */
159: {
160: char *metadata;
161: size_t save = php_stream_tell(fp), read;
162: phar_entry_info *mentry;
163:
164: metadata = (char *) safe_emalloc(1, entry->uncompressed_filesize, 1);
165:
166: read = php_stream_read(fp, metadata, entry->uncompressed_filesize);
167: if (read != entry->uncompressed_filesize) {
168: efree(metadata);
169: php_stream_seek(fp, save, SEEK_SET);
170: return FAILURE;
171: }
172:
173: if (phar_parse_metadata(&metadata, &entry->metadata, entry->uncompressed_filesize TSRMLS_CC) == FAILURE) {
174: /* if not valid serialized data, it is a regular string */
175: efree(metadata);
176: php_stream_seek(fp, save, SEEK_SET);
177: return FAILURE;
178: }
179:
180: if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) {
181: entry->phar->metadata = entry->metadata;
182: entry->metadata = NULL;
183: } else if (entry->filename_len >= sizeof(".phar/.metadata/") + sizeof("/.metadata.bin") - 1 && SUCCESS == zend_hash_find(&(entry->phar->manifest), entry->filename + sizeof(".phar/.metadata/") - 1, entry->filename_len - (sizeof("/.metadata.bin") - 1 + sizeof(".phar/.metadata/") - 1), (void *)&mentry)) {
184: /* transfer this metadata to the entry it refers */
185: mentry->metadata = entry->metadata;
186: entry->metadata = NULL;
187: }
188:
189: efree(metadata);
190: php_stream_seek(fp, save, SEEK_SET);
191: return SUCCESS;
192: }
193: /* }}} */
194:
195: int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */
196: {
197: char buf[512], *actual_alias = NULL, *p;
198: phar_entry_info entry = {0};
199: size_t pos = 0, read, totalsize;
200: tar_header *hdr;
201: php_uint32 sum1, sum2, size, old;
202: phar_archive_data *myphar, **actual;
203: int last_was_longlink = 0;
204:
205: if (error) {
206: *error = NULL;
207: }
208:
209: php_stream_seek(fp, 0, SEEK_END);
210: totalsize = php_stream_tell(fp);
211: php_stream_seek(fp, 0, SEEK_SET);
212: read = php_stream_read(fp, buf, sizeof(buf));
213:
214: if (read != sizeof(buf)) {
215: if (error) {
216: spprintf(error, 4096, "phar error: \"%s\" is not a tar file or is truncated", fname);
217: }
218: php_stream_close(fp);
219: return FAILURE;
220: }
221:
222: hdr = (tar_header*)buf;
223: old = (memcmp(hdr->magic, "ustar", sizeof("ustar")-1) != 0);
224:
225: myphar = (phar_archive_data *) pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
226: myphar->is_persistent = PHAR_G(persist);
227: /* estimate number of entries, can't be certain with tar files */
228: zend_hash_init(&myphar->manifest, 2 + (totalsize >> 12),
229: zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)myphar->is_persistent);
230: zend_hash_init(&myphar->mounted_dirs, 5,
231: zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent);
232: zend_hash_init(&myphar->virtual_dirs, 4 + (totalsize >> 11),
233: zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent);
234: myphar->is_tar = 1;
235: /* remember whether this entire phar was compressed with gz/bzip2 */
236: myphar->flags = compression;
237:
238: entry.is_tar = 1;
239: entry.is_crc_checked = 1;
240: entry.phar = myphar;
241: pos += sizeof(buf);
242:
243: do {
244: phar_entry_info *newentry;
245:
246: pos = php_stream_tell(fp);
247: hdr = (tar_header*) buf;
248: sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum));
249: if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) {
250: break;
251: }
252: memset(hdr->checksum, ' ', sizeof(hdr->checksum));
253: sum2 = phar_tar_checksum(buf, old?sizeof(old_tar_header):sizeof(tar_header));
254:
255: size = entry.uncompressed_filesize = entry.compressed_filesize =
256: phar_tar_number(hdr->size, sizeof(hdr->size));
257:
258: if (((!old && hdr->prefix[0] == 0) || old) && strlen(hdr->name) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
259: off_t curloc;
260:
261: if (size > 511) {
262: if (error) {
263: spprintf(error, 4096, "phar error: tar-based phar \"%s\" has signature that is larger than 511 bytes, cannot process", fname);
264: }
265: bail:
266: php_stream_close(fp);
267: phar_destroy_phar_data(myphar TSRMLS_CC);
268: return FAILURE;
269: }
270: curloc = php_stream_tell(fp);
271: read = php_stream_read(fp, buf, size);
272: if (read != size) {
273: if (error) {
274: spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be read", fname);
275: }
276: goto bail;
277: }
278: #ifdef WORDS_BIGENDIAN
279: # define PHAR_GET_32(buffer) \
280: (((((unsigned char*)(buffer))[3]) << 24) \
281: | ((((unsigned char*)(buffer))[2]) << 16) \
282: | ((((unsigned char*)(buffer))[1]) << 8) \
283: | (((unsigned char*)(buffer))[0]))
284: #else
285: # define PHAR_GET_32(buffer) (php_uint32) *(buffer)
286: #endif
287: myphar->sig_flags = PHAR_GET_32(buf);
288: if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, myphar->sig_flags, buf + 8, size - 8, fname, &myphar->signature, &myphar->sig_len, error TSRMLS_CC)) {
289: if (error) {
290: char *save = *error;
291: spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be verified: %s", fname, save);
292: efree(save);
293: }
294: goto bail;
295: }
296: php_stream_seek(fp, curloc + 512, SEEK_SET);
297: /* signature checked out, let's ensure this is the last file in the phar */
298: if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) {
299: /* this is not good enough - seek succeeds even on truncated tars */
300: php_stream_seek(fp, 512, SEEK_CUR);
301: if ((uint)php_stream_tell(fp) > totalsize) {
302: if (error) {
303: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
304: }
305: php_stream_close(fp);
306: phar_destroy_phar_data(myphar TSRMLS_CC);
307: return FAILURE;
308: }
309: }
310:
311: read = php_stream_read(fp, buf, sizeof(buf));
312:
313: if (read != sizeof(buf)) {
314: if (error) {
315: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
316: }
317: php_stream_close(fp);
318: phar_destroy_phar_data(myphar TSRMLS_CC);
319: return FAILURE;
320: }
321:
322: hdr = (tar_header*) buf;
323: sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum));
324:
325: if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) {
326: break;
327: }
328:
329: if (error) {
330: spprintf(error, 4096, "phar error: \"%s\" has entries after signature, invalid phar", fname);
331: }
332:
333: goto bail;
334: }
335:
336: if (!last_was_longlink && hdr->typeflag == 'L') {
337: last_was_longlink = 1;
338: /* support the ././@LongLink system for storing long filenames */
339: entry.filename_len = entry.uncompressed_filesize;
340:
341: /* Check for overflow - bug 61065 */
342: if (entry.filename_len == UINT_MAX) {
343: if (error) {
344: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (invalid entry size)", fname);
345: }
346: php_stream_close(fp);
347: phar_destroy_phar_data(myphar TSRMLS_CC);
348: return FAILURE;
349: }
350: entry.filename = pemalloc(entry.filename_len+1, myphar->is_persistent);
351:
352: read = php_stream_read(fp, entry.filename, entry.filename_len);
353: if (read != entry.filename_len) {
354: efree(entry.filename);
355: if (error) {
356: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
357: }
358: php_stream_close(fp);
359: phar_destroy_phar_data(myphar TSRMLS_CC);
360: return FAILURE;
361: }
362: entry.filename[entry.filename_len] = '\0';
363:
364: /* skip blank stuff */
365: size = ((size+511)&~511) - size;
366:
367: /* this is not good enough - seek succeeds even on truncated tars */
368: php_stream_seek(fp, size, SEEK_CUR);
369: if ((uint)php_stream_tell(fp) > totalsize) {
370: efree(entry.filename);
371: if (error) {
372: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
373: }
374: php_stream_close(fp);
375: phar_destroy_phar_data(myphar TSRMLS_CC);
376: return FAILURE;
377: }
378:
379: read = php_stream_read(fp, buf, sizeof(buf));
380:
381: if (read != sizeof(buf)) {
382: efree(entry.filename);
383: if (error) {
384: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
385: }
386: php_stream_close(fp);
387: phar_destroy_phar_data(myphar TSRMLS_CC);
388: return FAILURE;
389: }
390: continue;
391: } else if (!last_was_longlink && !old && hdr->prefix[0] != 0) {
392: char name[256];
393: int i, j;
394:
395: for (i = 0; i < 155; i++) {
396: name[i] = hdr->prefix[i];
397: if (name[i] == '\0') {
398: break;
399: }
400: }
401: name[i++] = '/';
402: for (j = 0; j < 100; j++) {
403: name[i+j] = hdr->name[j];
404: if (name[i+j] == '\0') {
405: break;
406: }
407: }
408:
409: entry.filename_len = i+j;
410:
411: if (name[entry.filename_len - 1] == '/') {
412: /* some tar programs store directories with trailing slash */
413: entry.filename_len--;
414: }
415: entry.filename = pestrndup(name, entry.filename_len, myphar->is_persistent);
416: } else if (!last_was_longlink) {
417: int i;
418:
419: /* calculate strlen, which can be no longer than 100 */
420: for (i = 0; i < 100; i++) {
421: if (hdr->name[i] == '\0') {
422: break;
423: }
424: }
425: entry.filename_len = i;
426: entry.filename = pestrndup(hdr->name, i, myphar->is_persistent);
427:
428: if (entry.filename[entry.filename_len - 1] == '/') {
429: /* some tar programs store directories with trailing slash */
430: entry.filename[entry.filename_len - 1] = '\0';
431: entry.filename_len--;
432: }
433: }
434: last_was_longlink = 0;
435:
436: phar_add_virtual_dirs(myphar, entry.filename, entry.filename_len TSRMLS_CC);
437:
438: if (sum1 != sum2) {
439: if (error) {
440: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename);
441: }
442: pefree(entry.filename, myphar->is_persistent);
443: php_stream_close(fp);
444: phar_destroy_phar_data(myphar TSRMLS_CC);
445: return FAILURE;
446: }
447:
448: entry.tar_type = ((old & (hdr->typeflag == '\0')) ? TAR_FILE : hdr->typeflag);
449: entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */
450: entry.fp_type = PHAR_FP;
451: entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK;
452: entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime));
453: entry.is_persistent = myphar->is_persistent;
454: #ifndef S_ISDIR
455: #define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
456: #endif
457: if (old && entry.tar_type == TAR_FILE && S_ISDIR(entry.flags)) {
458: entry.tar_type = TAR_DIR;
459: }
460:
461: if (entry.tar_type == TAR_DIR) {
462: entry.is_dir = 1;
463: } else {
464: entry.is_dir = 0;
465: }
466:
467: entry.link = NULL;
468:
469: if (entry.tar_type == TAR_LINK) {
470: if (!zend_hash_exists(&myphar->manifest, hdr->linkname, strlen(hdr->linkname))) {
471: if (error) {
472: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%s\"", fname, hdr->linkname);
473: }
474: pefree(entry.filename, entry.is_persistent);
475: php_stream_close(fp);
476: phar_destroy_phar_data(myphar TSRMLS_CC);
477: return FAILURE;
478: }
479: entry.link = estrdup(hdr->linkname);
480: } else if (entry.tar_type == TAR_SYMLINK) {
481: entry.link = estrdup(hdr->linkname);
482: }
483: phar_set_inode(&entry TSRMLS_CC);
484: zend_hash_add(&myphar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), (void **) &newentry);
485:
486: if (entry.is_persistent) {
487: ++entry.manifest_pos;
488: }
489:
490: if (entry.filename_len >= sizeof(".phar/.metadata")-1 && !memcmp(entry.filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) {
491: if (FAILURE == phar_tar_process_metadata(newentry, fp TSRMLS_CC)) {
492: if (error) {
493: spprintf(error, 4096, "phar error: tar-based phar \"%s\" has invalid metadata in magic file \"%s\"", fname, entry.filename);
494: }
495: php_stream_close(fp);
496: phar_destroy_phar_data(myphar TSRMLS_CC);
497: return FAILURE;
498: }
499: }
500:
501: if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
502: /* found explicit alias */
503: if (size > 511) {
504: if (error) {
505: spprintf(error, 4096, "phar error: tar-based phar \"%s\" has alias that is larger than 511 bytes, cannot process", fname);
506: }
507: php_stream_close(fp);
508: phar_destroy_phar_data(myphar TSRMLS_CC);
509: return FAILURE;
510: }
511:
512: read = php_stream_read(fp, buf, size);
513:
514: if (read == size) {
515: buf[size] = '\0';
516: if (!phar_validate_alias(buf, size)) {
517: if (size > 50) {
518: buf[50] = '.';
519: buf[51] = '.';
520: buf[52] = '.';
521: buf[53] = '\0';
522: }
523:
524: if (error) {
525: spprintf(error, 4096, "phar error: invalid alias \"%s\" in tar-based phar \"%s\"", buf, fname);
526: }
527:
528: php_stream_close(fp);
529: phar_destroy_phar_data(myphar TSRMLS_CC);
530: return FAILURE;
531: }
532:
533: actual_alias = pestrndup(buf, size, myphar->is_persistent);
534: myphar->alias = actual_alias;
535: myphar->alias_len = size;
536: php_stream_seek(fp, pos, SEEK_SET);
537: } else {
538: if (error) {
539: spprintf(error, 4096, "phar error: Unable to read alias from tar-based phar \"%s\"", fname);
540: }
541:
542: php_stream_close(fp);
543: phar_destroy_phar_data(myphar TSRMLS_CC);
544: return FAILURE;
545: }
546: }
547:
548: size = (size+511)&~511;
549:
550: if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) {
551: /* this is not good enough - seek succeeds even on truncated tars */
552: php_stream_seek(fp, size, SEEK_CUR);
553: if ((uint)php_stream_tell(fp) > totalsize) {
554: if (error) {
555: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
556: }
557: php_stream_close(fp);
558: phar_destroy_phar_data(myphar TSRMLS_CC);
559: return FAILURE;
560: }
561: }
562:
563: read = php_stream_read(fp, buf, sizeof(buf));
564:
565: if (read != sizeof(buf)) {
566: if (error) {
567: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
568: }
569: php_stream_close(fp);
570: phar_destroy_phar_data(myphar TSRMLS_CC);
571: return FAILURE;
572: }
573: } while (read != 0);
574:
575: if (zend_hash_exists(&(myphar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
576: myphar->is_data = 0;
577: } else {
578: myphar->is_data = 1;
579: }
580:
581: /* ensure signature set */
582: if (!myphar->is_data && PHAR_G(require_hash) && !myphar->signature) {
583: php_stream_close(fp);
584: phar_destroy_phar_data(myphar TSRMLS_CC);
585: if (error) {
586: spprintf(error, 0, "tar-based phar \"%s\" does not have a signature", fname);
587: }
588: return FAILURE;
589: }
590:
591: myphar->fname = pestrndup(fname, fname_len, myphar->is_persistent);
592: #ifdef PHP_WIN32
593: phar_unixify_path_separators(myphar->fname, fname_len);
594: #endif
595: myphar->fname_len = fname_len;
596: myphar->fp = fp;
597: p = strrchr(myphar->fname, '/');
598:
599: if (p) {
600: myphar->ext = memchr(p, '.', (myphar->fname + fname_len) - p);
601: if (myphar->ext == p) {
602: myphar->ext = memchr(p + 1, '.', (myphar->fname + fname_len) - p - 1);
603: }
604: if (myphar->ext) {
605: myphar->ext_len = (myphar->fname + fname_len) - myphar->ext;
606: }
607: }
608:
609: phar_request_initialize(TSRMLS_C);
610:
611: if (SUCCESS != zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len, (void*)&myphar, sizeof(phar_archive_data*), (void **)&actual)) {
612: if (error) {
613: spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\" to phar registry", fname);
614: }
615: php_stream_close(fp);
616: phar_destroy_phar_data(myphar TSRMLS_CC);
617: return FAILURE;
618: }
619:
620: myphar = *actual;
621:
622: if (actual_alias) {
623: phar_archive_data **fd_ptr;
624:
625: myphar->is_temporary_alias = 0;
626:
627: if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void **)&fd_ptr)) {
628: if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, myphar->alias_len TSRMLS_CC)) {
629: if (error) {
630: spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname);
631: }
632: zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len);
633: return FAILURE;
634: }
635: }
636:
637: zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL);
638: } else {
639: phar_archive_data **fd_ptr;
640:
641: if (alias_len) {
642: if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
643: if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
644: if (error) {
645: spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname);
646: }
647: zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len);
648: return FAILURE;
649: }
650: }
651: zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL);
652: myphar->alias = pestrndup(alias, alias_len, myphar->is_persistent);
653: myphar->alias_len = alias_len;
654: } else {
655: myphar->alias = pestrndup(myphar->fname, fname_len, myphar->is_persistent);
656: myphar->alias_len = fname_len;
657: }
658:
659: myphar->is_temporary_alias = 1;
660: }
661:
662: if (pphar) {
663: *pphar = myphar;
664: }
665:
666: return SUCCESS;
667: }
668: /* }}} */
669:
670: struct _phar_pass_tar_info {
671: php_stream *old;
672: php_stream *new;
673: int free_fp;
674: int free_ufp;
675: char **error;
676: };
677:
678: static int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */
679: {
680: tar_header header;
681: size_t pos;
682: phar_entry_info *entry = (phar_entry_info *) pDest;
683: struct _phar_pass_tar_info *fp = (struct _phar_pass_tar_info *)argument;
684: char padding[512];
685:
686: if (entry->is_mounted) {
687: return ZEND_HASH_APPLY_KEEP;
688: }
689:
690: if (entry->is_deleted) {
691: if (entry->fp_refcount <= 0) {
692: return ZEND_HASH_APPLY_REMOVE;
693: } else {
694: /* we can't delete this in-memory until it is closed */
695: return ZEND_HASH_APPLY_KEEP;
696: }
697: }
698:
699: phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len TSRMLS_CC);
700: memset((char *) &header, 0, sizeof(header));
701:
702: if (entry->filename_len > 100) {
703: char *boundary;
704: if (entry->filename_len > 256) {
705: if (fp->error) {
706: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too long for tar file format", entry->phar->fname, entry->filename);
707: }
708: return ZEND_HASH_APPLY_STOP;
709: }
710: boundary = entry->filename + entry->filename_len - 101;
711: while (*boundary && *boundary != '/') {
712: ++boundary;
713: }
714: if (!*boundary || ((boundary - entry->filename) > 155)) {
715: if (fp->error) {
716: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too long for tar file format", entry->phar->fname, entry->filename);
717: }
718: return ZEND_HASH_APPLY_STOP;
719: }
720: memcpy(header.prefix, entry->filename, boundary - entry->filename);
721: memcpy(header.name, boundary + 1, entry->filename_len - (boundary + 1 - entry->filename));
722: } else {
723: memcpy(header.name, entry->filename, entry->filename_len);
724: }
725:
726: phar_tar_octal(header.mode, entry->flags & PHAR_ENT_PERM_MASK, sizeof(header.mode)-1);
727:
728: if (FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) {
729: if (fp->error) {
730: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too large for tar file format", entry->phar->fname, entry->filename);
731: }
732: return ZEND_HASH_APPLY_STOP;
733: }
734:
735: if (FAILURE == phar_tar_octal(header.mtime, entry->timestamp, sizeof(header.mtime)-1)) {
736: if (fp->error) {
737: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, file modification time of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename);
738: }
739: return ZEND_HASH_APPLY_STOP;
740: }
741:
742: /* calc checksum */
743: header.typeflag = entry->tar_type;
744:
745: if (entry->link) {
746: strncpy(header.linkname, entry->link, strlen(entry->link));
747: }
748:
749: strncpy(header.magic, "ustar", sizeof("ustar")-1);
750: strncpy(header.version, "00", sizeof("00")-1);
751: strncpy(header.checksum, " ", sizeof(" ")-1);
752: entry->crc32 = phar_tar_checksum((char *)&header, sizeof(header));
753:
754: if (FAILURE == phar_tar_octal(header.checksum, entry->crc32, sizeof(header.checksum)-1)) {
755: if (fp->error) {
756: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, checksum of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename);
757: }
758: return ZEND_HASH_APPLY_STOP;
759: }
760:
761: /* write header */
762: entry->header_offset = php_stream_tell(fp->new);
763:
764: if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) {
765: if (fp->error) {
766: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for file \"%s\" could not be written", entry->phar->fname, entry->filename);
767: }
768: return ZEND_HASH_APPLY_STOP;
769: }
770:
771: pos = php_stream_tell(fp->new); /* save start of file within tar */
772:
773: /* write contents */
774: if (entry->uncompressed_filesize) {
775: if (FAILURE == phar_open_entry_fp(entry, fp->error, 0 TSRMLS_CC)) {
776: return ZEND_HASH_APPLY_STOP;
777: }
778:
779: if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
780: if (fp->error) {
781: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename);
782: }
783: return ZEND_HASH_APPLY_STOP;
784: }
785:
786: if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), fp->new, entry->uncompressed_filesize, NULL)) {
787: if (fp->error) {
788: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename);
789: }
790: return ZEND_HASH_APPLY_STOP;
791: }
792:
793: memset(padding, 0, 512);
794: php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize);
795: }
796:
797: if (!entry->is_modified && entry->fp_refcount) {
798: /* open file pointers refer to this fp, do not free the stream */
799: switch (entry->fp_type) {
800: case PHAR_FP:
801: fp->free_fp = 0;
802: break;
803: case PHAR_UFP:
804: fp->free_ufp = 0;
805: default:
806: break;
807: }
808: }
809:
810: entry->is_modified = 0;
811:
812: if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
813: if (!entry->fp_refcount) {
814: php_stream_close(entry->fp);
815: }
816: entry->fp = NULL;
817: }
818:
819: entry->fp_type = PHAR_FP;
820:
821: /* note new location within tar */
822: entry->offset = entry->offset_abs = pos;
823: return ZEND_HASH_APPLY_KEEP;
824: }
825: /* }}} */
826:
827: int phar_tar_setmetadata(zval *metadata, phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */
828: {
829: php_serialize_data_t metadata_hash;
830:
831: if (entry->metadata_str.c) {
832: smart_str_free(&entry->metadata_str);
833: }
834:
835: entry->metadata_str.c = 0;
836: entry->metadata_str.len = 0;
837: PHP_VAR_SERIALIZE_INIT(metadata_hash);
838: php_var_serialize(&entry->metadata_str, &metadata, &metadata_hash TSRMLS_CC);
839: PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
840: entry->uncompressed_filesize = entry->compressed_filesize = entry->metadata_str.len;
841:
842: if (entry->fp && entry->fp_type == PHAR_MOD) {
843: php_stream_close(entry->fp);
844: }
845:
846: entry->fp_type = PHAR_MOD;
847: entry->is_modified = 1;
848: entry->fp = php_stream_fopen_tmpfile();
849: entry->offset = entry->offset_abs = 0;
850: if (entry->fp == NULL) {
851: spprintf(error, 0, "phar error: unable to create temporary file");
852: return -1;
853: }
854: if (entry->metadata_str.len != php_stream_write(entry->fp, entry->metadata_str.c, entry->metadata_str.len)) {
855: spprintf(error, 0, "phar tar error: unable to write metadata to magic metadata file \"%s\"", entry->filename);
856: zend_hash_del(&(entry->phar->manifest), entry->filename, entry->filename_len);
857: return ZEND_HASH_APPLY_STOP;
858: }
859:
860: return ZEND_HASH_APPLY_KEEP;
861: }
862: /* }}} */
863:
864: static int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) /* {{{ */
865: {
866: int lookfor_len;
867: struct _phar_pass_tar_info *i = (struct _phar_pass_tar_info *)argument;
868: char *lookfor, **error = i->error;
869: phar_entry_info *entry = (phar_entry_info *)pDest, *metadata, newentry = {0};
870:
871: if (entry->filename_len >= sizeof(".phar/.metadata") && !memcmp(entry->filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) {
872: if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) {
873: return phar_tar_setmetadata(entry->phar->metadata, entry, error TSRMLS_CC);
874: }
875: /* search for the file this metadata entry references */
876: if (entry->filename_len >= sizeof(".phar/.metadata/") + sizeof("/.metadata.bin") - 1 && !zend_hash_exists(&(entry->phar->manifest), entry->filename + sizeof(".phar/.metadata/") - 1, entry->filename_len - (sizeof("/.metadata.bin") - 1 + sizeof(".phar/.metadata/") - 1))) {
877: /* this is orphaned metadata, erase it */
878: return ZEND_HASH_APPLY_REMOVE;
879: }
880: /* we can keep this entry, the file that refers to it exists */
881: return ZEND_HASH_APPLY_KEEP;
882: }
883:
884: if (!entry->is_modified) {
885: return ZEND_HASH_APPLY_KEEP;
886: }
887:
888: /* now we are dealing with regular files, so look for metadata */
889: lookfor_len = spprintf(&lookfor, 0, ".phar/.metadata/%s/.metadata.bin", entry->filename);
890:
891: if (!entry->metadata) {
892: zend_hash_del(&(entry->phar->manifest), lookfor, lookfor_len);
893: efree(lookfor);
894: return ZEND_HASH_APPLY_KEEP;
895: }
896:
897: if (SUCCESS == zend_hash_find(&(entry->phar->manifest), lookfor, lookfor_len, (void **)&metadata)) {
898: int ret;
899: ret = phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC);
900: efree(lookfor);
901: return ret;
902: }
903:
904: newentry.filename = lookfor;
905: newentry.filename_len = lookfor_len;
906: newentry.phar = entry->phar;
907: newentry.tar_type = TAR_FILE;
908: newentry.is_tar = 1;
909:
910: if (SUCCESS != zend_hash_add(&(entry->phar->manifest), lookfor, lookfor_len, (void *)&newentry, sizeof(phar_entry_info), (void **)&metadata)) {
911: efree(lookfor);
912: spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for file \"%s\"", entry->filename);
913: return ZEND_HASH_APPLY_STOP;
914: }
915:
916: return phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC);
917: }
918: /* }}} */
919:
920: int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */
921: {
922: phar_entry_info entry = {0};
923: static const char newstub[] = "<?php // tar-based phar archive stub file\n__HALT_COMPILER();";
924: php_stream *oldfile, *newfile, *stubfile;
925: int closeoldfile, free_user_stub, signature_length;
926: struct _phar_pass_tar_info pass;
927: char *buf, *signature, *tmp, sigbuf[8];
928: char halt_stub[] = "__HALT_COMPILER();";
929:
930: entry.flags = PHAR_ENT_PERM_DEF_FILE;
931: entry.timestamp = time(NULL);
932: entry.is_modified = 1;
933: entry.is_crc_checked = 1;
934: entry.is_tar = 1;
935: entry.tar_type = '0';
936: entry.phar = phar;
937: entry.fp_type = PHAR_MOD;
938:
939: if (phar->is_persistent) {
940: if (error) {
941: spprintf(error, 0, "internal error: attempt to flush cached tar-based phar \"%s\"", phar->fname);
942: }
943: return EOF;
944: }
945:
946: if (phar->is_data) {
947: goto nostub;
948: }
949:
950: /* set alias */
951: if (!phar->is_temporary_alias && phar->alias_len) {
952: entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
953: entry.filename_len = sizeof(".phar/alias.txt")-1;
954: entry.fp = php_stream_fopen_tmpfile();
955: if (entry.fp == NULL) {
956: spprintf(error, 0, "phar error: unable to create temporary file");
957: return -1;
958: }
959: if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
960: if (error) {
961: spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
962: }
963: return EOF;
964: }
965:
966: entry.uncompressed_filesize = phar->alias_len;
967:
968: if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
969: if (error) {
970: spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
971: }
972: return EOF;
973: }
974: } else {
975: zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
976: }
977:
978: /* set stub */
979: if (user_stub && !defaultstub) {
980: char *pos;
981: if (len < 0) {
982: /* resource passed in */
983: if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
984: if (error) {
985: spprintf(error, 0, "unable to access resource to copy stub to new tar-based phar \"%s\"", phar->fname);
986: }
987: return EOF;
988: }
989: if (len == -1) {
990: len = PHP_STREAM_COPY_ALL;
991: } else {
992: len = -len;
993: }
994: user_stub = 0;
995:
996: if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
997: if (error) {
998: spprintf(error, 0, "unable to read resource to copy stub to new tar-based phar \"%s\"", phar->fname);
999: }
1000: return EOF;
1001: }
1002: free_user_stub = 1;
1003: } else {
1004: free_user_stub = 0;
1005: }
1006:
1007: tmp = estrndup(user_stub, len);
1008: if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1009: efree(tmp);
1010: if (error) {
1011: spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname);
1012: }
1013: if (free_user_stub) {
1014: efree(user_stub);
1015: }
1016: return EOF;
1017: }
1018: pos = user_stub + (pos - tmp);
1019: efree(tmp);
1020:
1021: len = pos - user_stub + 18;
1022: entry.fp = php_stream_fopen_tmpfile();
1023: if (entry.fp == NULL) {
1024: spprintf(error, 0, "phar error: unable to create temporary file");
1025: return EOF;
1026: }
1027: entry.uncompressed_filesize = len + 5;
1028:
1029: if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1030: || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1031: if (error) {
1032: spprintf(error, 0, "unable to create stub from string in new tar-based phar \"%s\"", phar->fname);
1033: }
1034: if (free_user_stub) {
1035: efree(user_stub);
1036: }
1037: php_stream_close(entry.fp);
1038: return EOF;
1039: }
1040:
1041: entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1042: entry.filename_len = sizeof(".phar/stub.php")-1;
1043: zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
1044:
1045: if (free_user_stub) {
1046: efree(user_stub);
1047: }
1048: } else {
1049: /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1050: entry.fp = php_stream_fopen_tmpfile();
1051: if (entry.fp == NULL) {
1052: spprintf(error, 0, "phar error: unable to create temporary file");
1053: return EOF;
1054: }
1055: if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1056: php_stream_close(entry.fp);
1057: if (error) {
1058: spprintf(error, 0, "unable to %s stub in%star-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1059: }
1060: return EOF;
1061: }
1062:
1063: entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1064: entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1065: entry.filename_len = sizeof(".phar/stub.php")-1;
1066:
1067: if (!defaultstub) {
1068: if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1069: if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1070: php_stream_close(entry.fp);
1071: efree(entry.filename);
1072: if (error) {
1073: spprintf(error, 0, "unable to create stub in tar-based phar \"%s\"", phar->fname);
1074: }
1075: return EOF;
1076: }
1077: } else {
1078: php_stream_close(entry.fp);
1079: efree(entry.filename);
1080: }
1081: } else {
1082: if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1083: php_stream_close(entry.fp);
1084: efree(entry.filename);
1085: if (error) {
1086: spprintf(error, 0, "unable to overwrite stub in tar-based phar \"%s\"", phar->fname);
1087: }
1088: return EOF;
1089: }
1090: }
1091: }
1092: nostub:
1093: if (phar->fp && !phar->is_brandnew) {
1094: oldfile = phar->fp;
1095: closeoldfile = 0;
1096: php_stream_rewind(oldfile);
1097: } else {
1098: oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1099: closeoldfile = oldfile != NULL;
1100: }
1101:
1102: newfile = php_stream_fopen_tmpfile();
1103: if (!newfile) {
1104: if (error) {
1105: spprintf(error, 0, "unable to create temporary file");
1106: }
1107: if (closeoldfile) {
1108: php_stream_close(oldfile);
1109: }
1110: return EOF;
1111: }
1112:
1113: pass.old = oldfile;
1114: pass.new = newfile;
1115: pass.error = error;
1116: pass.free_fp = 1;
1117: pass.free_ufp = 1;
1118:
1119: if (phar->metadata) {
1120: phar_entry_info *mentry;
1121: if (SUCCESS == zend_hash_find(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void **)&mentry)) {
1122: if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) {
1123: if (closeoldfile) {
1124: php_stream_close(oldfile);
1125: }
1126: return EOF;
1127: }
1128: } else {
1129: phar_entry_info newentry = {0};
1130:
1131: newentry.filename = estrndup(".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1);
1132: newentry.filename_len = sizeof(".phar/.metadata.bin")-1;
1133: newentry.phar = phar;
1134: newentry.tar_type = TAR_FILE;
1135: newentry.is_tar = 1;
1136:
1137: if (SUCCESS != zend_hash_add(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void *)&newentry, sizeof(phar_entry_info), (void **)&mentry)) {
1138: spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for phar archive \"%s\"", phar->fname);
1139: if (closeoldfile) {
1140: php_stream_close(oldfile);
1141: }
1142: return EOF;
1143: }
1144:
1145: if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) {
1146: zend_hash_del(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1);
1147: if (closeoldfile) {
1148: php_stream_close(oldfile);
1149: }
1150: return EOF;
1151: }
1152: }
1153: }
1154:
1155: zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_setupmetadata, (void *) &pass TSRMLS_CC);
1156:
1157: if (error && *error) {
1158: if (closeoldfile) {
1159: php_stream_close(oldfile);
1160: }
1161:
1162: /* on error in the hash iterator above, error is set */
1163: php_stream_close(newfile);
1164: return EOF;
1165: }
1166:
1167: zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_writeheaders, (void *) &pass TSRMLS_CC);
1168:
1169: /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1170: if (!phar->is_data || phar->sig_flags) {
1171: if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, error TSRMLS_CC)) {
1172: if (error) {
1173: char *save = *error;
1174: spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", save);
1175: efree(save);
1176: }
1177:
1178: if (closeoldfile) {
1179: php_stream_close(oldfile);
1180: }
1181:
1182: php_stream_close(newfile);
1183: return EOF;
1184: }
1185:
1186: entry.filename = ".phar/signature.bin";
1187: entry.filename_len = sizeof(".phar/signature.bin")-1;
1188: entry.fp = php_stream_fopen_tmpfile();
1189: if (entry.fp == NULL) {
1190: spprintf(error, 0, "phar error: unable to create temporary file");
1191: return EOF;
1192: }
1193: #ifdef WORDS_BIGENDIAN
1194: # define PHAR_SET_32(var, buffer) \
1195: *(php_uint32 *)(var) = (((((unsigned char*)&(buffer))[3]) << 24) \
1196: | ((((unsigned char*)&(buffer))[2]) << 16) \
1197: | ((((unsigned char*)&(buffer))[1]) << 8) \
1198: | (((unsigned char*)&(buffer))[0]))
1199: #else
1200: # define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer)
1201: #endif
1202: PHAR_SET_32(sigbuf, phar->sig_flags);
1203: PHAR_SET_32(sigbuf + 4, signature_length);
1204:
1205: if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) {
1206: efree(signature);
1207: if (error) {
1208: spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", phar->fname);
1209: }
1210:
1211: if (closeoldfile) {
1212: php_stream_close(oldfile);
1213: }
1214: php_stream_close(newfile);
1215: return EOF;
1216: }
1217:
1218: efree(signature);
1219: entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1220: /* throw out return value and write the signature */
1221: entry.filename_len = phar_tar_writeheaders((void *)&entry, (void *)&pass TSRMLS_CC);
1222:
1223: if (error && *error) {
1224: if (closeoldfile) {
1225: php_stream_close(oldfile);
1226: }
1227: /* error is set by writeheaders */
1228: php_stream_close(newfile);
1229: return EOF;
1230: }
1231: } /* signature */
1232:
1233: /* add final zero blocks */
1234: buf = (char *) ecalloc(1024, 1);
1235: php_stream_write(newfile, buf, 1024);
1236: efree(buf);
1237:
1238: if (closeoldfile) {
1239: php_stream_close(oldfile);
1240: }
1241:
1242: /* on error in the hash iterator above, error is set */
1243: if (error && *error) {
1244: php_stream_close(newfile);
1245: return EOF;
1246: }
1247:
1248: if (phar->fp && pass.free_fp) {
1249: php_stream_close(phar->fp);
1250: }
1251:
1252: if (phar->ufp) {
1253: if (pass.free_ufp) {
1254: php_stream_close(phar->ufp);
1255: }
1256: phar->ufp = NULL;
1257: }
1258:
1259: phar->is_brandnew = 0;
1260: php_stream_rewind(newfile);
1261:
1262: if (phar->donotflush) {
1263: /* deferred flush */
1264: phar->fp = newfile;
1265: } else {
1266: phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1267: if (!phar->fp) {
1268: phar->fp = newfile;
1269: if (error) {
1270: spprintf(error, 0, "unable to open new phar \"%s\" for writing", phar->fname);
1271: }
1272: return EOF;
1273: }
1274:
1275: if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
1276: php_stream_filter *filter;
1277: /* to properly compress, we have to tell zlib to add a zlib header */
1278: zval filterparams;
1279:
1280: array_init(&filterparams);
1281: /* this is defined in zlib's zconf.h */
1282: #ifndef MAX_WBITS
1283: #define MAX_WBITS 15
1284: #endif
1285: add_assoc_long(&filterparams, "window", MAX_WBITS + 16);
1286: filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC);
1287: zval_dtor(&filterparams);
1288:
1289: if (!filter) {
1290: /* copy contents uncompressed rather than lose them */
1291: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1292: php_stream_close(newfile);
1293: if (error) {
1294: 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);
1295: }
1296: return EOF;
1297: }
1298:
1299: php_stream_filter_append(&phar->fp->writefilters, filter);
1300: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1301: php_stream_filter_flush(filter, 1);
1302: php_stream_filter_remove(filter, 1 TSRMLS_CC);
1303: php_stream_close(phar->fp);
1304: /* use the temp stream as our base */
1305: phar->fp = newfile;
1306: } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
1307: php_stream_filter *filter;
1308:
1309: filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC);
1310: php_stream_filter_append(&phar->fp->writefilters, filter);
1311: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1312: php_stream_filter_flush(filter, 1);
1313: php_stream_filter_remove(filter, 1 TSRMLS_CC);
1314: php_stream_close(phar->fp);
1315: /* use the temp stream as our base */
1316: phar->fp = newfile;
1317: } else {
1318: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1319: /* we could also reopen the file in "rb" mode but there is no need for that */
1320: php_stream_close(newfile);
1321: }
1322: }
1323: return EOF;
1324: }
1325: /* }}} */
1326:
1327: /*
1328: * Local variables:
1329: * tab-width: 4
1330: * c-basic-offset: 4
1331: * End:
1332: * vim600: noet sw=4 ts=4 fdm=marker
1333: * vim<600: noet sw=4 ts=4
1334: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>