Annotation of embedaddon/php/ext/phar/tar.c, revision 1.1.1.1
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | TAR archive support for Phar |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 2005-2012 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 *) emalloc(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: entry.filename = pemalloc(entry.filename_len+1, myphar->is_persistent);
341:
342: read = php_stream_read(fp, entry.filename, entry.filename_len);
343: if (read != entry.filename_len) {
344: efree(entry.filename);
345: if (error) {
346: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
347: }
348: php_stream_close(fp);
349: phar_destroy_phar_data(myphar TSRMLS_CC);
350: return FAILURE;
351: }
352: entry.filename[entry.filename_len] = '\0';
353:
354: /* skip blank stuff */
355: size = ((size+511)&~511) - size;
356:
357: /* this is not good enough - seek succeeds even on truncated tars */
358: php_stream_seek(fp, size, SEEK_CUR);
359: if ((uint)php_stream_tell(fp) > totalsize) {
360: efree(entry.filename);
361: if (error) {
362: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
363: }
364: php_stream_close(fp);
365: phar_destroy_phar_data(myphar TSRMLS_CC);
366: return FAILURE;
367: }
368:
369: read = php_stream_read(fp, buf, sizeof(buf));
370:
371: if (read != sizeof(buf)) {
372: efree(entry.filename);
373: if (error) {
374: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
375: }
376: php_stream_close(fp);
377: phar_destroy_phar_data(myphar TSRMLS_CC);
378: return FAILURE;
379: }
380: continue;
381: } else if (!last_was_longlink && !old && hdr->prefix[0] != 0) {
382: char name[256];
383: int i, j;
384:
385: for (i = 0; i < 155; i++) {
386: name[i] = hdr->prefix[i];
387: if (name[i] == '\0') {
388: break;
389: }
390: }
391: name[i++] = '/';
392: for (j = 0; j < 100; j++) {
393: name[i+j] = hdr->name[j];
394: if (name[i+j] == '\0') {
395: break;
396: }
397: }
398:
399: entry.filename_len = i+j;
400:
401: if (name[entry.filename_len - 1] == '/') {
402: /* some tar programs store directories with trailing slash */
403: entry.filename_len--;
404: }
405: entry.filename = pestrndup(name, entry.filename_len, myphar->is_persistent);
406: } else if (!last_was_longlink) {
407: int i;
408:
409: /* calculate strlen, which can be no longer than 100 */
410: for (i = 0; i < 100; i++) {
411: if (hdr->name[i] == '\0') {
412: break;
413: }
414: }
415: entry.filename_len = i;
416: entry.filename = pestrndup(hdr->name, i, myphar->is_persistent);
417:
418: if (entry.filename[entry.filename_len - 1] == '/') {
419: /* some tar programs store directories with trailing slash */
420: entry.filename[entry.filename_len - 1] = '\0';
421: entry.filename_len--;
422: }
423: }
424: last_was_longlink = 0;
425:
426: phar_add_virtual_dirs(myphar, entry.filename, entry.filename_len TSRMLS_CC);
427:
428: if (sum1 != sum2) {
429: if (error) {
430: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename);
431: }
432: pefree(entry.filename, myphar->is_persistent);
433: php_stream_close(fp);
434: phar_destroy_phar_data(myphar TSRMLS_CC);
435: return FAILURE;
436: }
437:
438: entry.tar_type = ((old & (hdr->typeflag == '\0')) ? TAR_FILE : hdr->typeflag);
439: entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */
440: entry.fp_type = PHAR_FP;
441: entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK;
442: entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime));
443: entry.is_persistent = myphar->is_persistent;
444: #ifndef S_ISDIR
445: #define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
446: #endif
447: if (old && entry.tar_type == TAR_FILE && S_ISDIR(entry.flags)) {
448: entry.tar_type = TAR_DIR;
449: }
450:
451: if (entry.tar_type == TAR_DIR) {
452: entry.is_dir = 1;
453: } else {
454: entry.is_dir = 0;
455: }
456:
457: entry.link = NULL;
458:
459: if (entry.tar_type == TAR_LINK) {
460: if (!zend_hash_exists(&myphar->manifest, hdr->linkname, strlen(hdr->linkname))) {
461: if (error) {
462: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%s\"", fname, hdr->linkname);
463: }
464: pefree(entry.filename, entry.is_persistent);
465: php_stream_close(fp);
466: phar_destroy_phar_data(myphar TSRMLS_CC);
467: return FAILURE;
468: }
469: entry.link = estrdup(hdr->linkname);
470: } else if (entry.tar_type == TAR_SYMLINK) {
471: entry.link = estrdup(hdr->linkname);
472: }
473: phar_set_inode(&entry TSRMLS_CC);
474: zend_hash_add(&myphar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), (void **) &newentry);
475:
476: if (entry.is_persistent) {
477: ++entry.manifest_pos;
478: }
479:
480: if (entry.filename_len >= sizeof(".phar/.metadata")-1 && !memcmp(entry.filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) {
481: if (FAILURE == phar_tar_process_metadata(newentry, fp TSRMLS_CC)) {
482: if (error) {
483: spprintf(error, 4096, "phar error: tar-based phar \"%s\" has invalid metadata in magic file \"%s\"", fname, entry.filename);
484: }
485: php_stream_close(fp);
486: phar_destroy_phar_data(myphar TSRMLS_CC);
487: return FAILURE;
488: }
489: }
490:
491: if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
492: /* found explicit alias */
493: if (size > 511) {
494: if (error) {
495: spprintf(error, 4096, "phar error: tar-based phar \"%s\" has alias that is larger than 511 bytes, cannot process", fname);
496: }
497: php_stream_close(fp);
498: phar_destroy_phar_data(myphar TSRMLS_CC);
499: return FAILURE;
500: }
501:
502: read = php_stream_read(fp, buf, size);
503:
504: if (read == size) {
505: buf[size] = '\0';
506: if (!phar_validate_alias(buf, size)) {
507: if (size > 50) {
508: buf[50] = '.';
509: buf[51] = '.';
510: buf[52] = '.';
511: buf[53] = '\0';
512: }
513:
514: if (error) {
515: spprintf(error, 4096, "phar error: invalid alias \"%s\" in tar-based phar \"%s\"", buf, fname);
516: }
517:
518: php_stream_close(fp);
519: phar_destroy_phar_data(myphar TSRMLS_CC);
520: return FAILURE;
521: }
522:
523: actual_alias = pestrndup(buf, size, myphar->is_persistent);
524: myphar->alias = actual_alias;
525: myphar->alias_len = size;
526: php_stream_seek(fp, pos, SEEK_SET);
527: } else {
528: if (error) {
529: spprintf(error, 4096, "phar error: Unable to read alias from tar-based phar \"%s\"", fname);
530: }
531:
532: php_stream_close(fp);
533: phar_destroy_phar_data(myphar TSRMLS_CC);
534: return FAILURE;
535: }
536: }
537:
538: size = (size+511)&~511;
539:
540: if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) {
541: /* this is not good enough - seek succeeds even on truncated tars */
542: php_stream_seek(fp, size, SEEK_CUR);
543: if ((uint)php_stream_tell(fp) > totalsize) {
544: if (error) {
545: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
546: }
547: php_stream_close(fp);
548: phar_destroy_phar_data(myphar TSRMLS_CC);
549: return FAILURE;
550: }
551: }
552:
553: read = php_stream_read(fp, buf, sizeof(buf));
554:
555: if (read != sizeof(buf)) {
556: if (error) {
557: spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
558: }
559: php_stream_close(fp);
560: phar_destroy_phar_data(myphar TSRMLS_CC);
561: return FAILURE;
562: }
563: } while (read != 0);
564:
565: if (zend_hash_exists(&(myphar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
566: myphar->is_data = 0;
567: } else {
568: myphar->is_data = 1;
569: }
570:
571: /* ensure signature set */
572: if (!myphar->is_data && PHAR_G(require_hash) && !myphar->signature) {
573: php_stream_close(fp);
574: phar_destroy_phar_data(myphar TSRMLS_CC);
575: if (error) {
576: spprintf(error, 0, "tar-based phar \"%s\" does not have a signature", fname);
577: }
578: return FAILURE;
579: }
580:
581: myphar->fname = pestrndup(fname, fname_len, myphar->is_persistent);
582: #ifdef PHP_WIN32
583: phar_unixify_path_separators(myphar->fname, fname_len);
584: #endif
585: myphar->fname_len = fname_len;
586: myphar->fp = fp;
587: p = strrchr(myphar->fname, '/');
588:
589: if (p) {
590: myphar->ext = memchr(p, '.', (myphar->fname + fname_len) - p);
591: if (myphar->ext == p) {
592: myphar->ext = memchr(p + 1, '.', (myphar->fname + fname_len) - p - 1);
593: }
594: if (myphar->ext) {
595: myphar->ext_len = (myphar->fname + fname_len) - myphar->ext;
596: }
597: }
598:
599: phar_request_initialize(TSRMLS_C);
600:
601: if (SUCCESS != zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len, (void*)&myphar, sizeof(phar_archive_data*), (void **)&actual)) {
602: if (error) {
603: spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\" to phar registry", fname);
604: }
605: php_stream_close(fp);
606: phar_destroy_phar_data(myphar TSRMLS_CC);
607: return FAILURE;
608: }
609:
610: myphar = *actual;
611:
612: if (actual_alias) {
613: phar_archive_data **fd_ptr;
614:
615: myphar->is_temporary_alias = 0;
616:
617: if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void **)&fd_ptr)) {
618: if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, myphar->alias_len TSRMLS_CC)) {
619: if (error) {
620: spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname);
621: }
622: zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len);
623: return FAILURE;
624: }
625: }
626:
627: zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL);
628: } else {
629: phar_archive_data **fd_ptr;
630:
631: if (alias_len) {
632: if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
633: if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
634: if (error) {
635: spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname);
636: }
637: zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len);
638: return FAILURE;
639: }
640: }
641: zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL);
642: myphar->alias = pestrndup(alias, alias_len, myphar->is_persistent);
643: myphar->alias_len = alias_len;
644: } else {
645: myphar->alias = pestrndup(myphar->fname, fname_len, myphar->is_persistent);
646: myphar->alias_len = fname_len;
647: }
648:
649: myphar->is_temporary_alias = 1;
650: }
651:
652: if (pphar) {
653: *pphar = myphar;
654: }
655:
656: return SUCCESS;
657: }
658: /* }}} */
659:
660: struct _phar_pass_tar_info {
661: php_stream *old;
662: php_stream *new;
663: int free_fp;
664: int free_ufp;
665: char **error;
666: };
667:
668: static int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */
669: {
670: tar_header header;
671: size_t pos;
672: phar_entry_info *entry = (phar_entry_info *) pDest;
673: struct _phar_pass_tar_info *fp = (struct _phar_pass_tar_info *)argument;
674: char padding[512];
675:
676: if (entry->is_mounted) {
677: return ZEND_HASH_APPLY_KEEP;
678: }
679:
680: if (entry->is_deleted) {
681: if (entry->fp_refcount <= 0) {
682: return ZEND_HASH_APPLY_REMOVE;
683: } else {
684: /* we can't delete this in-memory until it is closed */
685: return ZEND_HASH_APPLY_KEEP;
686: }
687: }
688:
689: phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len TSRMLS_CC);
690: memset((char *) &header, 0, sizeof(header));
691:
692: if (entry->filename_len > 100) {
693: char *boundary;
694: if (entry->filename_len > 256) {
695: if (fp->error) {
696: 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);
697: }
698: return ZEND_HASH_APPLY_STOP;
699: }
700: boundary = entry->filename + entry->filename_len - 101;
701: while (*boundary && *boundary != '/') {
702: ++boundary;
703: }
704: if (!*boundary || ((boundary - entry->filename) > 155)) {
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: memcpy(header.prefix, entry->filename, boundary - entry->filename);
711: memcpy(header.name, boundary + 1, entry->filename_len - (boundary + 1 - entry->filename));
712: } else {
713: memcpy(header.name, entry->filename, entry->filename_len);
714: }
715:
716: phar_tar_octal(header.mode, entry->flags & PHAR_ENT_PERM_MASK, sizeof(header.mode)-1);
717:
718: if (FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) {
719: if (fp->error) {
720: 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);
721: }
722: return ZEND_HASH_APPLY_STOP;
723: }
724:
725: if (FAILURE == phar_tar_octal(header.mtime, entry->timestamp, sizeof(header.mtime)-1)) {
726: if (fp->error) {
727: 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);
728: }
729: return ZEND_HASH_APPLY_STOP;
730: }
731:
732: /* calc checksum */
733: header.typeflag = entry->tar_type;
734:
735: if (entry->link) {
736: strncpy(header.linkname, entry->link, strlen(entry->link));
737: }
738:
739: strncpy(header.magic, "ustar", sizeof("ustar")-1);
740: strncpy(header.version, "00", sizeof("00")-1);
741: strncpy(header.checksum, " ", sizeof(" ")-1);
742: entry->crc32 = phar_tar_checksum((char *)&header, sizeof(header));
743:
744: if (FAILURE == phar_tar_octal(header.checksum, entry->crc32, sizeof(header.checksum)-1)) {
745: if (fp->error) {
746: 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);
747: }
748: return ZEND_HASH_APPLY_STOP;
749: }
750:
751: /* write header */
752: entry->header_offset = php_stream_tell(fp->new);
753:
754: if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) {
755: if (fp->error) {
756: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for file \"%s\" could not be written", entry->phar->fname, entry->filename);
757: }
758: return ZEND_HASH_APPLY_STOP;
759: }
760:
761: pos = php_stream_tell(fp->new); /* save start of file within tar */
762:
763: /* write contents */
764: if (entry->uncompressed_filesize) {
765: if (FAILURE == phar_open_entry_fp(entry, fp->error, 0 TSRMLS_CC)) {
766: return ZEND_HASH_APPLY_STOP;
767: }
768:
769: if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
770: if (fp->error) {
771: 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);
772: }
773: return ZEND_HASH_APPLY_STOP;
774: }
775:
776: if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), fp->new, entry->uncompressed_filesize, NULL)) {
777: if (fp->error) {
778: spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename);
779: }
780: return ZEND_HASH_APPLY_STOP;
781: }
782:
783: memset(padding, 0, 512);
784: php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize);
785: }
786:
787: if (!entry->is_modified && entry->fp_refcount) {
788: /* open file pointers refer to this fp, do not free the stream */
789: switch (entry->fp_type) {
790: case PHAR_FP:
791: fp->free_fp = 0;
792: break;
793: case PHAR_UFP:
794: fp->free_ufp = 0;
795: default:
796: break;
797: }
798: }
799:
800: entry->is_modified = 0;
801:
802: if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
803: if (!entry->fp_refcount) {
804: php_stream_close(entry->fp);
805: }
806: entry->fp = NULL;
807: }
808:
809: entry->fp_type = PHAR_FP;
810:
811: /* note new location within tar */
812: entry->offset = entry->offset_abs = pos;
813: return ZEND_HASH_APPLY_KEEP;
814: }
815: /* }}} */
816:
817: int phar_tar_setmetadata(zval *metadata, phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */
818: {
819: php_serialize_data_t metadata_hash;
820:
821: if (entry->metadata_str.c) {
822: smart_str_free(&entry->metadata_str);
823: }
824:
825: entry->metadata_str.c = 0;
826: entry->metadata_str.len = 0;
827: PHP_VAR_SERIALIZE_INIT(metadata_hash);
828: php_var_serialize(&entry->metadata_str, &metadata, &metadata_hash TSRMLS_CC);
829: PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
830: entry->uncompressed_filesize = entry->compressed_filesize = entry->metadata_str.len;
831:
832: if (entry->fp && entry->fp_type == PHAR_MOD) {
833: php_stream_close(entry->fp);
834: }
835:
836: entry->fp_type = PHAR_MOD;
837: entry->is_modified = 1;
838: entry->fp = php_stream_fopen_tmpfile();
839: entry->offset = entry->offset_abs = 0;
840:
841: if (entry->metadata_str.len != php_stream_write(entry->fp, entry->metadata_str.c, entry->metadata_str.len)) {
842: spprintf(error, 0, "phar tar error: unable to write metadata to magic metadata file \"%s\"", entry->filename);
843: zend_hash_del(&(entry->phar->manifest), entry->filename, entry->filename_len);
844: return ZEND_HASH_APPLY_STOP;
845: }
846:
847: return ZEND_HASH_APPLY_KEEP;
848: }
849: /* }}} */
850:
851: static int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) /* {{{ */
852: {
853: int lookfor_len;
854: struct _phar_pass_tar_info *i = (struct _phar_pass_tar_info *)argument;
855: char *lookfor, **error = i->error;
856: phar_entry_info *entry = (phar_entry_info *)pDest, *metadata, newentry = {0};
857:
858: if (entry->filename_len >= sizeof(".phar/.metadata") && !memcmp(entry->filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) {
859: if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) {
860: return phar_tar_setmetadata(entry->phar->metadata, entry, error TSRMLS_CC);
861: }
862: /* search for the file this metadata entry references */
863: 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))) {
864: /* this is orphaned metadata, erase it */
865: return ZEND_HASH_APPLY_REMOVE;
866: }
867: /* we can keep this entry, the file that refers to it exists */
868: return ZEND_HASH_APPLY_KEEP;
869: }
870:
871: if (!entry->is_modified) {
872: return ZEND_HASH_APPLY_KEEP;
873: }
874:
875: /* now we are dealing with regular files, so look for metadata */
876: lookfor_len = spprintf(&lookfor, 0, ".phar/.metadata/%s/.metadata.bin", entry->filename);
877:
878: if (!entry->metadata) {
879: zend_hash_del(&(entry->phar->manifest), lookfor, lookfor_len);
880: efree(lookfor);
881: return ZEND_HASH_APPLY_KEEP;
882: }
883:
884: if (SUCCESS == zend_hash_find(&(entry->phar->manifest), lookfor, lookfor_len, (void **)&metadata)) {
885: int ret;
886: ret = phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC);
887: efree(lookfor);
888: return ret;
889: }
890:
891: newentry.filename = lookfor;
892: newentry.filename_len = lookfor_len;
893: newentry.phar = entry->phar;
894: newentry.tar_type = TAR_FILE;
895: newentry.is_tar = 1;
896:
897: if (SUCCESS != zend_hash_add(&(entry->phar->manifest), lookfor, lookfor_len, (void *)&newentry, sizeof(phar_entry_info), (void **)&metadata)) {
898: efree(lookfor);
899: spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for file \"%s\"", entry->filename);
900: return ZEND_HASH_APPLY_STOP;
901: }
902:
903: return phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC);
904: }
905: /* }}} */
906:
907: int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */
908: {
909: phar_entry_info entry = {0};
910: static const char newstub[] = "<?php // tar-based phar archive stub file\n__HALT_COMPILER();";
911: php_stream *oldfile, *newfile, *stubfile;
912: int closeoldfile, free_user_stub, signature_length;
913: struct _phar_pass_tar_info pass;
914: char *buf, *signature, *tmp, sigbuf[8];
915: char halt_stub[] = "__HALT_COMPILER();";
916:
917: entry.flags = PHAR_ENT_PERM_DEF_FILE;
918: entry.timestamp = time(NULL);
919: entry.is_modified = 1;
920: entry.is_crc_checked = 1;
921: entry.is_tar = 1;
922: entry.tar_type = '0';
923: entry.phar = phar;
924: entry.fp_type = PHAR_MOD;
925:
926: if (phar->is_persistent) {
927: if (error) {
928: spprintf(error, 0, "internal error: attempt to flush cached tar-based phar \"%s\"", phar->fname);
929: }
930: return EOF;
931: }
932:
933: if (phar->is_data) {
934: goto nostub;
935: }
936:
937: /* set alias */
938: if (!phar->is_temporary_alias && phar->alias_len) {
939: entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
940: entry.filename_len = sizeof(".phar/alias.txt")-1;
941: entry.fp = php_stream_fopen_tmpfile();
942:
943: if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
944: if (error) {
945: spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
946: }
947: return EOF;
948: }
949:
950: entry.uncompressed_filesize = phar->alias_len;
951:
952: if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
953: if (error) {
954: spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
955: }
956: return EOF;
957: }
958: } else {
959: zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
960: }
961:
962: /* set stub */
963: if (user_stub && !defaultstub) {
964: char *pos;
965: if (len < 0) {
966: /* resource passed in */
967: if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
968: if (error) {
969: spprintf(error, 0, "unable to access resource to copy stub to new tar-based phar \"%s\"", phar->fname);
970: }
971: return EOF;
972: }
973: if (len == -1) {
974: len = PHP_STREAM_COPY_ALL;
975: } else {
976: len = -len;
977: }
978: user_stub = 0;
979: #if PHP_MAJOR_VERSION >= 6
980: if (!(len = php_stream_copy_to_mem(stubfile, (void **) &user_stub, len, 0)) || !user_stub) {
981: #else
982: if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
983: #endif
984: if (error) {
985: spprintf(error, 0, "unable to read resource to copy stub to new tar-based phar \"%s\"", phar->fname);
986: }
987: return EOF;
988: }
989: free_user_stub = 1;
990: } else {
991: free_user_stub = 0;
992: }
993:
994: tmp = estrndup(user_stub, len);
995: if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
996: efree(tmp);
997: if (error) {
998: spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname);
999: }
1000: if (free_user_stub) {
1001: efree(user_stub);
1002: }
1003: return EOF;
1004: }
1005: pos = user_stub + (pos - tmp);
1006: efree(tmp);
1007:
1008: len = pos - user_stub + 18;
1009: entry.fp = php_stream_fopen_tmpfile();
1010: entry.uncompressed_filesize = len + 5;
1011:
1012: if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1013: || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1014: if (error) {
1015: spprintf(error, 0, "unable to create stub from string in new tar-based phar \"%s\"", phar->fname);
1016: }
1017: if (free_user_stub) {
1018: efree(user_stub);
1019: }
1020: php_stream_close(entry.fp);
1021: return EOF;
1022: }
1023:
1024: entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1025: entry.filename_len = sizeof(".phar/stub.php")-1;
1026: zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
1027:
1028: if (free_user_stub) {
1029: efree(user_stub);
1030: }
1031: } else {
1032: /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1033: entry.fp = php_stream_fopen_tmpfile();
1034:
1035: if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1036: php_stream_close(entry.fp);
1037: if (error) {
1038: spprintf(error, 0, "unable to %s stub in%star-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1039: }
1040: return EOF;
1041: }
1042:
1043: entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1044: entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1045: entry.filename_len = sizeof(".phar/stub.php")-1;
1046:
1047: if (!defaultstub) {
1048: if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1049: if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1050: php_stream_close(entry.fp);
1051: efree(entry.filename);
1052: if (error) {
1053: spprintf(error, 0, "unable to create stub in tar-based phar \"%s\"", phar->fname);
1054: }
1055: return EOF;
1056: }
1057: } else {
1058: php_stream_close(entry.fp);
1059: efree(entry.filename);
1060: }
1061: } else {
1062: if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1063: php_stream_close(entry.fp);
1064: efree(entry.filename);
1065: if (error) {
1066: spprintf(error, 0, "unable to overwrite stub in tar-based phar \"%s\"", phar->fname);
1067: }
1068: return EOF;
1069: }
1070: }
1071: }
1072: nostub:
1073: if (phar->fp && !phar->is_brandnew) {
1074: oldfile = phar->fp;
1075: closeoldfile = 0;
1076: php_stream_rewind(oldfile);
1077: } else {
1078: oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1079: closeoldfile = oldfile != NULL;
1080: }
1081:
1082: newfile = php_stream_fopen_tmpfile();
1083:
1084: if (!newfile) {
1085: if (error) {
1086: spprintf(error, 0, "unable to create temporary file");
1087: }
1088: if (closeoldfile) {
1089: php_stream_close(oldfile);
1090: }
1091: return EOF;
1092: }
1093:
1094: pass.old = oldfile;
1095: pass.new = newfile;
1096: pass.error = error;
1097: pass.free_fp = 1;
1098: pass.free_ufp = 1;
1099:
1100: if (phar->metadata) {
1101: phar_entry_info *mentry;
1102: if (SUCCESS == zend_hash_find(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void **)&mentry)) {
1103: if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) {
1104: if (closeoldfile) {
1105: php_stream_close(oldfile);
1106: }
1107: return EOF;
1108: }
1109: } else {
1110: phar_entry_info newentry = {0};
1111:
1112: newentry.filename = estrndup(".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1);
1113: newentry.filename_len = sizeof(".phar/.metadata.bin")-1;
1114: newentry.phar = phar;
1115: newentry.tar_type = TAR_FILE;
1116: newentry.is_tar = 1;
1117:
1118: if (SUCCESS != zend_hash_add(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void *)&newentry, sizeof(phar_entry_info), (void **)&mentry)) {
1119: spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for phar archive \"%s\"", phar->fname);
1120: if (closeoldfile) {
1121: php_stream_close(oldfile);
1122: }
1123: return EOF;
1124: }
1125:
1126: if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) {
1127: zend_hash_del(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1);
1128: if (closeoldfile) {
1129: php_stream_close(oldfile);
1130: }
1131: return EOF;
1132: }
1133: }
1134: }
1135:
1136: zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_setupmetadata, (void *) &pass TSRMLS_CC);
1137:
1138: if (error && *error) {
1139: if (closeoldfile) {
1140: php_stream_close(oldfile);
1141: }
1142:
1143: /* on error in the hash iterator above, error is set */
1144: php_stream_close(newfile);
1145: return EOF;
1146: }
1147:
1148: zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_writeheaders, (void *) &pass TSRMLS_CC);
1149:
1150: /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1151: if (!phar->is_data || phar->sig_flags) {
1152: if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, error TSRMLS_CC)) {
1153: if (error) {
1154: char *save = *error;
1155: spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", save);
1156: efree(save);
1157: }
1158:
1159: if (closeoldfile) {
1160: php_stream_close(oldfile);
1161: }
1162:
1163: php_stream_close(newfile);
1164: return EOF;
1165: }
1166:
1167: entry.filename = ".phar/signature.bin";
1168: entry.filename_len = sizeof(".phar/signature.bin")-1;
1169: entry.fp = php_stream_fopen_tmpfile();
1170:
1171: #ifdef WORDS_BIGENDIAN
1172: # define PHAR_SET_32(var, buffer) \
1173: *(php_uint32 *)(var) = (((((unsigned char*)&(buffer))[3]) << 24) \
1174: | ((((unsigned char*)&(buffer))[2]) << 16) \
1175: | ((((unsigned char*)&(buffer))[1]) << 8) \
1176: | (((unsigned char*)&(buffer))[0]))
1177: #else
1178: # define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer)
1179: #endif
1180: PHAR_SET_32(sigbuf, phar->sig_flags);
1181: PHAR_SET_32(sigbuf + 4, signature_length);
1182:
1183: if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) {
1184: efree(signature);
1185: if (error) {
1186: spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", phar->fname);
1187: }
1188:
1189: if (closeoldfile) {
1190: php_stream_close(oldfile);
1191: }
1192: php_stream_close(newfile);
1193: return EOF;
1194: }
1195:
1196: efree(signature);
1197: entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1198: /* throw out return value and write the signature */
1199: entry.filename_len = phar_tar_writeheaders((void *)&entry, (void *)&pass TSRMLS_CC);
1200:
1201: if (error && *error) {
1202: if (closeoldfile) {
1203: php_stream_close(oldfile);
1204: }
1205: /* error is set by writeheaders */
1206: php_stream_close(newfile);
1207: return EOF;
1208: }
1209: } /* signature */
1210:
1211: /* add final zero blocks */
1212: buf = (char *) ecalloc(1024, 1);
1213: php_stream_write(newfile, buf, 1024);
1214: efree(buf);
1215:
1216: if (closeoldfile) {
1217: php_stream_close(oldfile);
1218: }
1219:
1220: /* on error in the hash iterator above, error is set */
1221: if (error && *error) {
1222: php_stream_close(newfile);
1223: return EOF;
1224: }
1225:
1226: if (phar->fp && pass.free_fp) {
1227: php_stream_close(phar->fp);
1228: }
1229:
1230: if (phar->ufp) {
1231: if (pass.free_ufp) {
1232: php_stream_close(phar->ufp);
1233: }
1234: phar->ufp = NULL;
1235: }
1236:
1237: phar->is_brandnew = 0;
1238: php_stream_rewind(newfile);
1239:
1240: if (phar->donotflush) {
1241: /* deferred flush */
1242: phar->fp = newfile;
1243: } else {
1244: phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1245: if (!phar->fp) {
1246: phar->fp = newfile;
1247: if (error) {
1248: spprintf(error, 0, "unable to open new phar \"%s\" for writing", phar->fname);
1249: }
1250: return EOF;
1251: }
1252:
1253: if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
1254: php_stream_filter *filter;
1255: /* to properly compress, we have to tell zlib to add a zlib header */
1256: zval filterparams;
1257:
1258: array_init(&filterparams);
1259: /* this is defined in zlib's zconf.h */
1260: #ifndef MAX_WBITS
1261: #define MAX_WBITS 15
1262: #endif
1263: add_assoc_long(&filterparams, "window", MAX_WBITS + 16);
1264: filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC);
1265: zval_dtor(&filterparams);
1266:
1267: if (!filter) {
1268: /* copy contents uncompressed rather than lose them */
1269: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1270: php_stream_close(newfile);
1271: if (error) {
1272: 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);
1273: }
1274: return EOF;
1275: }
1276:
1277: php_stream_filter_append(&phar->fp->writefilters, filter);
1278: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1279: php_stream_filter_flush(filter, 1);
1280: php_stream_filter_remove(filter, 1 TSRMLS_CC);
1281: php_stream_close(phar->fp);
1282: /* use the temp stream as our base */
1283: phar->fp = newfile;
1284: } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
1285: php_stream_filter *filter;
1286:
1287: filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC);
1288: php_stream_filter_append(&phar->fp->writefilters, filter);
1289: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1290: php_stream_filter_flush(filter, 1);
1291: php_stream_filter_remove(filter, 1 TSRMLS_CC);
1292: php_stream_close(phar->fp);
1293: /* use the temp stream as our base */
1294: phar->fp = newfile;
1295: } else {
1296: phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1297: /* we could also reopen the file in "rb" mode but there is no need for that */
1298: php_stream_close(newfile);
1299: }
1300: }
1301: return EOF;
1302: }
1303: /* }}} */
1304:
1305: /*
1306: * Local variables:
1307: * tab-width: 4
1308: * c-basic-offset: 4
1309: * End:
1310: * vim600: noet sw=4 ts=4 fdm=marker
1311: * vim<600: noet sw=4 ts=4
1312: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>