PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - phar - tar.c
Test: PHP Code Coverage
Date: 2009-11-21 Instrumented lines: 707
Code covered: 71.6 % Executed lines: 506
Legend: not executed executed

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

Generated by: LTP GCOV extension version 1.5

Generated at Sat, 21 Nov 2009 12:27:06 +0000 (3 days ago)

Copyright © 2005-2009 The PHP Group
All rights reserved.