Annotation of embedaddon/php/ext/phar/tar.c, revision 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>