From: Marcus Boerger Date: Wed, 4 Jan 2006 02:26:15 +0000 (+0000) Subject: - Imlement normal stream handling, use .phar.php anf .phar.gz to detect X-Git-Tag: RELEASE_1_0_4~140 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=be1177259e1c00f2c157b9da9511fde7899db516;p=php - Imlement normal stream handling, use .phar.php anf .phar.gz to detect where the filename part ends and whether it is compressed or not # This already allows: php ext/spl/examples/directorytree.php phar:///... --- diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 7b9780bb77..82e8a9848a 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -109,67 +109,38 @@ PHP_METHOD(Phar, canCompress) } /* }}} */ -/* {{{ proto mixed Phar::mapPhar(string alias, mixed compressed, [mixed unused [, mixed unused]]) - * Maps the curretnly executed file (a phar) with the given alias */ -PHP_METHOD(Phar, mapPhar) +#define MAPPHAR_ALLOC_FAIL(msg) \ + php_stream_close(fp);\ + php_error_docref(NULL TSRMLS_CC, E_ERROR, msg, fname);\ + return FAILURE; + +#define MAPPHAR_FAIL(msg) \ + efree(savebuf);\ + MAPPHAR_ALLOC_FAIL(msg) + +static int phar_open_file(php_stream *fp, char *fname, char *alias, int alias_len, zend_bool compressed, long halt_offset) /* {{{ */ { - char *fname, *alias, *buffer, *endbuffer, *unpack_var, *savebuf; + char *buffer, *endbuffer, *unpack_var, *savebuf; phar_file_data mydata; - zend_bool compressed; phar_manifest_entry entry; HashTable *manifest; - int alias_len, i; - long halt_offset; + int i; php_uint32 manifest_len, manifest_count, manifest_index; - zval *halt_constant, **unused1, **unused2; - php_stream *fp; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sb|zz", &alias, &alias_len, &compressed, &unused1, &unused2) == FAILURE) { - return; - } -#ifndef HAVE_PHAR_ZLIB - if (compressed) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "zlib extension is required for compressed .phar files"); - return; - } -#endif - fname = zend_get_executed_filename(TSRMLS_C); - if (!strcmp(fname, "[no active file]")) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "cannot initialize a phar outside of PHP execution"); - return; - } - - MAKE_STD_ZVAL(halt_constant); - if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) { - zval_dtor(halt_constant); - FREE_ZVAL(halt_constant); - php_error_docref(NULL TSRMLS_CC, E_ERROR, "__HALT_COMPILER(); must be declared in a phar"); - return; - } - halt_offset = Z_LVAL(*halt_constant); - zval_dtor(halt_constant); - FREE_ZVAL(halt_constant); if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { - return; + return FAILURE; } if (php_check_open_basedir(fname TSRMLS_CC)) { - return; + return FAILURE; } fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); if (!fp) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to open phar for reading \"%s\"", fname); - return; + return FAILURE; } -#define MAPPHAR_ALLOC_FAIL(msg) php_stream_close(fp);\ - php_stream_close(fp);\ - php_error_docref(NULL TSRMLS_CC, E_ERROR, msg, fname);\ - return; -#define MAPPHAR_FAIL(msg) efree(savebuf);\ - MAPPHAR_ALLOC_FAIL(msg) /* check for ?>\n and increment accordingly */ if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) { @@ -283,13 +254,185 @@ PHP_METHOD(Phar, mapPhar) zend_hash_add(&(PHAR_GLOBALS->phar_data), alias, alias_len, &mydata, sizeof(phar_file_data), NULL); efree(savebuf); + + return SUCCESS; +} +/* }}} */ + +static int phar_open_filename(char *fname, char *alias, int alias_len, zend_bool compressed) /* {{{ */ +{ + const char token[] = "__HALT_COMPILER();"; + char *pos, buffer[1024 + sizeof(token)]; + const long readsize = sizeof(buffer) - sizeof(token); + const long tokenlen = sizeof(token) - 1; + int result; + long halt_offset; + php_stream *fp; + + fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); + + if (!fp) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to open phar for reading \"%s\"", fname); + return FAILURE; + } + + /* Maybe it's better to compile the file instead of just searching, */ + /* but we only want the offset. So we want a .re scanner to find it. */ + + if (-1 == php_stream_seek(fp, 0, SEEK_SET)) { + MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"") + } + + buffer[sizeof(buffer)-1] = '\0'; + memset(buffer, 32, sizeof(token)); + halt_offset = 0; + while(!php_stream_eof(fp)) { + if (php_stream_read(fp, buffer+tokenlen, readsize) < 0) { + MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest)") + } + + if ((pos = strstr(buffer, token)) != NULL) { + halt_offset = (pos - buffer); /* no -tokenlen+tokenlen here */ + result = phar_open_file(fp, fname, alias, alias_len, compressed, halt_offset); + php_stream_close(fp); + return result; + } + + halt_offset += readsize; + memmove(buffer, buffer + tokenlen, readsize + 1); + } + + MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)") php_stream_close(fp); + return FAILURE; +} +/* }}} */ + +static php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options) /* {{{ */ +{ + php_url *resource; + char *pos_p, *pos_z, *ext_str, *alias; + int len, ext_len; + + if (!strncasecmp(filename, "phar://", 7)) { + filename += 7; + pos_p = strstr(filename, ".phar.php"); + pos_z = strstr(filename, ".phar.gz"); + if (pos_p) { + if (pos_z) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\" (cannot contain .phar.php and .phar.gz)", filename); + return NULL; + } + ext_str = pos_p; + ext_len = 9; + } else if (pos_z) { + ext_str = pos_z; + ext_len = 8; + } else { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\" (filename extension must be either .phar.php or .phar.gz)", filename); + return NULL; + } + resource = emalloc(sizeof(php_url)); + resource->scheme = estrndup("phar", 4); + len = ext_str - filename + ext_len; + alias = resource->host = estrndup(filename, len); + if (ext_str[ext_len]) { + resource->path = estrdup(ext_str+ext_len); + } else { + resource->path = estrndup("/", 1); + } +#if MBO_0 + if (resource) { + fprintf(stderr, "Alias: %s\n", alias); + fprintf(stderr, "Compressed:%s\n", pos_z ? "yes" : "no"); + fprintf(stderr, "Scheme: %s\n", resource->scheme); +/* fprintf(stderr, "User: %s\n", resource->user);*/ +/* fprintf(stderr, "Pass: %s\n", resource->pass ? "***" : NULL);*/ + fprintf(stderr, "Host: %s\n", resource->host); +/* fprintf(stderr, "Port: %d\n", resource->port);*/ + fprintf(stderr, "Path: %s\n", resource->path); +/* fprintf(stderr, "Query: %s\n", resource->query);*/ +/* fprintf(stderr, "Fragment: %s\n", resource->fragment);*/ + } +#endif + if (phar_open_filename(resource->host, alias, len, pos_z ? 1 : 0) == FAILURE) + { + php_url_free(resource); + return NULL; + } + + return resource; + } + + return NULL; } /* }}} */ +static int phar_open_compiled_file(char *alias, int alias_len, zend_bool compressed) /* {{{ */ +{ + char *fname; + int result; + long halt_offset; + zval *halt_constant; + php_stream *fp; + + fname = zend_get_executed_filename(TSRMLS_C); + + if (!strcmp(fname, "[no active file]")) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "cannot initialize a phar outside of PHP execution"); + return FAILURE; + } + + MAKE_STD_ZVAL(halt_constant); + if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) { + zval_dtor(halt_constant); + FREE_ZVAL(halt_constant); + php_error_docref(NULL TSRMLS_CC, E_ERROR, "__HALT_COMPILER(); must be declared in a phar"); + return FAILURE; + } + halt_offset = Z_LVAL(*halt_constant); + zval_dtor(halt_constant); + FREE_ZVAL(halt_constant); + + fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); + + if (!fp) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to open phar for reading \"%s\"", fname); + return FAILURE; + } + + result = phar_open_file(fp, fname, alias, alias_len, compressed, halt_offset); + php_stream_close(fp); + return result; +} +/* }}} */ + +/* {{{ proto mixed Phar::mapPhar(string alias, mixed compressed, [mixed unused [, mixed unused]]) + * Maps the curretnly executed file (a phar) with the given alias */ +PHP_METHOD(Phar, mapPhar) +{ + char *alias; + zend_bool compressed; + int alias_len; + zval **unused1, **unused2; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sb|zz", &alias, &alias_len, &compressed, &unused1, &unused2) == FAILURE) { + return; + } +#ifndef HAVE_PHAR_ZLIB + if (compressed) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "zlib extension is required for compressed .phar files"); + return; + } +#endif + + RETURN_BOOL(phar_open_compiled_file(alias, alias_len, compressed) == SUCCESS); +} /* }}} */ + PHP_PHAR_API php_stream *php_stream_phar_url_wrapper(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); PHP_PHAR_API int phar_close(php_stream *stream, int close_handle TSRMLS_DC); PHP_PHAR_API int phar_closedir(php_stream *stream, int close_handle TSRMLS_DC); +PHP_PHAR_API int phar_seekdir(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); PHP_PHAR_API size_t phar_read(php_stream *stream, char *buf, size_t count TSRMLS_DC); PHP_PHAR_API size_t phar_readdir(php_stream *stream, char *buf, size_t count TSRMLS_DC); PHP_PHAR_API int phar_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); @@ -320,7 +463,7 @@ static php_stream_ops phar_dir_ops = { phar_closedir, /* close */ phar_flush, /* flush (does nothing) */ "phar stream", - NULL, /* seek */ + phar_seekdir, /* seek */ NULL, /* cast */ phar_stat, /* stat */ NULL, /* set option */ @@ -399,6 +542,11 @@ PHP_PHAR_API php_stream * php_stream_phar_url_wrapper(php_stream_wrapper *wrappe #endif resource = php_url_parse(path); + + if (!resource && (resource = phar_open_url(wrapper, path, mode, options)) == NULL) { + return NULL; + } + /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource || !resource->scheme || !resource->host || !resource->path) { if (resource) { @@ -631,6 +779,35 @@ PHP_PHAR_API int phar_closedir(php_stream *stream, int close_handle TSRMLS_DC) } /* }}} */ +PHP_PHAR_API int phar_seekdir(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */ +{ + HashTable *data = (HashTable *)stream->abstract; + + if (data) + { + if (whence == SEEK_END) { + whence = SEEK_SET; + offset = zend_hash_num_elements(data) + offset; + } + if (whence == SEEK_SET) { + zend_hash_internal_pointer_reset(data); + } + + if (offset < 0) { + php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: cannot seek because the resulting seek is negative"); + return -1; + } else { + *newoffset = 0; + while (*newoffset < offset && zend_hash_move_forward(data) == SUCCESS) { + (*newoffset)++; + } + return 0; + } + } + return -1; +} +/* }}} */ + PHP_PHAR_API size_t phar_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */ { size_t to_read; @@ -813,11 +990,17 @@ PHP_PHAR_API int phar_stream_stat(php_stream_wrapper *wrapper, char *url, int fl phar_manifest_entry *file_data; resource = php_url_parse(url); + + if (!resource && (resource = phar_open_url(wrapper, url, "r", 0)) == NULL) { + return -1; + } + /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource || !resource->scheme || !resource->host || !resource->path) { - php_url_free(resource); + if (resource) { + php_url_free(resource); + } php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: invalid url \"%s\"", url); - php_url_free(resource); return -1; } @@ -1000,6 +1183,11 @@ PHP_PHAR_API php_stream *phar_opendir(php_stream_wrapper *wrapper, char *filenam phar_manifest_entry *file_data; resource = php_url_parse(filename); + + if (!resource && (resource = phar_open_url(wrapper, filename, mode, options)) == NULL) { + return NULL; + } + /* we must have at the very least phar://alias.phar/ */ if (!resource || !resource->scheme || !resource->host || !resource->path) { if (resource && resource->host && !resource->path) { @@ -1119,8 +1307,10 @@ PHP_MINIT_FUNCTION(phar) little_endian_long_map[2] = size - 3; little_endian_long_map[3] = size - 4; } + INIT_CLASS_ENTRY(php_archive_entry, "Phar", php_archive_methods); php_archive_entry_ptr = zend_register_internal_class(&php_archive_entry TSRMLS_CC); + return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper TSRMLS_CC); } /* }}} */ @@ -1176,7 +1366,7 @@ zend_module_entry phar_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif - "phar", + "Phar", phar_functions, PHP_MINIT(phar), PHP_MSHUTDOWN(phar),