Annotation of embedaddon/php/ext/phar/tar.c, revision 1.1.1.3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>