Return to tar.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / phar |
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: */