root/ext/phar/tar.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. phar_tar_number
  2. phar_tar_octal
  3. phar_tar_checksum
  4. phar_is_tar
  5. phar_open_or_create_tar
  6. phar_tar_process_metadata
  7. strnlen
  8. phar_parse_tarfile
  9. phar_tar_writeheaders_int
  10. phar_tar_writeheaders
  11. phar_tar_setmetadata
  12. phar_tar_setupmetadata
  13. phar_tar_flush

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

/* [<][>][^][v][top][bottom][index][help] */