diff options
author | Greg Beaver <cellog@php.net> | 2007-12-10 20:42:02 +0000 |
---|---|---|
committer | Greg Beaver <cellog@php.net> | 2007-12-10 20:42:02 +0000 |
commit | 0d38d74493bc5381117cf9d82fb0d11f3973b2e2 (patch) | |
tree | d131380ed99b3da6fcde4e42309aa53853acb7fe | |
parent | e4c2bc1863910264cf0ea6d75bd88d36b0961fc7 (diff) | |
download | php-git-0d38d74493bc5381117cf9d82fb0d11f3973b2e2.tar.gz |
add unfinished experimental tar support (may not lead anywhere)
-rw-r--r-- | ext/phar/config.m4 | 2 | ||||
-rw-r--r-- | ext/phar/phar.c | 5 | ||||
-rw-r--r-- | ext/phar/phar2.c | 244 | ||||
-rw-r--r-- | ext/phar/phar2.h | 34 | ||||
-rw-r--r-- | ext/phar/phar2_openhash.c | 150 | ||||
-rw-r--r-- | ext/phar/phar2_openhash.h | 59 | ||||
-rwxr-xr-x | ext/phar/phar_internal.h | 10 |
7 files changed, 499 insertions, 5 deletions
diff --git a/ext/phar/config.m4 b/ext/phar/config.m4 index 75c26eae52..4782889d2f 100644 --- a/ext/phar/config.m4 +++ b/ext/phar/config.m4 @@ -5,7 +5,7 @@ PHP_ARG_ENABLE(phar, for phar support/phar zlib support, [ --enable-phar Enable phar support, use --with-zlib-dir if zlib detection fails]) if test "$PHP_PHAR" != "no"; then - PHP_NEW_EXTENSION(phar, phar.c phar_object.c phar_path_check.c, $ext_shared) + PHP_NEW_EXTENSION(phar, phar.c phar_object.c phar_path_check.c phar2.c phar2_openhash.c, $ext_shared) PHP_ADD_EXTENSION_DEP(phar, zlib, false) PHP_ADD_EXTENSION_DEP(phar, bz2, false) PHP_ADD_EXTENSION_DEP(phar, spl, false) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 436943b36c..1183a799ab 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -21,6 +21,7 @@ #define PHAR_MAIN #include "phar_internal.h" +#include "phar2.h" #include "SAPI.h" ZEND_DECLARE_MODULE_GLOBALS(phar) @@ -947,6 +948,10 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int } return FAILURE; } + if ((manifest_ver & PHAR_API_MAJORVERSION) == PHAR_API_MAJORVERSION) { + /* this is a phar in tar format */ + return phar2_open_file(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, savebuf, error TSRMLS_CC); + } PHAR_GET_32(buffer, manifest_flags); diff --git a/ext/phar/phar2.c b/ext/phar/phar2.c new file mode 100644 index 0000000000..7dae30c30d --- /dev/null +++ b/ext/phar/phar2.c @@ -0,0 +1,244 @@ +/* + +----------------------------------------------------------------------+ + | phar php single-file executable PHP extension | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gregory Beaver <cellog@php.net> | + | Marcus Boerger <helly@php.net> | + | based on work by Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "phar_internal.h" + + +#define TAR_FILE '0' +#define TAR_LINK '1' +#define TAR_SYMLINK '2' +#define TAR_DIR '5' +#define TAR_NEW '8' + +/** + * the format of the header block for a file, in the older UNIX-compatible + * TAR format + */ +typedef struct _old_tar_header { /* {{{ */ + char name[100]; /* name of file; + directory is indicated by a trailing slash (/) */ + char mode[8]; /* file mode */ + char uid[8]; /* owner user ID */ + char gid[8]; /* owner group ID */ + char size[12]; /* length of file in bytes */ + char mtime[12]; /* modify time of file */ + char chksum[8]; /* checksum for header */ + char link; /* indicator for links; + 1 for a linked file, + 2 for a symbolic link, + 0 otherwise */ + char linkname[100]; /* name of linked file */ +} old_tar_header; +/* }}} */ + +/** + * the new USTAR header format. + * Note that tar can determine that the USTAR format is being used by the + * presence of the null-terminated string "ustar" in the magic field. + */ +typedef struct _tar_header { /* {{{ */ + char name[100]; /* name of file */ + char mode[8]; /* file mode */ + char uid[8]; /* owner user ID */ + char gid[8]; /* owner group ID */ + char size[12]; /* length of file in bytes */ + char mtime[12]; /* modify time of file */ + char chksum[8]; /* checksum for header */ + char typeflag; /* type of file + 0 Regular file + 1 Link to another file already archived + 2 Symbolic link + 3 Character special device + 4 Block special device + 5 Directory + 6 FIFO special file + 7 Reserved */ + char linkname[100]; /* name of linked file */ + char magic[6]; /* USTAR indicator */ + char version[2]; /* USTAR version */ + char uname[32]; /* owner user name */ + char gname[32]; /* owner group name */ + char devmajor[8]; /* device major number */ + char devminor[8]; /* device minor number */ + char prefix[155]; /* prefix for file name; + the value of the prefix field, if non-null, + is prefixed to the name field to allow names + longer then 100 characters */ +} tar_header; +/* }}} */ + +typedef struct _tar_file tar_file; + +typedef struct _tar_entry { /* {{{ */ + tar_file *tar; + char type; + off_t start; + off_t pos; + off_t size; + mode_t mode; + time_t mtime; + uid_t uid; + gid_t gid; + union { + HashTable *content; + char *link; + } u; +} tar_entry; +/* }}} */ + +struct _tar_file { /* {{{ */ + php_stream *stream; + char *real_name; + int refcount; + int opened; + tar_entry root; +}; +/* }}} */ + +static int tar_number(char* buf, int len) /* {{{ */ +{ + int num = 0; + int i = 0; + + while (i < len && buf[i] == ' ') { + i++; + } + while (i < len && + buf[i] >= '0' && + buf[i] <= '7') { + num = num * 8 + (buf[i] - '0'); + i++; + } + return num; +} +/* }}} */ + +static int tar_checksum(char* buf, int len) /* {{{ */ +{ + int sum = 0; + char *end = buf + len; + + while (buf != end) { + sum += (unsigned char)*buf; + buf++; + } + return sum; +} +/* }}} */ +#define MAPPHAR_ALLOC_FAIL(msg) \ + php_stream_close(fp);\ + if (error) {\ + spprintf(error, 0, msg, fname);\ + }\ + return FAILURE; + +#ifdef WORDS_BIGENDIAN +# define PHAR_GET_32(buffer, var) \ + var = ((((unsigned char*)(buffer))[3]) << 24) \ + | ((((unsigned char*)(buffer))[2]) << 16) \ + | ((((unsigned char*)(buffer))[1]) << 8) \ + | (((unsigned char*)(buffer))[0]); \ + (buffer) += 4 +# define PHAR_GET_16(buffer, var) \ + var = ((((unsigned char*)(buffer))[1]) << 8) \ + | (((unsigned char*)(buffer))[0]); \ + (buffer) += 2 +#else +# define PHAR_GET_32(buffer, var) \ + var = *(php_uint32*)(buffer); \ + buffer += 4 +# define PHAR_GET_16(buffer, var) \ + var = *(php_uint16*)(buffer); \ + buffer += 2 +#endif + +int phar2_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, char *buffer, char **error TSRMLS_DC) /* {{{ */ +{ + /* The structure of a tar-based phar archive is as follows: + - the first file contains the loader stub and file hash. The file hash is a mapping + of filename hash to location of manifest data within the official phar manifest using + an implementation of open hash that allows the entire thing to be read in without any + processing beyond signature check. + - the second file contains the official phar manifest as used in phar 1.x + - third file to second-to-last file contain the phar's files + - final file contains the phar signature + */ + php_uint32 openhash_len; + phar_archive_data *mydata; + + buffer += 6; + PHAR_GET_32(buffer), openhash_len); + if (openhash_len != sizeof(phar2_openhash) && openhash_len != sizeof(phar2_openhash_large)) { + MAPPHAR_ALLOC_FAIL("Corrupted phar2 archive, filename hash is wrong size in phar \"%s\""); + } + buffer -= 6; + buffer = (char *) erealloc((void *) buffer, openhash_len); + if (!buffer) { + MAPPHAR_ALLOC_FAIL("Could not allocate space for fast filename hash in phar \"%s\""); + } + if (openhash_len != php_stream_read(fp, buffer, openhash_len)) { + MAPPHAR_ALLOC_FAIL("Could not read fast filename hash in phar \"%s\""); + } + + PHAR_G(fast_fname_map) = (phar2_openhash *) buffer; + mydata = ecalloc(sizeof(phar_archive_data), 1); + + /* check whether we have meta data, zero check works regardless of byte order */ + if (phar_parse_metadata(fp, &buffer, endbuffer, &mydata->metadata TSRMLS_CC) == FAILURE) { + MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\""); + } + + /* set up our manifest - entries will be created JIT as accessed */ + zend_hash_init(&mydata->manifest, sizeof(phar_entry_info), + zend_get_hash_value, destroy_phar_manifest, 0); + snprintf(mydata->version, sizeof(mydata->version), "%u.%u.%u", manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0xF); + mydata->internal_file_start = halt_offset + manifest_len + 4; + mydata->halt_offset = halt_offset; + mydata->flags = manifest_flags; + mydata->fp = fp; + mydata->fname = estrndup(fname, fname_len); +#ifdef PHP_WIN32 + phar_unixify_path_separators(mydata->fname, fname_len); +#endif + mydata->fname_len = fname_len; + mydata->alias = alias ? estrndup(alias, alias_len) : mydata->fname; + mydata->alias_len = alias ? alias_len : fname_len; + mydata->sig_flags = sig_flags; + mydata->sig_len = sig_len; + mydata->signature = signature; + phar_request_initialize(TSRMLS_C); + zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + if (register_alias) { + mydata->is_explicit_alias = 1; + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + } else { + mydata->is_explicit_alias = 0; + } + efree(savebuf); + + if (pphar) { + *pphar = mydata; + } + + return SUCCESS; +} +/* }}} */
\ No newline at end of file diff --git a/ext/phar/phar2.h b/ext/phar/phar2.h new file mode 100644 index 0000000000..b35e636638 --- /dev/null +++ b/ext/phar/phar2.h @@ -0,0 +1,34 @@ +/* + +----------------------------------------------------------------------+ + | phar php single-file executable PHP extension | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gregory Beaver <cellog@php.net> | + | Marcus Boerger <helly@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +BEGIN_EXTERN_C() + +int phar2_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, char *buffer, char **error TSRMLS_DC); + +END_EXTERN_C() + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/phar/phar2_openhash.c b/ext/phar/phar2_openhash.c new file mode 100644 index 0000000000..47c7be656d --- /dev/null +++ b/ext/phar/phar2_openhash.c @@ -0,0 +1,150 @@ +/* + +----------------------------------------------------------------------+ + | phar open hash implementation for phar2 tar-based archives | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gregory Beaver <cellog@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "zend_hash.h" + +static inline int phar2_openhash_doublehash(ulong k, int m, int i) /* {{{ */ +{ + /* implement double hash probing */ + return ((k % m) + (i + (k % (m - 1)))) % m; +} +/* }}} */ + +phar2_openhash *phar2_openhash_init(int large) /* {{{ */ +{ + phar2_openhash *hash; + if (large) { + hash = (phar2_openhash *) ecalloc(sizeof(phar2_openhash_large)); + } else { + hash = (phar2_openhash *) ecalloc(sizeof(phar2_openhash)); + hash->is_large = '\001'; + } + return hash; +} +/* }}} */ + +int phar2_openhash_destroy(phar2_openhash *hash) /* {{{ */ +{ + return efree(hash); +} +/* }}} */ + +/* retrieve the offset of this file's manifest entry. Returns offset or -1 */ +long phar2_openhash_getoffset(phar2_openhash *hash, char *fname, int len) /* {{{ */ +{ + int myhash, hashsize; + char substr[20]; + + memset((void *) substr, 0, 20); + if (len < 20) { + memcpy((void *) &substr, (void *) fname, len); + } else { + memcpy((void *) &substr, (void *) fname + (len - 20), 20); + } + hashsize = (hash->is_large ? PHAR2_HASHSIZE : PHAR2_HASHSIZE_SMALL); + for (i = 0; i < hashsize; i ++) { + myhash = phar2_openhash_doublehash(zhash, hashsize, i); + if (hash->info[myhash].filepart[0]) { + if (!memcmp(hash->info[myhash].filepart, substr, 20)) { + /* found it */ + return hash->info[myhash].offset; + } + } else { + /* empty - does not exist */ + return -1; + } + } + return -1; +} +/* }}} */ + +static int phar2_openhash_insert_ex(phar2_openhash *hash, char* fname, ulong zhash, long offset) /* {{{ */ +{ + int myhash, hashsize; + hashsize = (hash->is_large ? PHAR2_HASHSIZE : PHAR2_HASHSIZE_SMALL); + for (i = 0; i < hashsize; i ++) { + myhash = phar2_openhash_doublehash(zhash, hashsize, i); + if (hash->info[myhash].filepart[0]) { + continue; + } else { + memcpy((void *) &hash->info[myhash].filepart, (void *) fname, 20); + hash->info[myhash].offset = offset; + hash->info[myhash].zhash = zhash; + hash->count++; + return SUCCESS; + } + } + return FAILURE; +} +/* }}} */ + +/* a super-large phar is being created, enlarge the hash and re-populate it */ +phar2_openhash_large *phar2_openhash_enlarge(phar2_openhash *hash) /* {{{ */ +{ + phar_openhash_large *newhash = phar2_openhash_init(1); + for (phar2_openhash_entry s = newhash->info, int i = 0; i < hash->count; i++) { + if (!s[i].filepart[0]) { + continue; + } + phar2_openhash_insert_ex((phar2_openhash *)newhash, &(s[i].filepart), s[i].zhash, s[i].offset); + } + phar2_openhash_destroy(hash); + return newhash; +} +/* }}} */ + +int phar2_openhash_insert(phar2_openhash *hash, char* fname, int len, long offset) /* {{{ */ +{ + ulong zhash = zend_inline_hash_func(string, len); + char substr[20]; + + if (-1 != phar2_openhash_getoffset(hash, fname, len)) { + /* already exists, so fail */ + return FAILURE; + } + if (hash->is_large) { + if (hash->count + 1 == PHAR_HASHSIZE) { + return FAILURE; + } + } else { + if (hash->count + 1 == PHAR_HASHSIZE_SMALL) { + /* resize for mega-huge phars */ + hash = (phar2_openhash *) phar2_openhash_enlarge(hash); + } + } + + memset((void *) substr, 0, 20); + if (len < 20) { + memcpy((void *) &substr, (void *) fname, len); + } else { + memcpy((void *) &substr, (void *) fname + (len - 20), 20); + } + return phar2_openhash_insert_ex(hash, substr, zhash, offset); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/phar/phar2_openhash.h b/ext/phar/phar2_openhash.h new file mode 100644 index 0000000000..f8ef10006e --- /dev/null +++ b/ext/phar/phar2_openhash.h @@ -0,0 +1,59 @@ +/* + +----------------------------------------------------------------------+ + | phar open hash implementation for phar2 tar-based archives | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gregory Beaver <cellog@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* maximum number of files that will fit into a phar */ +#define PHAR2_HASHSIZE_SMALL 997 +#define PHAR2_HASHSIZE 4999 + +typedef struct _phar2_openhash_entry { + char[20] filepart; /* last 10 characters of filename */ + long offset; /* offset of this file's info in the manifest */ + ulong zhash; /* value of zend_inline_hash_func() on the filename */ +} phar2_openhash_entry; + +typedef struct _phar2_openhash { + char is_large; /* always 0 */ + int count; + phar2_openhash_entry info[PHAR2_HASHSIZE_SMALL]; +} phar2_openhash; + +typedef struct _phar2_openhash { + char is_large; /* always 1 */ + int count; + phar2_openhash_entry info[PHAR2_HASHSIZE]; +} phar2_openhash_large; + +BEGIN_EXTERN_C() + +phar2_openhash *phar2_openhash_init(int large); +int phar2_openhash_destroy(phar2_openhash *hash); +long phar2_openhash_getoffset(char *fname, int len); +int phar2_openhash_insert(phar2_openhash *hash, char* fname, int len, long offset); + +END_EXTERN_C() + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 0c4e85c436..e075ebdf01 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -30,6 +30,7 @@ #include "zend_execute.h" #include "zend_exceptions.h" #include "zend_hash.h" +#include "ext/phar/phar2_openhash.h" #include "zend_interfaces.h" #include "zend_operators.h" #include "zend_qsort.h" @@ -65,12 +66,12 @@ #define E_RECOVERABLE_ERROR E_ERROR #endif -#define PHAR_EXT_VERSION_STR "1.3.0" -#define PHAR_API_VERSION_STR "1.1.0" +#define PHAR_EXT_VERSION_STR "2.0.0" +#define PHAR_API_VERSION_STR "2.0.0" /* x.y.z maps to 0xyz0 */ -#define PHAR_API_VERSION 0x1100 +#define PHAR_API_VERSION 0x2000 #define PHAR_API_MIN_READ 0x1000 -#define PHAR_API_MAJORVERSION 0x1000 +#define PHAR_API_MAJORVERSION 0x2000 #define PHAR_API_MAJORVER_MASK 0xF000 #define PHAR_API_VER_MASK 0xFFF0 @@ -106,6 +107,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phar) HashTable phar_fname_map; HashTable phar_alias_map; HashTable phar_plain_map; + phar2_openhash *fast_fname_map; char* extract_list; int readonly; zend_bool readonly_orig; |