Back to index

php5  5.3.10
tar.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | TAR archive support for Phar                                         |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 2005-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt.                                 |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Dmitry Stogov <dmitry@zend.com>                             |
00016   |          Gregory Beaver <cellog@php.net>                             |
00017   +----------------------------------------------------------------------+
00018 */
00019 
00020 #include "phar_internal.h"
00021 
00022 static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */
00023 {
00024        php_uint32 num = 0;
00025        int i = 0;
00026 
00027        while (i < len && buf[i] == ' ') {
00028               ++i;
00029        }
00030 
00031        while (i < len && buf[i] >= '0' && buf[i] <= '7') {
00032               num = num * 8 + (buf[i] - '0');
00033               ++i;
00034        }
00035 
00036        return num;
00037 }
00038 /* }}} */
00039 
00040 /* adapted from format_octal() in libarchive
00041  * 
00042  * Copyright (c) 2003-2009 Tim Kientzle
00043  * All rights reserved.
00044  *
00045  * Redistribution and use in source and binary forms, with or without
00046  * modification, are permitted provided that the following conditions
00047  * are met:
00048  * 1. Redistributions of source code must retain the above copyright
00049  *    notice, this list of conditions and the following disclaimer.
00050  * 2. Redistributions in binary form must reproduce the above copyright
00051  *    notice, this list of conditions and the following disclaimer in the
00052  *    documentation and/or other materials provided with the distribution.
00053  *
00054  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
00055  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00056  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00057  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
00058  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00059  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00060  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00061  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00062  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00063  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00064  */
00065 static int phar_tar_octal(char *buf, php_uint32 val, int len) /* {{{ */
00066 {
00067        char *p = buf;
00068        int s = len;
00069 
00070        p += len;            /* Start at the end and work backwards. */
00071        while (s-- > 0) {
00072               *--p = (char)('0' + (val & 7));
00073               val >>= 3;
00074        }
00075 
00076        if (val == 0)
00077               return SUCCESS;
00078 
00079        /* If it overflowed, fill field with max value. */
00080        while (len-- > 0)
00081               *p++ = '7';
00082 
00083        return FAILURE;
00084 }
00085 /* }}} */
00086 
00087 static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */
00088 {
00089        php_uint32 sum = 0;
00090        char *end = buf + len;
00091 
00092        while (buf != end) {
00093               sum += (unsigned char)*buf;
00094               ++buf;
00095        }
00096        return sum;
00097 }
00098 /* }}} */
00099 
00100 int phar_is_tar(char *buf, char *fname) /* {{{ */
00101 {
00102        tar_header *header = (tar_header *) buf;
00103        php_uint32 checksum = phar_tar_number(header->checksum, sizeof(header->checksum));
00104        php_uint32 ret;
00105        char save[sizeof(header->checksum)];
00106 
00107        /* assume that the first filename in a tar won't begin with <?php */
00108        if (!strncmp(buf, "<?php", sizeof("<?php")-1)) {
00109               return 0;
00110        }
00111 
00112        memcpy(save, header->checksum, sizeof(header->checksum));
00113        memset(header->checksum, ' ', sizeof(header->checksum));
00114        ret = (checksum == phar_tar_checksum(buf, 512));
00115        memcpy(header->checksum, save, sizeof(header->checksum));
00116        if (!ret && strstr(fname, ".tar")) {
00117               /* probably a corrupted tar - so we will pretend it is one */
00118               return 1;
00119        }
00120        return ret;
00121 }
00122 /* }}} */
00123 
00124 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) /* {{{ */
00125 {
00126        phar_archive_data *phar;
00127        int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error TSRMLS_CC);
00128 
00129        if (FAILURE == ret) {
00130               return FAILURE;
00131        }
00132 
00133        if (pphar) {
00134               *pphar = phar;
00135        }
00136 
00137        phar->is_data = is_data;
00138 
00139        if (phar->is_tar) {
00140               return ret;
00141        }
00142 
00143        if (phar->is_brandnew) {
00144               phar->is_tar = 1;
00145               phar->is_zip = 0;
00146               phar->internal_file_start = 0;
00147               return SUCCESS;
00148        }
00149 
00150        /* we've reached here - the phar exists and is a regular phar */
00151        if (error) {
00152               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);
00153        }
00154        return FAILURE;
00155 }
00156 /* }}} */
00157 
00158 static int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) /* {{{ */
00159 {
00160        char *metadata;
00161        size_t save = php_stream_tell(fp), read;
00162        phar_entry_info *mentry;
00163 
00164        metadata = (char *) emalloc(entry->uncompressed_filesize + 1);
00165 
00166        read = php_stream_read(fp, metadata, entry->uncompressed_filesize);
00167        if (read != entry->uncompressed_filesize) {
00168               efree(metadata);
00169               php_stream_seek(fp, save, SEEK_SET);
00170               return FAILURE;
00171        }
00172 
00173        if (phar_parse_metadata(&metadata, &entry->metadata, entry->uncompressed_filesize TSRMLS_CC) == FAILURE) {
00174               /* if not valid serialized data, it is a regular string */
00175               efree(metadata);
00176               php_stream_seek(fp, save, SEEK_SET);
00177               return FAILURE;
00178        }
00179 
00180        if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) {
00181               entry->phar->metadata = entry->metadata;
00182               entry->metadata = NULL;
00183        } 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)) {
00184               /* transfer this metadata to the entry it refers */
00185               mentry->metadata = entry->metadata;
00186               entry->metadata = NULL;
00187        }
00188 
00189        efree(metadata);
00190        php_stream_seek(fp, save, SEEK_SET);
00191        return SUCCESS;
00192 }
00193 /* }}} */
00194 
00195 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) /* {{{ */
00196 {
00197        char buf[512], *actual_alias = NULL, *p;
00198        phar_entry_info entry = {0};
00199        size_t pos = 0, read, totalsize;
00200        tar_header *hdr;
00201        php_uint32 sum1, sum2, size, old;
00202        phar_archive_data *myphar, **actual;
00203        int last_was_longlink = 0;
00204 
00205        if (error) {
00206               *error = NULL;
00207        }
00208 
00209        php_stream_seek(fp, 0, SEEK_END);
00210        totalsize = php_stream_tell(fp);
00211        php_stream_seek(fp, 0, SEEK_SET);
00212        read = php_stream_read(fp, buf, sizeof(buf));
00213 
00214        if (read != sizeof(buf)) {
00215               if (error) {
00216                      spprintf(error, 4096, "phar error: \"%s\" is not a tar file or is truncated", fname);
00217               }
00218               php_stream_close(fp);
00219               return FAILURE;
00220        }
00221 
00222        hdr = (tar_header*)buf;
00223        old = (memcmp(hdr->magic, "ustar", sizeof("ustar")-1) != 0);
00224 
00225        myphar = (phar_archive_data *) pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
00226        myphar->is_persistent = PHAR_G(persist);
00227        /* estimate number of entries, can't be certain with tar files */
00228        zend_hash_init(&myphar->manifest, 2 + (totalsize >> 12),
00229               zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)myphar->is_persistent);
00230        zend_hash_init(&myphar->mounted_dirs, 5,
00231               zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent);
00232        zend_hash_init(&myphar->virtual_dirs, 4 + (totalsize >> 11),
00233               zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent);
00234        myphar->is_tar = 1;
00235        /* remember whether this entire phar was compressed with gz/bzip2 */
00236        myphar->flags = compression;
00237 
00238        entry.is_tar = 1;
00239        entry.is_crc_checked = 1;
00240        entry.phar = myphar;
00241        pos += sizeof(buf);
00242 
00243        do {
00244               phar_entry_info *newentry;
00245 
00246               pos = php_stream_tell(fp);
00247               hdr = (tar_header*) buf;
00248               sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum));
00249               if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) {
00250                      break;
00251               }
00252               memset(hdr->checksum, ' ', sizeof(hdr->checksum));
00253               sum2 = phar_tar_checksum(buf, old?sizeof(old_tar_header):sizeof(tar_header));
00254 
00255               size = entry.uncompressed_filesize = entry.compressed_filesize =
00256                      phar_tar_number(hdr->size, sizeof(hdr->size));
00257 
00258               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)) {
00259                      off_t curloc;
00260 
00261                      if (size > 511) {
00262                             if (error) {
00263                                    spprintf(error, 4096, "phar error: tar-based phar \"%s\" has signature that is larger than 511 bytes, cannot process", fname);
00264                             }
00265 bail:
00266                             php_stream_close(fp);
00267                             phar_destroy_phar_data(myphar TSRMLS_CC);
00268                             return FAILURE;
00269                      }
00270                      curloc = php_stream_tell(fp);
00271                      read = php_stream_read(fp, buf, size);
00272                      if (read != size) {
00273                             if (error) {
00274                                    spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be read", fname);
00275                             }
00276                             goto bail;
00277                      }
00278 #ifdef WORDS_BIGENDIAN
00279 # define PHAR_GET_32(buffer) \
00280        (((((unsigned char*)(buffer))[3]) << 24) \
00281               | ((((unsigned char*)(buffer))[2]) << 16) \
00282               | ((((unsigned char*)(buffer))[1]) <<  8) \
00283               | (((unsigned char*)(buffer))[0]))
00284 #else
00285 # define PHAR_GET_32(buffer) (php_uint32) *(buffer)
00286 #endif
00287                      myphar->sig_flags = PHAR_GET_32(buf);
00288                      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)) {
00289                             if (error) {
00290                                    char *save = *error;
00291                                    spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be verified: %s", fname, save);
00292                                    efree(save);
00293                             }
00294                             goto bail;
00295                      }
00296                      php_stream_seek(fp, curloc + 512, SEEK_SET);
00297                      /* signature checked out, let's ensure this is the last file in the phar */
00298                      if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) {
00299                             /* this is not good enough - seek succeeds even on truncated tars */
00300                             php_stream_seek(fp, 512, SEEK_CUR);
00301                             if ((uint)php_stream_tell(fp) > totalsize) {
00302                                    if (error) {
00303                                           spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
00304                                    }
00305                                    php_stream_close(fp);
00306                                    phar_destroy_phar_data(myphar TSRMLS_CC);
00307                                    return FAILURE;
00308                             }
00309                      }
00310 
00311                      read = php_stream_read(fp, buf, sizeof(buf));
00312 
00313                      if (read != sizeof(buf)) {
00314                             if (error) {
00315                                    spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
00316                             }
00317                             php_stream_close(fp);
00318                             phar_destroy_phar_data(myphar TSRMLS_CC);
00319                             return FAILURE;
00320                      }
00321 
00322                      hdr = (tar_header*) buf;
00323                      sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum));
00324 
00325                      if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) {
00326                             break;
00327                      }
00328 
00329                      if (error) {
00330                             spprintf(error, 4096, "phar error: \"%s\" has entries after signature, invalid phar", fname);
00331                      }
00332 
00333                      goto bail;
00334               }
00335 
00336               if (!last_was_longlink && hdr->typeflag == 'L') {
00337                      last_was_longlink = 1;
00338                      /* support the ././@LongLink system for storing long filenames */
00339                      entry.filename_len = entry.uncompressed_filesize;
00340                      entry.filename = pemalloc(entry.filename_len+1, myphar->is_persistent);
00341 
00342                      read = php_stream_read(fp, entry.filename, entry.filename_len);
00343                      if (read != entry.filename_len) {
00344                             efree(entry.filename);
00345                             if (error) {
00346                                    spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
00347                             }
00348                             php_stream_close(fp);
00349                             phar_destroy_phar_data(myphar TSRMLS_CC);
00350                             return FAILURE;
00351                      }
00352                      entry.filename[entry.filename_len] = '\0';
00353 
00354                      /* skip blank stuff */
00355                      size = ((size+511)&~511) - size;
00356 
00357                      /* this is not good enough - seek succeeds even on truncated tars */
00358                      php_stream_seek(fp, size, SEEK_CUR);
00359                      if ((uint)php_stream_tell(fp) > totalsize) {
00360                             efree(entry.filename);
00361                             if (error) {
00362                                    spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
00363                             }
00364                             php_stream_close(fp);
00365                             phar_destroy_phar_data(myphar TSRMLS_CC);
00366                             return FAILURE;
00367                      }
00368 
00369                      read = php_stream_read(fp, buf, sizeof(buf));
00370        
00371                      if (read != sizeof(buf)) {
00372                             efree(entry.filename);
00373                             if (error) {
00374                                    spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
00375                             }
00376                             php_stream_close(fp);
00377                             phar_destroy_phar_data(myphar TSRMLS_CC);
00378                             return FAILURE;
00379                      }
00380                      continue;
00381               } else if (!last_was_longlink && !old && hdr->prefix[0] != 0) {
00382                      char name[256];
00383                      int i, j;
00384 
00385                      for (i = 0; i < 155; i++) {
00386                             name[i] = hdr->prefix[i];
00387                             if (name[i] == '\0') {
00388                                    break;
00389                             }
00390                      }
00391                      name[i++] = '/';
00392                      for (j = 0; j < 100; j++) {
00393                             name[i+j] = hdr->name[j];
00394                             if (name[i+j] == '\0') {
00395                                    break;
00396                             }
00397                      }
00398 
00399                      entry.filename_len = i+j;
00400 
00401                      if (name[entry.filename_len - 1] == '/') {
00402                             /* some tar programs store directories with trailing slash */
00403                             entry.filename_len--;
00404                      }
00405                      entry.filename = pestrndup(name, entry.filename_len, myphar->is_persistent);
00406               } else if (!last_was_longlink) {
00407                      int i;
00408 
00409                      /* calculate strlen, which can be no longer than 100 */
00410                      for (i = 0; i < 100; i++) {
00411                             if (hdr->name[i] == '\0') {
00412                                    break;
00413                             }
00414                      }
00415                      entry.filename_len = i;
00416                      entry.filename = pestrndup(hdr->name, i, myphar->is_persistent);
00417 
00418                      if (entry.filename[entry.filename_len - 1] == '/') {
00419                             /* some tar programs store directories with trailing slash */
00420                             entry.filename[entry.filename_len - 1] = '\0';
00421                             entry.filename_len--;
00422                      }
00423               }
00424               last_was_longlink = 0;
00425 
00426               phar_add_virtual_dirs(myphar, entry.filename, entry.filename_len TSRMLS_CC);
00427 
00428               if (sum1 != sum2) {
00429                      if (error) {
00430                             spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename);
00431                      }
00432                      pefree(entry.filename, myphar->is_persistent);
00433                      php_stream_close(fp);
00434                      phar_destroy_phar_data(myphar TSRMLS_CC);
00435                      return FAILURE;
00436               }
00437 
00438               entry.tar_type = ((old & (hdr->typeflag == '\0')) ? TAR_FILE : hdr->typeflag);
00439               entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */
00440               entry.fp_type = PHAR_FP;
00441               entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK;
00442               entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime));
00443               entry.is_persistent = myphar->is_persistent;
00444 #ifndef S_ISDIR
00445 #define S_ISDIR(mode)       (((mode)&S_IFMT) == S_IFDIR)
00446 #endif
00447               if (old && entry.tar_type == TAR_FILE && S_ISDIR(entry.flags)) {
00448                      entry.tar_type = TAR_DIR;
00449               }
00450 
00451               if (entry.tar_type == TAR_DIR) {
00452                      entry.is_dir = 1;
00453               } else {
00454                      entry.is_dir = 0;
00455               }
00456 
00457               entry.link = NULL;
00458 
00459               if (entry.tar_type == TAR_LINK) {
00460                      if (!zend_hash_exists(&myphar->manifest, hdr->linkname, strlen(hdr->linkname))) {
00461                             if (error) {
00462                                    spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%s\"", fname, hdr->linkname);
00463                             }
00464                             pefree(entry.filename, entry.is_persistent);
00465                             php_stream_close(fp);
00466                             phar_destroy_phar_data(myphar TSRMLS_CC);
00467                             return FAILURE;
00468                      }
00469                      entry.link = estrdup(hdr->linkname);
00470               } else if (entry.tar_type == TAR_SYMLINK) {
00471                      entry.link = estrdup(hdr->linkname);
00472               }
00473               phar_set_inode(&entry TSRMLS_CC);
00474               zend_hash_add(&myphar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), (void **) &newentry);
00475 
00476               if (entry.is_persistent) {
00477                      ++entry.manifest_pos;
00478               }
00479 
00480               if (entry.filename_len >= sizeof(".phar/.metadata")-1 && !memcmp(entry.filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) {
00481                      if (FAILURE == phar_tar_process_metadata(newentry, fp TSRMLS_CC)) {
00482                             if (error) {
00483                                    spprintf(error, 4096, "phar error: tar-based phar \"%s\" has invalid metadata in magic file \"%s\"", fname, entry.filename);
00484                             }
00485                             php_stream_close(fp);
00486                             phar_destroy_phar_data(myphar TSRMLS_CC);
00487                             return FAILURE;
00488                      }
00489               }
00490 
00491               if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
00492                      /* found explicit alias */
00493                      if (size > 511) {
00494                             if (error) {
00495                                    spprintf(error, 4096, "phar error: tar-based phar \"%s\" has alias that is larger than 511 bytes, cannot process", fname);
00496                             }
00497                             php_stream_close(fp);
00498                             phar_destroy_phar_data(myphar TSRMLS_CC);
00499                             return FAILURE;
00500                      }
00501 
00502                      read = php_stream_read(fp, buf, size);
00503 
00504                      if (read == size) {
00505                             buf[size] = '\0';
00506                             if (!phar_validate_alias(buf, size)) {
00507                                    if (size > 50) {
00508                                           buf[50] = '.';
00509                                           buf[51] = '.';
00510                                           buf[52] = '.';
00511                                           buf[53] = '\0';
00512                                    }
00513 
00514                                    if (error) {
00515                                           spprintf(error, 4096, "phar error: invalid alias \"%s\" in tar-based phar \"%s\"", buf, fname);
00516                                    }
00517 
00518                                    php_stream_close(fp);
00519                                    phar_destroy_phar_data(myphar TSRMLS_CC);
00520                                    return FAILURE;
00521                             }
00522 
00523                             actual_alias = pestrndup(buf, size, myphar->is_persistent);
00524                             myphar->alias = actual_alias;
00525                             myphar->alias_len = size;
00526                             php_stream_seek(fp, pos, SEEK_SET);
00527                      } else {
00528                             if (error) {
00529                                    spprintf(error, 4096, "phar error: Unable to read alias from tar-based phar \"%s\"", fname);
00530                             }
00531 
00532                             php_stream_close(fp);
00533                             phar_destroy_phar_data(myphar TSRMLS_CC);
00534                             return FAILURE;
00535                      }
00536               }
00537 
00538               size = (size+511)&~511;
00539 
00540               if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) {
00541                      /* this is not good enough - seek succeeds even on truncated tars */
00542                      php_stream_seek(fp, size, SEEK_CUR);
00543                      if ((uint)php_stream_tell(fp) > totalsize) {
00544                             if (error) {
00545                                    spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
00546                             }
00547                             php_stream_close(fp);
00548                             phar_destroy_phar_data(myphar TSRMLS_CC);
00549                             return FAILURE;
00550                      }
00551               }
00552 
00553               read = php_stream_read(fp, buf, sizeof(buf));
00554 
00555               if (read != sizeof(buf)) {
00556                      if (error) {
00557                             spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
00558                      }
00559                      php_stream_close(fp);
00560                      phar_destroy_phar_data(myphar TSRMLS_CC);
00561                      return FAILURE;
00562               }
00563        } while (read != 0);
00564 
00565        if (zend_hash_exists(&(myphar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
00566               myphar->is_data = 0;
00567        } else {
00568               myphar->is_data = 1;
00569        }
00570 
00571        /* ensure signature set */
00572        if (!myphar->is_data && PHAR_G(require_hash) && !myphar->signature) {
00573               php_stream_close(fp);
00574               phar_destroy_phar_data(myphar TSRMLS_CC);
00575               if (error) {
00576                      spprintf(error, 0, "tar-based phar \"%s\" does not have a signature", fname);
00577               }
00578               return FAILURE;
00579        }
00580 
00581        myphar->fname = pestrndup(fname, fname_len, myphar->is_persistent);
00582 #ifdef PHP_WIN32
00583        phar_unixify_path_separators(myphar->fname, fname_len);
00584 #endif
00585        myphar->fname_len = fname_len;
00586        myphar->fp = fp;
00587        p = strrchr(myphar->fname, '/');
00588 
00589        if (p) {
00590               myphar->ext = memchr(p, '.', (myphar->fname + fname_len) - p);
00591               if (myphar->ext == p) {
00592                      myphar->ext = memchr(p + 1, '.', (myphar->fname + fname_len) - p - 1);
00593               }
00594               if (myphar->ext) {
00595                      myphar->ext_len = (myphar->fname + fname_len) - myphar->ext;
00596               }
00597        }
00598 
00599        phar_request_initialize(TSRMLS_C);
00600 
00601        if (SUCCESS != zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len, (void*)&myphar, sizeof(phar_archive_data*), (void **)&actual)) {
00602               if (error) {
00603                      spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\" to phar registry", fname);
00604               }
00605               php_stream_close(fp);
00606               phar_destroy_phar_data(myphar TSRMLS_CC);
00607               return FAILURE;
00608        }
00609 
00610        myphar = *actual;
00611 
00612        if (actual_alias) {
00613               phar_archive_data **fd_ptr;
00614 
00615               myphar->is_temporary_alias = 0;
00616 
00617               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void **)&fd_ptr)) {
00618                      if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, myphar->alias_len TSRMLS_CC)) {
00619                             if (error) {
00620                                    spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname);
00621                             }
00622                             zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len);
00623                             return FAILURE;
00624                      }
00625               }
00626 
00627               zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL);
00628        } else {
00629               phar_archive_data **fd_ptr;
00630 
00631               if (alias_len) {
00632                      if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
00633                             if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
00634                                    if (error) {
00635                                           spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname);
00636                                    }
00637                                    zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len);
00638                                    return FAILURE;
00639                             }
00640                      }
00641                      zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL);
00642                      myphar->alias = pestrndup(alias, alias_len, myphar->is_persistent);
00643                      myphar->alias_len = alias_len;
00644               } else {
00645                      myphar->alias = pestrndup(myphar->fname, fname_len, myphar->is_persistent);
00646                      myphar->alias_len = fname_len;
00647               }
00648 
00649               myphar->is_temporary_alias = 1;
00650        }
00651 
00652        if (pphar) {
00653               *pphar = myphar;
00654        }
00655 
00656        return SUCCESS;
00657 }
00658 /* }}} */
00659 
00660 struct _phar_pass_tar_info {
00661        php_stream *old;
00662        php_stream *new;
00663        int free_fp;
00664        int free_ufp;
00665        char **error;
00666 };
00667 
00668 static int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */
00669 {
00670        tar_header header;
00671        size_t pos;
00672        phar_entry_info *entry = (phar_entry_info *) pDest;
00673        struct _phar_pass_tar_info *fp = (struct _phar_pass_tar_info *)argument;
00674        char padding[512];
00675 
00676        if (entry->is_mounted) {
00677               return ZEND_HASH_APPLY_KEEP;
00678        }
00679 
00680        if (entry->is_deleted) {
00681               if (entry->fp_refcount <= 0) {
00682                      return ZEND_HASH_APPLY_REMOVE;
00683               } else {
00684                      /* we can't delete this in-memory until it is closed */
00685                      return ZEND_HASH_APPLY_KEEP;
00686               }
00687        }
00688 
00689        phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len TSRMLS_CC);
00690        memset((char *) &header, 0, sizeof(header));
00691 
00692        if (entry->filename_len > 100) {
00693               char *boundary;
00694               if (entry->filename_len > 256) {
00695                      if (fp->error) {
00696                             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);
00697                      }
00698                      return ZEND_HASH_APPLY_STOP;
00699               }
00700               boundary = entry->filename + entry->filename_len - 101;
00701               while (*boundary && *boundary != '/') {
00702                      ++boundary;
00703               }
00704               if (!*boundary || ((boundary - entry->filename) > 155)) {
00705                      if (fp->error) {
00706                             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);
00707                      }
00708                      return ZEND_HASH_APPLY_STOP;
00709               }
00710               memcpy(header.prefix, entry->filename, boundary - entry->filename);
00711               memcpy(header.name, boundary + 1, entry->filename_len - (boundary + 1 - entry->filename));
00712        } else {
00713               memcpy(header.name, entry->filename, entry->filename_len);
00714        }
00715 
00716        phar_tar_octal(header.mode, entry->flags & PHAR_ENT_PERM_MASK, sizeof(header.mode)-1);
00717 
00718        if (FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) {
00719               if (fp->error) {
00720                      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);
00721               }
00722               return ZEND_HASH_APPLY_STOP;
00723        }
00724 
00725        if (FAILURE == phar_tar_octal(header.mtime, entry->timestamp, sizeof(header.mtime)-1)) {
00726               if (fp->error) {
00727                      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);
00728               }
00729               return ZEND_HASH_APPLY_STOP;
00730        }
00731 
00732        /* calc checksum */
00733        header.typeflag = entry->tar_type;
00734 
00735        if (entry->link) {
00736               strncpy(header.linkname, entry->link, strlen(entry->link));
00737        }
00738 
00739        strncpy(header.magic, "ustar", sizeof("ustar")-1);
00740        strncpy(header.version, "00", sizeof("00")-1);
00741        strncpy(header.checksum, "        ", sizeof("        ")-1);
00742        entry->crc32 = phar_tar_checksum((char *)&header, sizeof(header));
00743 
00744        if (FAILURE == phar_tar_octal(header.checksum, entry->crc32, sizeof(header.checksum)-1)) {
00745               if (fp->error) {
00746                      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);
00747               }
00748               return ZEND_HASH_APPLY_STOP;
00749        }
00750 
00751        /* write header */
00752        entry->header_offset = php_stream_tell(fp->new);
00753 
00754        if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) {
00755               if (fp->error) {
00756                      spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for  file \"%s\" could not be written", entry->phar->fname, entry->filename);
00757               }
00758               return ZEND_HASH_APPLY_STOP;
00759        }
00760 
00761        pos = php_stream_tell(fp->new); /* save start of file within tar */
00762 
00763        /* write contents */
00764        if (entry->uncompressed_filesize) {
00765               if (FAILURE == phar_open_entry_fp(entry, fp->error, 0 TSRMLS_CC)) {
00766                      return ZEND_HASH_APPLY_STOP;
00767               }
00768 
00769               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
00770                      if (fp->error) {
00771                             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);
00772                      }
00773                      return ZEND_HASH_APPLY_STOP;
00774               }
00775 
00776               if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), fp->new, entry->uncompressed_filesize, NULL)) {
00777                      if (fp->error) {
00778                             spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename);
00779                      }
00780                      return ZEND_HASH_APPLY_STOP;
00781               }
00782 
00783               memset(padding, 0, 512);
00784               php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize);
00785        }
00786 
00787        if (!entry->is_modified && entry->fp_refcount) {
00788               /* open file pointers refer to this fp, do not free the stream */
00789               switch (entry->fp_type) {
00790                      case PHAR_FP:
00791                             fp->free_fp = 0;
00792                             break;
00793                      case PHAR_UFP:
00794                             fp->free_ufp = 0;
00795                      default:
00796                             break;
00797               }
00798        }
00799 
00800        entry->is_modified = 0;
00801 
00802        if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
00803               if (!entry->fp_refcount) {
00804                      php_stream_close(entry->fp);
00805               }
00806               entry->fp = NULL;
00807        }
00808 
00809        entry->fp_type = PHAR_FP;
00810 
00811        /* note new location within tar */
00812        entry->offset = entry->offset_abs = pos;
00813        return ZEND_HASH_APPLY_KEEP;
00814 }
00815 /* }}} */
00816 
00817 int phar_tar_setmetadata(zval *metadata, phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */
00818 {
00819        php_serialize_data_t metadata_hash;
00820 
00821        if (entry->metadata_str.c) {
00822               smart_str_free(&entry->metadata_str);
00823        }
00824 
00825        entry->metadata_str.c = 0;
00826        entry->metadata_str.len = 0;
00827        PHP_VAR_SERIALIZE_INIT(metadata_hash);
00828        php_var_serialize(&entry->metadata_str, &metadata, &metadata_hash TSRMLS_CC);
00829        PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
00830        entry->uncompressed_filesize = entry->compressed_filesize = entry->metadata_str.len;
00831 
00832        if (entry->fp && entry->fp_type == PHAR_MOD) {
00833               php_stream_close(entry->fp);
00834        }
00835 
00836        entry->fp_type = PHAR_MOD;
00837        entry->is_modified = 1;
00838        entry->fp = php_stream_fopen_tmpfile();
00839        entry->offset = entry->offset_abs = 0;
00840 
00841        if (entry->metadata_str.len != php_stream_write(entry->fp, entry->metadata_str.c, entry->metadata_str.len)) {
00842               spprintf(error, 0, "phar tar error: unable to write metadata to magic metadata file \"%s\"", entry->filename);
00843               zend_hash_del(&(entry->phar->manifest), entry->filename, entry->filename_len);
00844               return ZEND_HASH_APPLY_STOP;
00845        }
00846 
00847        return ZEND_HASH_APPLY_KEEP;
00848 }
00849 /* }}} */
00850 
00851 static int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) /* {{{ */
00852 {
00853        int lookfor_len;
00854        struct _phar_pass_tar_info *i = (struct _phar_pass_tar_info *)argument;
00855        char *lookfor, **error = i->error;
00856        phar_entry_info *entry = (phar_entry_info *)pDest, *metadata, newentry = {0};
00857 
00858        if (entry->filename_len >= sizeof(".phar/.metadata") && !memcmp(entry->filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) {
00859               if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) {
00860                      return phar_tar_setmetadata(entry->phar->metadata, entry, error TSRMLS_CC);
00861               }
00862               /* search for the file this metadata entry references */
00863               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))) {
00864                      /* this is orphaned metadata, erase it */
00865                      return ZEND_HASH_APPLY_REMOVE;
00866               }
00867               /* we can keep this entry, the file that refers to it exists */
00868               return ZEND_HASH_APPLY_KEEP;
00869        }
00870 
00871        if (!entry->is_modified) {
00872               return ZEND_HASH_APPLY_KEEP;
00873        }
00874 
00875        /* now we are dealing with regular files, so look for metadata */
00876        lookfor_len = spprintf(&lookfor, 0, ".phar/.metadata/%s/.metadata.bin", entry->filename);
00877 
00878        if (!entry->metadata) {
00879               zend_hash_del(&(entry->phar->manifest), lookfor, lookfor_len);
00880               efree(lookfor);
00881               return ZEND_HASH_APPLY_KEEP;
00882        }
00883 
00884        if (SUCCESS == zend_hash_find(&(entry->phar->manifest), lookfor, lookfor_len, (void **)&metadata)) {
00885               int ret;
00886               ret = phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC);
00887               efree(lookfor);
00888               return ret;
00889        }
00890 
00891        newentry.filename = lookfor;
00892        newentry.filename_len = lookfor_len;
00893        newentry.phar = entry->phar;
00894        newentry.tar_type = TAR_FILE;
00895        newentry.is_tar = 1;
00896 
00897        if (SUCCESS != zend_hash_add(&(entry->phar->manifest), lookfor, lookfor_len, (void *)&newentry, sizeof(phar_entry_info), (void **)&metadata)) {
00898               efree(lookfor);
00899               spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for file \"%s\"", entry->filename);
00900               return ZEND_HASH_APPLY_STOP;
00901        }
00902 
00903        return phar_tar_setmetadata(entry->metadata, metadata, error TSRMLS_CC);
00904 }
00905 /* }}} */
00906 
00907 int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */
00908 {
00909        phar_entry_info entry = {0};
00910        static const char newstub[] = "<?php // tar-based phar archive stub file\n__HALT_COMPILER();";
00911        php_stream *oldfile, *newfile, *stubfile;
00912        int closeoldfile, free_user_stub, signature_length;
00913        struct _phar_pass_tar_info pass;
00914        char *buf, *signature, *tmp, sigbuf[8];
00915        char halt_stub[] = "__HALT_COMPILER();";
00916 
00917        entry.flags = PHAR_ENT_PERM_DEF_FILE;
00918        entry.timestamp = time(NULL);
00919        entry.is_modified = 1;
00920        entry.is_crc_checked = 1;
00921        entry.is_tar = 1;
00922        entry.tar_type = '0';
00923        entry.phar = phar;
00924        entry.fp_type = PHAR_MOD;
00925 
00926        if (phar->is_persistent) {
00927               if (error) {
00928                      spprintf(error, 0, "internal error: attempt to flush cached tar-based phar \"%s\"", phar->fname);
00929               }
00930               return EOF;
00931        }
00932 
00933        if (phar->is_data) {
00934               goto nostub;
00935        }
00936 
00937        /* set alias */
00938        if (!phar->is_temporary_alias && phar->alias_len) {
00939               entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
00940               entry.filename_len = sizeof(".phar/alias.txt")-1;
00941               entry.fp = php_stream_fopen_tmpfile();
00942 
00943               if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
00944                      if (error) {
00945                             spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
00946                      }
00947                      return EOF;
00948               }
00949 
00950               entry.uncompressed_filesize = phar->alias_len;
00951 
00952               if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
00953                      if (error) {
00954                             spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
00955                      }
00956                      return EOF;
00957               }
00958        } else {
00959               zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
00960        }
00961 
00962        /* set stub */
00963        if (user_stub && !defaultstub) {
00964               char *pos;
00965               if (len < 0) {
00966                      /* resource passed in */
00967                      if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
00968                             if (error) {
00969                                    spprintf(error, 0, "unable to access resource to copy stub to new tar-based phar \"%s\"", phar->fname);
00970                             }
00971                             return EOF;
00972                      }
00973                      if (len == -1) {
00974                             len = PHP_STREAM_COPY_ALL;
00975                      } else {
00976                             len = -len;
00977                      }
00978                      user_stub = 0;
00979 #if PHP_MAJOR_VERSION >= 6
00980                      if (!(len = php_stream_copy_to_mem(stubfile, (void **) &user_stub, len, 0)) || !user_stub) {
00981 #else
00982                      if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
00983 #endif
00984                             if (error) {
00985                                    spprintf(error, 0, "unable to read resource to copy stub to new tar-based phar \"%s\"", phar->fname);
00986                             }
00987                             return EOF;
00988                      }
00989                      free_user_stub = 1;
00990               } else {
00991                      free_user_stub = 0;
00992               }
00993 
00994               tmp = estrndup(user_stub, len);
00995               if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
00996                      efree(tmp);
00997                      if (error) {
00998                             spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname);
00999                      }
01000                      if (free_user_stub) {
01001                             efree(user_stub);
01002                      }
01003                      return EOF;
01004               }
01005               pos = user_stub + (pos - tmp);
01006               efree(tmp);
01007 
01008               len = pos - user_stub + 18;
01009               entry.fp = php_stream_fopen_tmpfile();
01010               entry.uncompressed_filesize = len + 5;
01011 
01012               if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
01013               ||            5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
01014                      if (error) {
01015                             spprintf(error, 0, "unable to create stub from string in new tar-based phar \"%s\"", phar->fname);
01016                      }
01017                      if (free_user_stub) {
01018                             efree(user_stub);
01019                      }
01020                      php_stream_close(entry.fp);
01021                      return EOF;
01022               }
01023 
01024               entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
01025               entry.filename_len = sizeof(".phar/stub.php")-1;
01026               zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
01027 
01028               if (free_user_stub) {
01029                      efree(user_stub);
01030               }
01031        } else {
01032               /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
01033               entry.fp = php_stream_fopen_tmpfile();
01034 
01035               if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
01036                      php_stream_close(entry.fp);
01037                      if (error) {
01038                             spprintf(error, 0, "unable to %s stub in%star-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
01039                      }
01040                      return EOF;
01041               }
01042 
01043               entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
01044               entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
01045               entry.filename_len = sizeof(".phar/stub.php")-1;
01046 
01047               if (!defaultstub) {
01048                      if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
01049                             if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
01050                                    php_stream_close(entry.fp);
01051                                    efree(entry.filename);
01052                                    if (error) {
01053                                           spprintf(error, 0, "unable to create stub in tar-based phar \"%s\"", phar->fname);
01054                                    }
01055                                    return EOF;
01056                             }
01057                      } else {
01058                             php_stream_close(entry.fp);
01059                             efree(entry.filename);
01060                      }
01061               } else {
01062                      if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
01063                             php_stream_close(entry.fp);
01064                             efree(entry.filename);
01065                             if (error) {
01066                                    spprintf(error, 0, "unable to overwrite stub in tar-based phar \"%s\"", phar->fname);
01067                             }
01068                             return EOF;
01069                      }
01070               }
01071        }
01072 nostub:
01073        if (phar->fp && !phar->is_brandnew) {
01074               oldfile = phar->fp;
01075               closeoldfile = 0;
01076               php_stream_rewind(oldfile);
01077        } else {
01078               oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
01079               closeoldfile = oldfile != NULL;
01080        }
01081 
01082        newfile = php_stream_fopen_tmpfile();
01083 
01084        if (!newfile) {
01085               if (error) {
01086                      spprintf(error, 0, "unable to create temporary file");
01087               }
01088               if (closeoldfile) {
01089                      php_stream_close(oldfile);
01090               }
01091               return EOF;
01092        }
01093 
01094        pass.old = oldfile;
01095        pass.new = newfile;
01096        pass.error = error;
01097        pass.free_fp = 1;
01098        pass.free_ufp = 1;
01099 
01100        if (phar->metadata) {
01101               phar_entry_info *mentry;
01102               if (SUCCESS == zend_hash_find(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void **)&mentry)) {
01103                      if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) {
01104                             if (closeoldfile) {
01105                                    php_stream_close(oldfile);
01106                             }
01107                             return EOF;
01108                      }
01109               } else {
01110                      phar_entry_info newentry = {0};
01111 
01112                      newentry.filename = estrndup(".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1);
01113                      newentry.filename_len = sizeof(".phar/.metadata.bin")-1;
01114                      newentry.phar = phar;
01115                      newentry.tar_type = TAR_FILE;
01116                      newentry.is_tar = 1;
01117 
01118                      if (SUCCESS != zend_hash_add(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void *)&newentry, sizeof(phar_entry_info), (void **)&mentry)) {
01119                             spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for phar archive \"%s\"", phar->fname);
01120                             if (closeoldfile) {
01121                                    php_stream_close(oldfile);
01122                             }
01123                             return EOF;
01124                      }
01125 
01126                      if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error TSRMLS_CC)) {
01127                             zend_hash_del(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1);
01128                             if (closeoldfile) {
01129                                    php_stream_close(oldfile);
01130                             }
01131                             return EOF;
01132                      }
01133               }
01134        }
01135 
01136        zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_setupmetadata, (void *) &pass TSRMLS_CC);
01137 
01138        if (error && *error) {
01139               if (closeoldfile) {
01140                      php_stream_close(oldfile);
01141               }
01142 
01143               /* on error in the hash iterator above, error is set */
01144               php_stream_close(newfile);
01145               return EOF;
01146        }
01147 
01148        zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_writeheaders, (void *) &pass TSRMLS_CC);
01149 
01150        /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
01151        if (!phar->is_data || phar->sig_flags) {
01152               if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, error TSRMLS_CC)) {
01153                      if (error) {
01154                             char *save = *error;
01155                             spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", save);
01156                             efree(save);
01157                      }
01158 
01159                      if (closeoldfile) {
01160                             php_stream_close(oldfile);
01161                      }
01162 
01163                      php_stream_close(newfile);
01164                      return EOF;
01165               }
01166 
01167               entry.filename = ".phar/signature.bin";
01168               entry.filename_len = sizeof(".phar/signature.bin")-1;
01169               entry.fp = php_stream_fopen_tmpfile();
01170 
01171 #ifdef WORDS_BIGENDIAN
01172 # define PHAR_SET_32(var, buffer) \
01173        *(php_uint32 *)(var) = (((((unsigned char*)&(buffer))[3]) << 24) \
01174               | ((((unsigned char*)&(buffer))[2]) << 16) \
01175               | ((((unsigned char*)&(buffer))[1]) << 8) \
01176               | (((unsigned char*)&(buffer))[0]))
01177 #else
01178 # define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer)
01179 #endif
01180               PHAR_SET_32(sigbuf, phar->sig_flags);
01181               PHAR_SET_32(sigbuf + 4, signature_length);
01182 
01183               if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) {
01184                      efree(signature);
01185                      if (error) {
01186                             spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", phar->fname);
01187                      }
01188 
01189                      if (closeoldfile) {
01190                             php_stream_close(oldfile);
01191                      }
01192                      php_stream_close(newfile);
01193                      return EOF;
01194               }
01195 
01196               efree(signature);
01197               entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
01198               /* throw out return value and write the signature */
01199               entry.filename_len = phar_tar_writeheaders((void *)&entry, (void *)&pass TSRMLS_CC);
01200 
01201               if (error && *error) {
01202                      if (closeoldfile) {
01203                             php_stream_close(oldfile);
01204                      }
01205                      /* error is set by writeheaders */
01206                      php_stream_close(newfile);
01207                      return EOF;
01208               }
01209        } /* signature */
01210 
01211        /* add final zero blocks */
01212        buf = (char *) ecalloc(1024, 1);
01213        php_stream_write(newfile, buf, 1024);
01214        efree(buf);
01215 
01216        if (closeoldfile) {
01217               php_stream_close(oldfile);
01218        }
01219 
01220        /* on error in the hash iterator above, error is set */
01221        if (error && *error) {
01222               php_stream_close(newfile);
01223               return EOF;
01224        }
01225 
01226        if (phar->fp && pass.free_fp) {
01227               php_stream_close(phar->fp);
01228        }
01229 
01230        if (phar->ufp) {
01231               if (pass.free_ufp) {
01232                      php_stream_close(phar->ufp);
01233               }
01234               phar->ufp = NULL;
01235        }
01236 
01237        phar->is_brandnew = 0;
01238        php_stream_rewind(newfile);
01239 
01240        if (phar->donotflush) {
01241               /* deferred flush */
01242               phar->fp = newfile;
01243        } else {
01244               phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
01245               if (!phar->fp) {
01246                      phar->fp = newfile;
01247                      if (error) {
01248                             spprintf(error, 0, "unable to open new phar \"%s\" for writing", phar->fname);
01249                      }
01250                      return EOF;
01251               }
01252 
01253               if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
01254                      php_stream_filter *filter;
01255                      /* to properly compress, we have to tell zlib to add a zlib header */
01256                      zval filterparams;
01257 
01258                      array_init(&filterparams);
01259 /* this is defined in zlib's zconf.h */
01260 #ifndef MAX_WBITS
01261 #define MAX_WBITS 15
01262 #endif
01263                      add_assoc_long(&filterparams, "window", MAX_WBITS + 16);
01264                      filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC);
01265                      zval_dtor(&filterparams);
01266 
01267                      if (!filter) {
01268                             /* copy contents uncompressed rather than lose them */
01269                             phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
01270                             php_stream_close(newfile);
01271                             if (error) {
01272                                    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);
01273                             }
01274                             return EOF;
01275                      }
01276 
01277                      php_stream_filter_append(&phar->fp->writefilters, filter);
01278                      phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
01279                      php_stream_filter_flush(filter, 1);
01280                      php_stream_filter_remove(filter, 1 TSRMLS_CC);
01281                      php_stream_close(phar->fp);
01282                      /* use the temp stream as our base */
01283                      phar->fp = newfile;
01284               } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
01285                      php_stream_filter *filter;
01286 
01287                      filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC);
01288                      php_stream_filter_append(&phar->fp->writefilters, filter);
01289                      phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
01290                      php_stream_filter_flush(filter, 1);
01291                      php_stream_filter_remove(filter, 1 TSRMLS_CC);
01292                      php_stream_close(phar->fp);
01293                      /* use the temp stream as our base */
01294                      phar->fp = newfile;
01295               } else {
01296                      phar_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
01297                      /* we could also reopen the file in "rb" mode but there is no need for that */
01298                      php_stream_close(newfile);
01299               }
01300        }
01301        return EOF;
01302 }
01303 /* }}} */
01304 
01305 /*
01306  * Local variables:
01307  * tab-width: 4
01308  * c-basic-offset: 4
01309  * End:
01310  * vim600: noet sw=4 ts=4 fdm=marker
01311  * vim<600: noet sw=4 ts=4
01312  */