Annotation of embedaddon/php/ext/phar/stream.c, revision 1.1

1.1     ! misho       1: /*
        !             2:   +----------------------------------------------------------------------+
        !             3:   | phar:// stream wrapper support                                       |
        !             4:   +----------------------------------------------------------------------+
        !             5:   | Copyright (c) 2005-2012 The PHP Group                                |
        !             6:   +----------------------------------------------------------------------+
        !             7:   | This source file is subject to version 3.01 of the PHP license,      |
        !             8:   | that is bundled with this package in the file LICENSE, and is        |
        !             9:   | available through the world-wide-web at the following url:           |
        !            10:   | http://www.php.net/license/3_01.txt.                                 |
        !            11:   | If you did not receive a copy of the PHP license and are unable to   |
        !            12:   | obtain it through the world-wide-web, please send a note to          |
        !            13:   | license@php.net so we can mail you a copy immediately.               |
        !            14:   +----------------------------------------------------------------------+
        !            15:   | Authors: Gregory Beaver <cellog@php.net>                             |
        !            16:   |          Marcus Boerger <helly@php.net>                              |
        !            17:   +----------------------------------------------------------------------+
        !            18: */
        !            19: 
        !            20: #define PHAR_STREAM 1
        !            21: #include "phar_internal.h"
        !            22: #include "stream.h"
        !            23: #include "dirstream.h"
        !            24: 
        !            25: php_stream_ops phar_ops = {
        !            26:        phar_stream_write, /* write */
        !            27:        phar_stream_read,  /* read */
        !            28:        phar_stream_close, /* close */
        !            29:        phar_stream_flush, /* flush */
        !            30:        "phar stream",
        !            31:        phar_stream_seek,  /* seek */
        !            32:        NULL,              /* cast */
        !            33:        phar_stream_stat,  /* stat */
        !            34:        NULL, /* set option */
        !            35: };
        !            36: 
        !            37: php_stream_wrapper_ops phar_stream_wops = {
        !            38:        phar_wrapper_open_url,
        !            39:        NULL,                  /* phar_wrapper_close */
        !            40:        NULL,                  /* phar_wrapper_stat, */
        !            41:        phar_wrapper_stat,     /* stat_url */
        !            42:        phar_wrapper_open_dir, /* opendir */
        !            43:        "phar",
        !            44:        phar_wrapper_unlink,   /* unlink */
        !            45:        phar_wrapper_rename,   /* rename */
        !            46:        phar_wrapper_mkdir,    /* create directory */
        !            47:        phar_wrapper_rmdir,    /* remove directory */
        !            48: };
        !            49: 
        !            50: php_stream_wrapper php_stream_phar_wrapper = {
        !            51:        &phar_stream_wops,
        !            52:        NULL,
        !            53:        0 /* is_url */
        !            54: };
        !            55: 
        !            56: /**
        !            57:  * Open a phar file for streams API
        !            58:  */
        !            59: php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC) /* {{{ */
        !            60: {
        !            61:        php_url *resource;
        !            62:        char *arch = NULL, *entry = NULL, *error;
        !            63:        int arch_len, entry_len;
        !            64: 
        !            65:        if (strlen(filename) < 7 || strncasecmp(filename, "phar://", 7)) {
        !            66:                return NULL;
        !            67:        }
        !            68:        if (mode[0] == 'a') {
        !            69:                if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
        !            70:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: open mode append not supported");
        !            71:                }
        !            72:                return NULL;
        !            73:        }
        !            74:        if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0) TSRMLS_CC) == FAILURE) {
        !            75:                if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
        !            76:                        if (arch && !entry) {
        !            77:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch);
        !            78:                                arch = NULL;
        !            79:                        } else {
        !            80:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url or non-existent phar \"%s\"", filename);
        !            81:                        }
        !            82:                }
        !            83:                return NULL;
        !            84:        }
        !            85:        resource = ecalloc(1, sizeof(php_url));
        !            86:        resource->scheme = estrndup("phar", 4);
        !            87:        resource->host = arch;
        !            88: 
        !            89:        resource->path = entry;
        !            90: #if MBO_0
        !            91:                if (resource) {
        !            92:                        fprintf(stderr, "Alias:     %s\n", alias);
        !            93:                        fprintf(stderr, "Scheme:    %s\n", resource->scheme);
        !            94: /*                     fprintf(stderr, "User:      %s\n", resource->user);*/
        !            95: /*                     fprintf(stderr, "Pass:      %s\n", resource->pass ? "***" : NULL);*/
        !            96:                        fprintf(stderr, "Host:      %s\n", resource->host);
        !            97: /*                     fprintf(stderr, "Port:      %d\n", resource->port);*/
        !            98:                        fprintf(stderr, "Path:      %s\n", resource->path);
        !            99: /*                     fprintf(stderr, "Query:     %s\n", resource->query);*/
        !           100: /*                     fprintf(stderr, "Fragment:  %s\n", resource->fragment);*/
        !           101:                }
        !           102: #endif
        !           103:        if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
        !           104:                phar_archive_data **pphar = NULL, *phar;
        !           105: 
        !           106:                if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_fname_map.arBuckets && FAILURE == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **)&pphar)) {
        !           107:                        pphar = NULL;
        !           108:                }
        !           109:                if (PHAR_G(readonly) && (!pphar || !(*pphar)->is_data)) {
        !           110:                        if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
        !           111:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by the php.ini setting phar.readonly");
        !           112:                        }
        !           113:                        php_url_free(resource);
        !           114:                        return NULL;
        !           115:                }
        !           116:                if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, 0, options, &phar, &error TSRMLS_CC) == FAILURE)
        !           117:                {
        !           118:                        if (error) {
        !           119:                                if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
        !           120:                                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
        !           121:                                }
        !           122:                                efree(error);
        !           123:                        }
        !           124:                        php_url_free(resource);
        !           125:                        return NULL;
        !           126:                }
        !           127:                if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
        !           128:                        if (error) {
        !           129:                                spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", resource->host);
        !           130:                                if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
        !           131:                                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
        !           132:                                }
        !           133:                                efree(error);
        !           134:                        }
        !           135:                        php_url_free(resource);
        !           136:                        return NULL;
        !           137:                }
        !           138:        } else {
        !           139:                if (phar_open_from_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE)
        !           140:                {
        !           141:                        if (error) {
        !           142:                                if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
        !           143:                                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
        !           144:                                }
        !           145:                                efree(error);
        !           146:                        }
        !           147:                        php_url_free(resource);
        !           148:                        return NULL;
        !           149:                }
        !           150:        }
        !           151:        return resource;
        !           152: }
        !           153: /* }}} */
        !           154: 
        !           155: /**
        !           156:  * used for fopen('phar://...') and company
        !           157:  */
        !           158: static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
        !           159: {
        !           160:        phar_archive_data *phar;
        !           161:        phar_entry_data *idata;
        !           162:        char *internal_file;
        !           163:        char *error;
        !           164:        HashTable *pharcontext;
        !           165:        php_url *resource = NULL;
        !           166:        php_stream *fpf;
        !           167:        zval **pzoption, *metadata;
        !           168:        uint host_len;
        !           169: 
        !           170:        if ((resource = phar_parse_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
        !           171:                return NULL;
        !           172:        }
        !           173: 
        !           174:        /* we must have at the very least phar://alias.phar/internalfile.php */
        !           175:        if (!resource->scheme || !resource->host || !resource->path) {
        !           176:                php_url_free(resource);
        !           177:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", path);
        !           178:                return NULL;
        !           179:        }
        !           180: 
        !           181:        if (strcasecmp("phar", resource->scheme)) {
        !           182:                php_url_free(resource);
        !           183:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", path);
        !           184:                return NULL;
        !           185:        }
        !           186: 
        !           187:        host_len = strlen(resource->host);
        !           188:        phar_request_initialize(TSRMLS_C);
        !           189: 
        !           190:        /* strip leading "/" */
        !           191:        internal_file = estrdup(resource->path + 1);
        !           192:        if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
        !           193:                if (NULL == (idata = phar_get_or_create_entry_data(resource->host, host_len, internal_file, strlen(internal_file), mode, 0, &error, 1 TSRMLS_CC))) {
        !           194:                        if (error) {
        !           195:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
        !           196:                                efree(error);
        !           197:                        } else {
        !           198:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, resource->host);
        !           199:                        }
        !           200:                        efree(internal_file);
        !           201:                        php_url_free(resource);
        !           202:                        return NULL;
        !           203:                }
        !           204:                if (error) {
        !           205:                        efree(error);
        !           206:                }
        !           207:                fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
        !           208:                php_url_free(resource);
        !           209:                efree(internal_file);
        !           210: #if PHP_MAJOR_VERSION >= 6
        !           211:                if (context && context->options && phar_find_key(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption TSRMLS_CC)) {
        !           212: #else
        !           213:                if (context && context->options && zend_hash_find(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption) == SUCCESS) {
        !           214: #endif
        !           215:                        pharcontext = HASH_OF(*pzoption);
        !           216:                        if (idata->internal_file->uncompressed_filesize == 0
        !           217:                                && idata->internal_file->compressed_filesize == 0
        !           218: #if PHP_MAJOR_VERSION >= 6
        !           219:                                && phar_find_key(pharcontext, "compress", sizeof("compress"), (void**)&pzoption TSRMLS_CC)
        !           220: #else
        !           221:                                && zend_hash_find(pharcontext, "compress", sizeof("compress"), (void**)&pzoption) == SUCCESS
        !           222: #endif
        !           223:                                && Z_TYPE_PP(pzoption) == IS_LONG
        !           224:                                && (Z_LVAL_PP(pzoption) & ~PHAR_ENT_COMPRESSION_MASK) == 0
        !           225:                        ) {
        !           226:                                idata->internal_file->flags &= ~PHAR_ENT_COMPRESSION_MASK;
        !           227:                                idata->internal_file->flags |= Z_LVAL_PP(pzoption);
        !           228:                        }
        !           229: #if PHP_MAJOR_VERSION >= 6
        !           230:                        if (phar_find_key(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption TSRMLS_CC)) {
        !           231: #else
        !           232:                        if (zend_hash_find(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption) == SUCCESS) {
        !           233: #endif
        !           234:                                if (idata->internal_file->metadata) {
        !           235:                                        zval_ptr_dtor(&idata->internal_file->metadata);
        !           236:                                        idata->internal_file->metadata = NULL;
        !           237:                                }
        !           238: 
        !           239:                                MAKE_STD_ZVAL(idata->internal_file->metadata);
        !           240:                                metadata = *pzoption;
        !           241:                                ZVAL_ZVAL(idata->internal_file->metadata, metadata, 1, 0);
        !           242:                                idata->phar->is_modified = 1;
        !           243:                        }
        !           244:                }
        !           245:                if (opened_path) {
        !           246:                        spprintf(opened_path, MAXPATHLEN, "phar://%s/%s", idata->phar->fname, idata->internal_file->filename);
        !           247:                }
        !           248:                return fpf;
        !           249:        } else {
        !           250:                if (!*internal_file && (options & STREAM_OPEN_FOR_INCLUDE)) {
        !           251:                        /* retrieve the stub */
        !           252:                        if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, NULL TSRMLS_CC)) {
        !           253:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "file %s is not a valid phar archive", resource->host);
        !           254:                                efree(internal_file);
        !           255:                                php_url_free(resource);
        !           256:                                return NULL;
        !           257:                        }
        !           258:                        if (phar->is_tar || phar->is_zip) {
        !           259:                                if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, ".phar/stub.php", sizeof(".phar/stub.php")-1, "r", 0, &error, 0 TSRMLS_CC)) || !idata) {
        !           260:                                        goto idata_error;
        !           261:                                }
        !           262:                                efree(internal_file);
        !           263:                                if (opened_path) {
        !           264:                                        spprintf(opened_path, MAXPATHLEN, "%s", phar->fname);
        !           265:                                }
        !           266:                                php_url_free(resource);
        !           267:                                goto phar_stub;
        !           268:                        } else {
        !           269:                                phar_entry_info *entry;
        !           270: 
        !           271:                                entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info));
        !           272:                                entry->is_temp_dir = 1;
        !           273:                                entry->filename = estrndup("", 0);
        !           274:                                entry->filename_len = 0;
        !           275:                                entry->phar = phar;
        !           276:                                entry->offset = entry->offset_abs = 0;
        !           277:                                entry->compressed_filesize = entry->uncompressed_filesize = phar->halt_offset;
        !           278:                                entry->is_crc_checked = 1;
        !           279: 
        !           280:                                idata = (phar_entry_data *) ecalloc(1, sizeof(phar_entry_data));
        !           281:                                idata->fp = phar_get_pharfp(phar TSRMLS_CC);
        !           282:                                idata->phar = phar;
        !           283:                                idata->internal_file = entry;
        !           284:                                if (!phar->is_persistent) {
        !           285:                                        ++(entry->phar->refcount);
        !           286:                                }
        !           287:                                ++(entry->fp_refcount);
        !           288:                                php_url_free(resource);
        !           289:                                if (opened_path) {
        !           290:                                        spprintf(opened_path, MAXPATHLEN, "%s", phar->fname);
        !           291:                                }
        !           292:                                efree(internal_file);
        !           293:                                goto phar_stub;
        !           294:                        }
        !           295:                }
        !           296:                /* read-only access is allowed to magic files in .phar directory */
        !           297:                if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, strlen(internal_file), "r", 0, &error, 0 TSRMLS_CC)) || !idata) {
        !           298: idata_error:
        !           299:                        if (error) {
        !           300:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
        !           301:                                efree(error);
        !           302:                        } else {
        !           303:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host);
        !           304:                        }
        !           305:                        efree(internal_file);
        !           306:                        php_url_free(resource);
        !           307:                        return NULL;
        !           308:                }
        !           309:        }
        !           310:        php_url_free(resource);
        !           311: #if MBO_0
        !           312:                fprintf(stderr, "Pharname:   %s\n", idata->phar->filename);
        !           313:                fprintf(stderr, "Filename:   %s\n", internal_file);
        !           314:                fprintf(stderr, "Entry:      %s\n", idata->internal_file->filename);
        !           315:                fprintf(stderr, "Size:       %u\n", idata->internal_file->uncompressed_filesize);
        !           316:                fprintf(stderr, "Compressed: %u\n", idata->internal_file->flags);
        !           317:                fprintf(stderr, "Offset:     %u\n", idata->internal_file->offset_within_phar);
        !           318:                fprintf(stderr, "Cached:     %s\n", idata->internal_file->filedata ? "yes" : "no");
        !           319: #endif
        !           320: 
        !           321:        /* check length, crc32 */
        !           322:        if (!idata->internal_file->is_crc_checked && phar_postprocess_file(idata, idata->internal_file->crc32, &error, 2 TSRMLS_CC) != SUCCESS) {
        !           323:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
        !           324:                efree(error);
        !           325:                phar_entry_delref(idata TSRMLS_CC);
        !           326:                efree(internal_file);
        !           327:                return NULL;
        !           328:        }
        !           329: 
        !           330:        if (!PHAR_G(cwd_init) && options & STREAM_OPEN_FOR_INCLUDE) {
        !           331:                char *entry = idata->internal_file->filename, *cwd;
        !           332: 
        !           333:                PHAR_G(cwd_init) = 1;
        !           334:                if ((idata->phar->is_tar || idata->phar->is_zip) && idata->internal_file->filename_len == sizeof(".phar/stub.php")-1 && !strncmp(idata->internal_file->filename, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
        !           335:                        /* we're executing the stub, which doesn't count as a file */
        !           336:                        PHAR_G(cwd_init) = 0;
        !           337:                } else if ((cwd = strrchr(entry, '/'))) {
        !           338:                        PHAR_G(cwd_len) = cwd - entry;
        !           339:                        PHAR_G(cwd) = estrndup(entry, PHAR_G(cwd_len));
        !           340:                } else {
        !           341:                        /* root directory */
        !           342:                        PHAR_G(cwd_len) = 0;
        !           343:                        PHAR_G(cwd) = NULL;
        !           344:                }
        !           345:        }
        !           346:        if (opened_path) {
        !           347:                spprintf(opened_path, MAXPATHLEN, "phar://%s/%s", idata->phar->fname, idata->internal_file->filename);
        !           348:        }
        !           349:        efree(internal_file);
        !           350: phar_stub:
        !           351:        fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
        !           352:        return fpf;
        !           353: }
        !           354: /* }}} */
        !           355: 
        !           356: /**
        !           357:  * Used for fclose($fp) where $fp is a phar archive
        !           358:  */
        !           359: static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
        !           360: {
        !           361:        phar_entry_delref((phar_entry_data *)stream->abstract TSRMLS_CC);
        !           362: 
        !           363:        return 0;
        !           364: }
        !           365: /* }}} */
        !           366: 
        !           367: /**
        !           368:  * used for fread($fp) and company on a fopen()ed phar file handle
        !           369:  */
        !           370: static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
        !           371: {
        !           372:        phar_entry_data *data = (phar_entry_data *)stream->abstract;
        !           373:        size_t got;
        !           374:        phar_entry_info *entry;
        !           375: 
        !           376:        if (data->internal_file->link) {
        !           377:                entry = phar_get_link_source(data->internal_file TSRMLS_CC);
        !           378:        } else {
        !           379:                entry = data->internal_file;
        !           380:        }
        !           381: 
        !           382:        if (entry->is_deleted) {
        !           383:                stream->eof = 1;
        !           384:                return 0;
        !           385:        }
        !           386: 
        !           387:        /* use our proxy position */
        !           388:        php_stream_seek(data->fp, data->position + data->zero, SEEK_SET);
        !           389: 
        !           390:        got = php_stream_read(data->fp, buf, MIN(count, entry->uncompressed_filesize - data->position));
        !           391:        data->position = php_stream_tell(data->fp) - data->zero;
        !           392:        stream->eof = (data->position == (off_t) entry->uncompressed_filesize);
        !           393: 
        !           394:        return got;
        !           395: }
        !           396: /* }}} */
        !           397: 
        !           398: /**
        !           399:  * Used for fseek($fp) on a phar file handle
        !           400:  */
        !           401: static int phar_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
        !           402: {
        !           403:        phar_entry_data *data = (phar_entry_data *)stream->abstract;
        !           404:        phar_entry_info *entry;
        !           405:        int res;
        !           406:        off_t temp;
        !           407: 
        !           408:        if (data->internal_file->link) {
        !           409:                entry = phar_get_link_source(data->internal_file TSRMLS_CC);
        !           410:        } else {
        !           411:                entry = data->internal_file;
        !           412:        }
        !           413: 
        !           414:        switch (whence) {
        !           415:                case SEEK_END :
        !           416:                        temp = data->zero + entry->uncompressed_filesize + offset;
        !           417:                        break;
        !           418:                case SEEK_CUR :
        !           419:                        temp = data->zero + data->position + offset;
        !           420:                        break;
        !           421:                case SEEK_SET :
        !           422:                        temp = data->zero + offset;
        !           423:                        break;
        !           424:                default :
        !           425:                        temp = 0;
        !           426:        }
        !           427:        if (temp > data->zero + (off_t) entry->uncompressed_filesize) {
        !           428:                *newoffset = -1;
        !           429:                return -1;
        !           430:        }
        !           431:        if (temp < data->zero) {
        !           432:                *newoffset = -1;
        !           433:                return -1;
        !           434:        }
        !           435:        res = php_stream_seek(data->fp, temp, SEEK_SET);
        !           436:        *newoffset = php_stream_tell(data->fp) - data->zero;
        !           437:        data->position = *newoffset;
        !           438:        return res;
        !           439: }
        !           440: /* }}} */
        !           441: 
        !           442: /**
        !           443:  * Used for writing to a phar file
        !           444:  */
        !           445: static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
        !           446: {
        !           447:        phar_entry_data *data = (phar_entry_data *) stream->abstract;
        !           448: 
        !           449:        php_stream_seek(data->fp, data->position, SEEK_SET);
        !           450:        if (count != php_stream_write(data->fp, buf, count)) {
        !           451:                php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: Could not write %d characters to \"%s\" in phar \"%s\"", (int) count, data->internal_file->filename, data->phar->fname);
        !           452:                return -1;
        !           453:        }
        !           454:        data->position = php_stream_tell(data->fp);
        !           455:        if (data->position > (off_t)data->internal_file->uncompressed_filesize) {
        !           456:                data->internal_file->uncompressed_filesize = data->position;
        !           457:        }
        !           458:        data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize;
        !           459:        data->internal_file->old_flags = data->internal_file->flags;
        !           460:        data->internal_file->is_modified = 1;
        !           461:        return count;
        !           462: }
        !           463: /* }}} */
        !           464: 
        !           465: /**
        !           466:  * Used to save work done on a writeable phar
        !           467:  */
        !           468: static int phar_stream_flush(php_stream *stream TSRMLS_DC) /* {{{ */
        !           469: {
        !           470:        char *error;
        !           471:        int ret;
        !           472:        if (stream->mode[0] == 'w' || (stream->mode[0] == 'r' && stream->mode[1] == '+')) {
        !           473:                ret = phar_flush(((phar_entry_data *)stream->abstract)->phar, 0, 0, 0, &error TSRMLS_CC);
        !           474:                if (error) {
        !           475:                        php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS TSRMLS_CC, "%s", error);
        !           476:                        efree(error);
        !           477:                }
        !           478:                return ret;
        !           479:        } else {
        !           480:                return EOF;
        !           481:        }
        !           482: }
        !           483: /* }}} */
        !           484: 
        !           485:  /* {{{ phar_dostat */
        !           486: /**
        !           487:  * stat an opened phar file handle stream, used by phar_stat()
        !           488:  */
        !           489: void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, zend_bool is_temp_dir TSRMLS_DC)
        !           490: {
        !           491:        memset(ssb, 0, sizeof(php_stream_statbuf));
        !           492: 
        !           493:        if (!is_temp_dir && !data->is_dir) {
        !           494:                ssb->sb.st_size = data->uncompressed_filesize;
        !           495:                ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
        !           496:                ssb->sb.st_mode |= S_IFREG; /* regular file */
        !           497:                /* timestamp is just the timestamp when this was added to the phar */
        !           498: #ifdef NETWARE
        !           499:                ssb->sb.st_mtime.tv_sec = data->timestamp;
        !           500:                ssb->sb.st_atime.tv_sec = data->timestamp;
        !           501:                ssb->sb.st_ctime.tv_sec = data->timestamp;
        !           502: #else
        !           503:                ssb->sb.st_mtime = data->timestamp;
        !           504:                ssb->sb.st_atime = data->timestamp;
        !           505:                ssb->sb.st_ctime = data->timestamp;
        !           506: #endif
        !           507:        } else if (!is_temp_dir && data->is_dir) {
        !           508:                ssb->sb.st_size = 0;
        !           509:                ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
        !           510:                ssb->sb.st_mode |= S_IFDIR; /* regular directory */
        !           511:                /* timestamp is just the timestamp when this was added to the phar */
        !           512: #ifdef NETWARE
        !           513:                ssb->sb.st_mtime.tv_sec = data->timestamp;
        !           514:                ssb->sb.st_atime.tv_sec = data->timestamp;
        !           515:                ssb->sb.st_ctime.tv_sec = data->timestamp;
        !           516: #else
        !           517:                ssb->sb.st_mtime = data->timestamp;
        !           518:                ssb->sb.st_atime = data->timestamp;
        !           519:                ssb->sb.st_ctime = data->timestamp;
        !           520: #endif
        !           521:        } else {
        !           522:                ssb->sb.st_size = 0;
        !           523:                ssb->sb.st_mode = 0777;
        !           524:                ssb->sb.st_mode |= S_IFDIR; /* regular directory */
        !           525: #ifdef NETWARE
        !           526:                ssb->sb.st_mtime.tv_sec = phar->max_timestamp;
        !           527:                ssb->sb.st_atime.tv_sec = phar->max_timestamp;
        !           528:                ssb->sb.st_ctime.tv_sec = phar->max_timestamp;
        !           529: #else
        !           530:                ssb->sb.st_mtime = phar->max_timestamp;
        !           531:                ssb->sb.st_atime = phar->max_timestamp;
        !           532:                ssb->sb.st_ctime = phar->max_timestamp;
        !           533: #endif
        !           534:        }
        !           535:        if (!phar->is_writeable) {
        !           536:                ssb->sb.st_mode = (ssb->sb.st_mode & 0555) | (ssb->sb.st_mode & ~0777);
        !           537:        }
        !           538: 
        !           539:        ssb->sb.st_nlink = 1;
        !           540:        ssb->sb.st_rdev = -1;
        !           541:        /* this is only for APC, so use /dev/null device - no chance of conflict there! */
        !           542:        ssb->sb.st_dev = 0xc;
        !           543:        /* generate unique inode number for alias/filename, so no phars will conflict */
        !           544:        if (!is_temp_dir) {
        !           545:                ssb->sb.st_ino = data->inode;
        !           546:        }
        !           547: #ifndef PHP_WIN32
        !           548:        ssb->sb.st_blksize = -1;
        !           549:        ssb->sb.st_blocks = -1;
        !           550: #endif
        !           551: }
        !           552: /* }}}*/
        !           553: 
        !           554: /**
        !           555:  * Stat an opened phar file handle
        !           556:  */
        !           557: static int phar_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
        !           558: {
        !           559:        phar_entry_data *data = (phar_entry_data *)stream->abstract;
        !           560: 
        !           561:        /* If ssb is NULL then someone is misbehaving */
        !           562:        if (!ssb) {
        !           563:                return -1;
        !           564:        }
        !           565: 
        !           566:        phar_dostat(data->phar, data->internal_file, ssb, 0 TSRMLS_CC);
        !           567:        return 0;
        !           568: }
        !           569: /* }}} */
        !           570: 
        !           571: /**
        !           572:  * Stream wrapper stat implementation of stat()
        !           573:  */
        !           574: static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags,
        !           575:                                  php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) /* {{{ */
        !           576: {
        !           577:        php_url *resource = NULL;
        !           578:        char *internal_file, *error;
        !           579:        phar_archive_data *phar;
        !           580:        phar_entry_info *entry;
        !           581:        uint host_len;
        !           582:        int internal_file_len;
        !           583: 
        !           584:        if ((resource = phar_parse_url(wrapper, url, "r", flags|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) {
        !           585:                return FAILURE;
        !           586:        }
        !           587: 
        !           588:        /* we must have at the very least phar://alias.phar/internalfile.php */
        !           589:        if (!resource->scheme || !resource->host || !resource->path) {
        !           590:                php_url_free(resource);
        !           591:                return FAILURE;
        !           592:        }
        !           593: 
        !           594:        if (strcasecmp("phar", resource->scheme)) {
        !           595:                php_url_free(resource);
        !           596:                return FAILURE;
        !           597:        }
        !           598: 
        !           599:        host_len = strlen(resource->host);
        !           600:        phar_request_initialize(TSRMLS_C);
        !           601: 
        !           602:        internal_file = resource->path + 1; /* strip leading "/" */
        !           603:        /* find the phar in our trusty global hash indexed by alias (host of phar://blah.phar/file.whatever) */
        !           604:        if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
        !           605:                php_url_free(resource);
        !           606:                if (error) {
        !           607:                        efree(error);
        !           608:                }
        !           609:                return FAILURE;
        !           610:        }
        !           611:        if (error) {
        !           612:                efree(error);
        !           613:        }
        !           614:        if (*internal_file == '\0') {
        !           615:                /* root directory requested */
        !           616:                phar_dostat(phar, NULL, ssb, 1 TSRMLS_CC);
        !           617:                php_url_free(resource);
        !           618:                return SUCCESS;
        !           619:        }
        !           620:        if (!phar->manifest.arBuckets) {
        !           621:                php_url_free(resource);
        !           622:                return FAILURE;
        !           623:        }
        !           624:        internal_file_len = strlen(internal_file);
        !           625:        /* search through the manifest of files, and if we have an exact match, it's a file */
        !           626:        if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, internal_file_len, (void**)&entry)) {
        !           627:                phar_dostat(phar, entry, ssb, 0 TSRMLS_CC);
        !           628:                php_url_free(resource);
        !           629:                return SUCCESS;
        !           630:        }
        !           631:        if (zend_hash_exists(&(phar->virtual_dirs), internal_file, internal_file_len)) {
        !           632:                phar_dostat(phar, NULL, ssb, 1 TSRMLS_CC);
        !           633:                php_url_free(resource);
        !           634:                return SUCCESS;
        !           635:        }
        !           636:        /* check for mounted directories */
        !           637:        if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) {
        !           638:                phar_zstr key;
        !           639:                char *str_key;
        !           640:                ulong unused;
        !           641:                uint keylen;
        !           642:                HashPosition pos;
        !           643: 
        !           644:                zend_hash_internal_pointer_reset_ex(&phar->mounted_dirs, &pos);
        !           645:                while (FAILURE != zend_hash_has_more_elements_ex(&phar->mounted_dirs, &pos)) {
        !           646:                        if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &keylen, &unused, 0, &pos)) {
        !           647:                                break;
        !           648:                        }
        !           649:                        PHAR_STR(key, str_key);
        !           650:                        if ((int)keylen >= internal_file_len || strncmp(str_key, internal_file, keylen)) {
        !           651:                                zend_hash_move_forward_ex(&phar->mounted_dirs, &pos);
        !           652:                                PHAR_STR_FREE(str_key);
        !           653:                                continue;
        !           654:                        } else {
        !           655:                                char *test;
        !           656:                                int test_len;
        !           657:                                php_stream_statbuf ssbi;
        !           658: 
        !           659:                                if (SUCCESS != zend_hash_find(&phar->manifest, str_key, keylen, (void **) &entry)) {
        !           660:                                        PHAR_STR_FREE(str_key);
        !           661:                                        goto free_resource;
        !           662:                                }
        !           663:                                PHAR_STR_FREE(str_key);
        !           664:                                if (!entry->tmp || !entry->is_mounted) {
        !           665:                                        goto free_resource;
        !           666:                                }
        !           667:                                test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, internal_file + keylen);
        !           668:                                if (SUCCESS != php_stream_stat_path(test, &ssbi)) {
        !           669:                                        efree(test);
        !           670:                                        zend_hash_move_forward_ex(&phar->mounted_dirs, &pos);
        !           671:                                        continue;
        !           672:                                }
        !           673:                                /* mount the file/directory just in time */
        !           674:                                if (SUCCESS != phar_mount_entry(phar, test, test_len, internal_file, internal_file_len TSRMLS_CC)) {
        !           675:                                        efree(test);
        !           676:                                        goto free_resource;
        !           677:                                }
        !           678:                                efree(test);
        !           679:                                if (SUCCESS != zend_hash_find(&phar->manifest, internal_file, internal_file_len, (void**)&entry)) {
        !           680:                                        goto free_resource;
        !           681:                                }
        !           682:                                phar_dostat(phar, entry, ssb, 0 TSRMLS_CC);
        !           683:                                php_url_free(resource);
        !           684:                                return SUCCESS;
        !           685:                        }
        !           686:                }
        !           687:        }
        !           688: free_resource:
        !           689:        php_url_free(resource);
        !           690:        return FAILURE;
        !           691: }
        !           692: /* }}} */
        !           693: 
        !           694: /**
        !           695:  * Unlink a file within a phar archive
        !           696:  */
        !           697: static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
        !           698: {
        !           699:        php_url *resource;
        !           700:        char *internal_file, *error;
        !           701:        int internal_file_len;
        !           702:        phar_entry_data *idata;
        !           703:        phar_archive_data **pphar;
        !           704:        uint host_len;
        !           705: 
        !           706:        if ((resource = phar_parse_url(wrapper, url, "rb", options TSRMLS_CC)) == NULL) {
        !           707:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: unlink failed");
        !           708:                return 0;
        !           709:        }
        !           710: 
        !           711:        /* we must have at the very least phar://alias.phar/internalfile.php */
        !           712:        if (!resource->scheme || !resource->host || !resource->path) {
        !           713:                php_url_free(resource);
        !           714:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url);
        !           715:                return 0;
        !           716:        }
        !           717: 
        !           718:        if (strcasecmp("phar", resource->scheme)) {
        !           719:                php_url_free(resource);
        !           720:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url);
        !           721:                return 0;
        !           722:        }
        !           723: 
        !           724:        host_len = strlen(resource->host);
        !           725:        phar_request_initialize(TSRMLS_C);
        !           726: 
        !           727:        if (FAILURE == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), resource->host, host_len, (void **) &pphar)) {
        !           728:                pphar = NULL;
        !           729:        }
        !           730:        if (PHAR_G(readonly) && (!pphar || !(*pphar)->is_data)) {
        !           731:                php_url_free(resource);
        !           732:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by the php.ini setting phar.readonly");
        !           733:                return 0;
        !           734:        }
        !           735: 
        !           736:        /* need to copy to strip leading "/", will get touched again */
        !           737:        internal_file = estrdup(resource->path + 1);
        !           738:        internal_file_len = strlen(internal_file);
        !           739:        if (FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, internal_file_len, "r", 0, &error, 1 TSRMLS_CC)) {
        !           740:                /* constraints of fp refcount were not met */
        !           741:                if (error) {
        !           742:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "unlink of \"%s\" failed: %s", url, error);
        !           743:                        efree(error);
        !           744:                } else {
        !           745:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "unlink of \"%s\" failed, file does not exist", url);
        !           746:                }
        !           747:                efree(internal_file);
        !           748:                php_url_free(resource);
        !           749:                return 0;
        !           750:        }
        !           751:        if (error) {
        !           752:                efree(error);
        !           753:        }
        !           754:        if (idata->internal_file->fp_refcount > 1) {
        !           755:                /* more than just our fp resource is open for this file */
        !           756:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, resource->host);
        !           757:                efree(internal_file);
        !           758:                php_url_free(resource);
        !           759:                phar_entry_delref(idata TSRMLS_CC);
        !           760:                return 0;
        !           761:        }
        !           762:        php_url_free(resource);
        !           763:        efree(internal_file);
        !           764:        phar_entry_remove(idata, &error TSRMLS_CC);
        !           765:        if (error) {
        !           766:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
        !           767:                efree(error);
        !           768:        }
        !           769:        return 1;
        !           770: }
        !           771: /* }}} */
        !           772: 
        !           773: static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
        !           774: {
        !           775:        php_url *resource_from, *resource_to;
        !           776:        char *error;
        !           777:        phar_archive_data *phar, *pfrom, *pto;
        !           778:        phar_entry_info *entry;
        !           779:        uint host_len;
        !           780:        int is_dir = 0;
        !           781:        int is_modified = 0;
        !           782: 
        !           783:        error = NULL;
        !           784: 
        !           785:        if ((resource_from = phar_parse_url(wrapper, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) {
        !           786:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_from);
        !           787:                return 0;
        !           788:        }
        !           789:        if (SUCCESS != phar_get_archive(&pfrom, resource_from->host, strlen(resource_from->host), NULL, 0, &error TSRMLS_CC)) {
        !           790:                pfrom = NULL;
        !           791:                if (error) {
        !           792:                        efree(error);
        !           793:                }
        !           794:        }
        !           795:        if (PHAR_G(readonly) && (!pfrom || !pfrom->is_data)) {
        !           796:                php_url_free(resource_from);
        !           797:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly");
        !           798:                return 0;
        !           799:        }
        !           800: 
        !           801:        if ((resource_to = phar_parse_url(wrapper, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) {
        !           802:                php_url_free(resource_from);
        !           803:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_to);
        !           804:                return 0;
        !           805:        }
        !           806:        if (SUCCESS != phar_get_archive(&pto, resource_to->host, strlen(resource_to->host), NULL, 0, &error TSRMLS_CC)) {
        !           807:                if (error) {
        !           808:                        efree(error);
        !           809:                }
        !           810:                pto = NULL;
        !           811:        }
        !           812:        if (PHAR_G(readonly) && (!pto || !pto->is_data)) {
        !           813:                php_url_free(resource_from);
        !           814:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly");
        !           815:                return 0;
        !           816:        }
        !           817: 
        !           818:        if (strcmp(resource_from->host, resource_to->host)) {
        !           819:                php_url_free(resource_from);
        !           820:                php_url_free(resource_to);
        !           821:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to);
        !           822:                return 0;
        !           823:        }
        !           824: 
        !           825:        /* we must have at the very least phar://alias.phar/internalfile.php */
        !           826:        if (!resource_from->scheme || !resource_from->host || !resource_from->path) {
        !           827:                php_url_free(resource_from);
        !           828:                php_url_free(resource_to);
        !           829:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from);
        !           830:                return 0;
        !           831:        }
        !           832: 
        !           833:        if (!resource_to->scheme || !resource_to->host || !resource_to->path) {
        !           834:                php_url_free(resource_from);
        !           835:                php_url_free(resource_to);
        !           836:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_to);
        !           837:                return 0;
        !           838:        }
        !           839: 
        !           840:        if (strcasecmp("phar", resource_from->scheme)) {
        !           841:                php_url_free(resource_from);
        !           842:                php_url_free(resource_to);
        !           843:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_from);
        !           844:                return 0;
        !           845:        }
        !           846: 
        !           847:        if (strcasecmp("phar", resource_to->scheme)) {
        !           848:                php_url_free(resource_from);
        !           849:                php_url_free(resource_to);
        !           850:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_to);
        !           851:                return 0;
        !           852:        }
        !           853: 
        !           854:        host_len = strlen(resource_from->host);
        !           855: 
        !           856:        if (SUCCESS != phar_get_archive(&phar, resource_from->host, host_len, NULL, 0, &error TSRMLS_CC)) {
        !           857:                php_url_free(resource_from);
        !           858:                php_url_free(resource_to);
        !           859:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
        !           860:                efree(error);
        !           861:                return 0;
        !           862:        }
        !           863: 
        !           864:        if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
        !           865:                php_url_free(resource_from);
        !           866:                php_url_free(resource_to);
        !           867:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", url_from, url_to);
        !           868:                return 0;
        !           869:        }
        !           870: 
        !           871:        if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) {
        !           872:                phar_entry_info new, *source;
        !           873: 
        !           874:                /* perform rename magic */
        !           875:                if (entry->is_deleted) {
        !           876:                        php_url_free(resource_from);
        !           877:                        php_url_free(resource_to);
        !           878:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to);
        !           879:                        return 0;
        !           880:                }
        !           881:                /* transfer all data over to the new entry */
        !           882:                memcpy((void *) &new, (void *) entry, sizeof(phar_entry_info));
        !           883:                /* mark the old one for deletion */
        !           884:                entry->is_deleted = 1;
        !           885:                entry->fp = NULL;
        !           886:                entry->metadata = 0;
        !           887:                entry->link = entry->tmp = NULL;
        !           888:                source = entry;
        !           889: 
        !           890:                /* add to the manifest, and then store the pointer to the new guy in entry */
        !           891:                zend_hash_add(&(phar->manifest), resource_to->path+1, strlen(resource_to->path)-1, (void **)&new, sizeof(phar_entry_info), (void **) &entry);
        !           892: 
        !           893:                entry->filename = estrdup(resource_to->path+1);
        !           894:                if (FAILURE == phar_copy_entry_fp(source, entry, &error TSRMLS_CC)) {
        !           895:                        php_url_free(resource_from);
        !           896:                        php_url_free(resource_to);
        !           897:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
        !           898:                        efree(error);
        !           899:                        zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename));
        !           900:                        return 0;
        !           901:                }
        !           902:                is_modified = 1;
        !           903:                entry->is_modified = 1;
        !           904:                entry->filename_len = strlen(entry->filename);
        !           905:                is_dir = entry->is_dir;
        !           906:        } else {
        !           907:                is_dir = zend_hash_exists(&(phar->virtual_dirs), resource_from->path+1, strlen(resource_from->path)-1);
        !           908:                if (!is_dir) {
        !           909:                        /* file does not exist */
        !           910:                        php_url_free(resource_from);
        !           911:                        php_url_free(resource_to);
        !           912:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", url_from, url_to);
        !           913:                        return 0;
        !           914: 
        !           915:                }
        !           916:        }
        !           917: 
        !           918:        /* Rename directory. Update all nested paths */
        !           919:        if (is_dir) {
        !           920:                int key_type;
        !           921:                phar_zstr key, new_key;
        !           922:                char *str_key, *new_str_key;
        !           923:                uint key_len, new_key_len;
        !           924:                ulong unused;
        !           925:                uint from_len = strlen(resource_from->path+1);
        !           926:                uint to_len = strlen(resource_to->path+1);
        !           927: 
        !           928:                for (zend_hash_internal_pointer_reset(&phar->manifest);
        !           929:                        HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL)) &&
        !           930:                        SUCCESS == zend_hash_get_current_data(&phar->manifest, (void **) &entry);
        !           931:                        zend_hash_move_forward(&phar->manifest)) {
        !           932: 
        !           933:                        PHAR_STR(key, str_key);
        !           934: 
        !           935:                        if (!entry->is_deleted &&
        !           936:                                key_len > from_len &&
        !           937:                                memcmp(str_key, resource_from->path+1, from_len) == 0 &&
        !           938:                                IS_SLASH(str_key[from_len])) {
        !           939: 
        !           940:                                new_key_len = key_len + to_len - from_len;
        !           941:                                new_str_key = emalloc(new_key_len+1);
        !           942:                                memcpy(new_str_key, resource_to->path + 1, to_len);
        !           943:                                memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len);
        !           944:                                new_str_key[new_key_len] = 0;
        !           945: 
        !           946:                                is_modified = 1;
        !           947:                                entry->is_modified = 1;
        !           948:                                efree(entry->filename);
        !           949:                                entry->filename = new_str_key;
        !           950:                                entry->filename_len = new_key_len;
        !           951: 
        !           952:                                PHAR_ZSTR(new_str_key, new_key);
        !           953: #if PHP_VERSION_ID < 50300
        !           954:                                zend_hash_update_current_key_ex(&phar->manifest, key_type, new_key, new_key_len, 0, NULL);
        !           955: #else
        !           956:                                zend_hash_update_current_key_ex(&phar->manifest, key_type, new_key, new_key_len, 0, HASH_UPDATE_KEY_ANYWAY, NULL);
        !           957: #endif
        !           958:                        }
        !           959:                        PHAR_STR_FREE(str_key);
        !           960:                }
        !           961: 
        !           962:                for (zend_hash_internal_pointer_reset(&phar->virtual_dirs);
        !           963:                        HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL));
        !           964:                        zend_hash_move_forward(&phar->virtual_dirs)) {
        !           965: 
        !           966:                        PHAR_STR(key, str_key);
        !           967: 
        !           968:                        if (key_len >= from_len &&
        !           969:                                memcmp(str_key, resource_from->path+1, from_len) == 0 &&
        !           970:                                (key_len == from_len || IS_SLASH(str_key[from_len]))) {
        !           971: 
        !           972:                                new_key_len = key_len + to_len - from_len;
        !           973:                                new_str_key = emalloc(new_key_len+1);
        !           974:                                memcpy(new_str_key, resource_to->path + 1, to_len);
        !           975:                                memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len);
        !           976:                                new_str_key[new_key_len] = 0;
        !           977: 
        !           978:                                PHAR_ZSTR(new_str_key, new_key);
        !           979: #if PHP_VERSION_ID < 50300
        !           980:                                zend_hash_update_current_key_ex(&phar->virtual_dirs, key_type, new_key, new_key_len, 0, NULL);
        !           981: #else
        !           982:                                zend_hash_update_current_key_ex(&phar->virtual_dirs, key_type, new_key, new_key_len, 0, HASH_UPDATE_KEY_ANYWAY, NULL);
        !           983: #endif
        !           984:                                efree(new_str_key);
        !           985:                        }
        !           986:                        PHAR_STR_FREE(str_key);
        !           987:                }
        !           988: 
        !           989:                for (zend_hash_internal_pointer_reset(&phar->mounted_dirs);
        !           990:                        HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &key_len, &unused, 0, NULL)) &&
        !           991:                        SUCCESS == zend_hash_get_current_data(&phar->mounted_dirs, (void **) &entry);
        !           992:                        zend_hash_move_forward(&phar->mounted_dirs)) {
        !           993: 
        !           994:                        PHAR_STR(key, str_key);
        !           995: 
        !           996:                        if (key_len >= from_len &&
        !           997:                                memcmp(str_key, resource_from->path+1, from_len) == 0 &&
        !           998:                                (key_len == from_len || IS_SLASH(str_key[from_len]))) {
        !           999: 
        !          1000:                                new_key_len = key_len + to_len - from_len;
        !          1001:                                new_str_key = emalloc(new_key_len+1);
        !          1002:                                memcpy(new_str_key, resource_to->path + 1, to_len);
        !          1003:                                memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len);
        !          1004:                                new_str_key[new_key_len] = 0;
        !          1005: 
        !          1006:                                PHAR_ZSTR(new_str_key, new_key);
        !          1007: #if PHP_VERSION_ID < 50300
        !          1008:                                zend_hash_update_current_key_ex(&phar->mounted_dirs, key_type, new_key, new_key_len, 0, NULL);
        !          1009: #else
        !          1010:                                zend_hash_update_current_key_ex(&phar->mounted_dirs, key_type, new_key, new_key_len, 0, HASH_UPDATE_KEY_ANYWAY, NULL);
        !          1011: #endif
        !          1012:                                efree(new_str_key);
        !          1013:                        }
        !          1014:                        PHAR_STR_FREE(str_key);
        !          1015:                }
        !          1016:        }
        !          1017: 
        !          1018:        if (is_modified) {
        !          1019:                phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
        !          1020:                if (error) {
        !          1021:                        php_url_free(resource_from);
        !          1022:                        php_url_free(resource_to);
        !          1023:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
        !          1024:                        efree(error);
        !          1025:                        return 0;
        !          1026:                }
        !          1027:        }
        !          1028: 
        !          1029:        php_url_free(resource_from);
        !          1030:        php_url_free(resource_to);
        !          1031: 
        !          1032:        return 1;
        !          1033: }
        !          1034: /* }}} */
        !          1035: 
        !          1036: /*
        !          1037:  * Local variables:
        !          1038:  * tab-width: 4
        !          1039:  * c-basic-offset: 4
        !          1040:  * End:
        !          1041:  * vim600: noet sw=4 ts=4 fdm=marker
        !          1042:  * vim<600: noet sw=4 ts=4
        !          1043:  */

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