From 9c2651d210791721d339b54ee23920f749d7fb04 Mon Sep 17 00:00:00 2001 From: Greg Beaver Date: Sun, 21 Jan 2007 23:22:57 +0000 Subject: [PATCH] X Phar->setStub() for specifying a new stub to the phar [Greg] --- ext/phar/TODO | 4 +- ext/phar/phar.c | 102 ++++++++++++++++++++++++++-------- ext/phar/phar_internal.h | 2 +- ext/phar/phar_object.c | 49 +++++++++++++++- ext/phar/tests/phar_stub.phpt | 80 ++++++++++++++++++++++++++ 5 files changed, 209 insertions(+), 28 deletions(-) create mode 100644 ext/phar/tests/phar_stub.phpt diff --git a/ext/phar/TODO b/ext/phar/TODO index a7728945444..432e754e842 100644 --- a/ext/phar/TODO +++ b/ext/phar/TODO @@ -12,9 +12,9 @@ Version 1.0.0 have a handle opened for writing * docs on file format/manifest description * docs on uses - * stream context for specifying compression of a file + X stream context for specifying compression of a file [Marcus] * stream context for specifying meta-data - * stream context for specifying a new prologue to the phar + X Phar->setStub() for specifying a new stub to the phar [Greg] * add setUncompressed(), setCompressedGZ() and setCompressedBZ2() to PharFileInfo class * add uncompressAllFiles(), compressAllFilesGZ() and compressAllFilesBZ2() diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 9a337fd0999..4e7d9e292af 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1170,6 +1170,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat php_stream *fp, *fpf; php_stream_filter *filter, *consumed; php_uint32 offset; + zval **pzoption; resource = php_url_parse(path); @@ -1203,6 +1204,17 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat idata->phar->refcount++; php_url_free(resource); efree(internal_file); + if (idata->internal_file->uncompressed_filesize == 0 + && idata->internal_file->compressed_filesize == 0 + && context && context->options + && zend_hash_find(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption) == SUCCESS + && zend_hash_find(HASH_OF(*pzoption), "compress", sizeof("compress"), (void**)&pzoption) == SUCCESS + && Z_TYPE_PP(pzoption) == IS_LONG + && (Z_LVAL_PP(pzoption) & ~PHAR_ENT_COMPRESSION_MASK) == 0 + ) { + idata->internal_file->flags &= ~PHAR_ENT_COMPRESSION_MASK; + idata->internal_file->flags |= Z_LVAL_PP(pzoption); + } return fpf; } else { if (NULL == (idata = phar_get_entry_data(resource->host, strlen(resource->host), internal_file, strlen(internal_file) TSRMLS_CC))) { @@ -1287,7 +1299,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat idata->fp = php_stream_temp_new(); if (php_stream_copy_to_stream(fp, idata->fp, idata->internal_file->uncompressed_filesize) != idata->internal_file->uncompressed_filesize) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", idata->phar->fname, internal_file); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch 1 on file \"%s\")", idata->phar->fname, internal_file); php_stream_close(idata->fp); efree(idata); efree(internal_file); @@ -1298,7 +1310,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat php_stream_filter_flush(consumed, 1); php_stream_filter_remove(consumed, 1 TSRMLS_CC); if (offset + idata->internal_file->compressed_filesize != php_stream_tell(fp)) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", idata->phar->fname, internal_file); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch 2 on file \"%s\")", idata->phar->fname, internal_file); php_stream_close(idata->fp); efree(idata); efree(internal_file); @@ -1310,7 +1322,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat /* bypass to temp stream */ idata->fp = php_stream_temp_new(); if (php_stream_copy_to_stream(fp, idata->fp, idata->internal_file->uncompressed_filesize) != idata->internal_file->uncompressed_filesize) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", idata->phar->fname, internal_file); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch 3 on file \"%s\")", idata->phar->fname, internal_file); php_stream_close(idata->fp); efree(idata); efree(internal_file); @@ -1537,9 +1549,15 @@ static inline void phar_set_16(char *buffer, int var) /* {{{ */ #endif } /* }}} */ -int phar_flush(phar_entry_data *data TSRMLS_DC) /* {{{ */ +/** + * Save phar contents to disk + * + * user_stub contains either a string, or a resource pointer, if len is a negative length. + * user_stub and len should be both 0 if the default or existing stub should be used + */ +int phar_flush(phar_entry_data *data, char *user_stub, long len TSRMLS_DC) /* {{{ */ { - static const char newprologue[] = "phar->halt_offset && oldfile) { - if (data->phar->halt_offset != php_stream_copy_to_stream(oldfile, newfile, data->phar->halt_offset)) { - if (oldfile) { - php_stream_close(oldfile); + if (len != 0) { + if (len < 0) { + /* resource passed in */ + if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) { + if (oldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to read resource to copy stub to new phar \"%s\"", data->phar->fname); + return EOF; + } + if (len == -1) { + len = PHP_STREAM_COPY_ALL; + } else { + len = -len; + } + if (len != php_stream_copy_to_stream(stubfile, newfile, len) && len != PHP_STREAM_COPY_ALL) { + if (oldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to copy stub from resource to new phar \"%s\"", data->phar->fname); + return EOF; + } + } else { + if (len != php_stream_write(newfile, user_stub, len)) { + if (oldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to create stub from string in new phar \"%s\"", data->phar->fname); + return EOF; } - php_stream_close(newfile); - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to copy prologue of old phar to new phar \"%s\"", data->phar->fname); - return EOF; } } else { - /* this is a brand new phar */ - data->phar->halt_offset = sizeof(newprologue)-1; - if (sizeof(newprologue)-1 != php_stream_write(newfile, newprologue, sizeof(newprologue)-1)) { - if (oldfile) { - php_stream_close(oldfile); + if (data->phar->halt_offset && oldfile) { + if (data->phar->halt_offset != php_stream_copy_to_stream(oldfile, newfile, data->phar->halt_offset)) { + if (oldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to copy stub of old phar to new phar \"%s\"", data->phar->fname); + return EOF; + } + } else { + /* this is a brand new phar */ + data->phar->halt_offset = sizeof(newstub)-1; + if (sizeof(newstub)-1 != php_stream_write(newfile, newstub, sizeof(newstub)-1)) { + if (oldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to create stub in new phar \"%s\"", data->phar->fname); + return EOF; } - php_stream_close(newfile); - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to create prologue in new phar \"%s\"", data->phar->fname); - return EOF; } } manifest_ftell = php_stream_tell(newfile); + halt_offset = manifest_ftell; /* compress as necessary, calculate crcs, manifest size, and file sizes */ new_manifest_count = 0; @@ -1912,7 +1967,6 @@ int phar_flush(phar_entry_data *data TSRMLS_DC) /* {{{ */ alias = 0; } alias_len = data->phar->alias_len; - halt_offset = data->phar->halt_offset; zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_alias_map), phar_unalias_apply, data->phar TSRMLS_CC); zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len); phar_open_file(file, fname, fname_len, alias, alias_len, halt_offset, NULL TSRMLS_CC); @@ -1930,7 +1984,7 @@ int phar_flush(phar_entry_data *data TSRMLS_DC) /* {{{ */ static int phar_stream_flush(php_stream *stream TSRMLS_DC) /* {{{ */ { if (stream->mode[0] == 'w' || (stream->mode[0] == 'r' && stream->mode[1] == '+')) { - return phar_flush((phar_entry_data *)stream->abstract TSRMLS_CC); + return phar_flush((phar_entry_data *)stream->abstract, 0, 0 TSRMLS_CC); } else { return EOF; } @@ -2315,7 +2369,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio idata = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); idata->fp = 0; idata->phar = phar_get_archive(resource->host, strlen(resource->host), 0, 0 TSRMLS_CC); - phar_flush(idata TSRMLS_CC); + phar_flush(idata, 0, 0 TSRMLS_CC); php_url_free(resource); efree(idata); return SUCCESS; diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 028a723e71f..53f1343e35d 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -247,7 +247,7 @@ static int phar_dir_stat( php_stream *stream, php_stream_statbuf *ssb TSRMLS_ void phar_destroy_phar_data(phar_archive_data *data TSRMLS_DC); phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, int path_len TSRMLS_DC); phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char *path, int path_len TSRMLS_DC); -int phar_flush(phar_entry_data *data TSRMLS_DC); +int phar_flush(phar_entry_data *data, char *user_stub, long len TSRMLS_DC); int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len TSRMLS_DC); END_EXTERN_C() diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index b816b70beaa..eae4a83fb4e 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -362,7 +362,7 @@ PHP_METHOD(Phar, offsetUnset) data->phar = phar_obj->arc.archive; data->fp = 0; /* internal_file is unused in phar_flush, so we won't set it */ - phar_flush(data TSRMLS_CC); + phar_flush(data, 0, 0 TSRMLS_CC); efree(data); RETURN_TRUE; } @@ -372,6 +372,47 @@ PHP_METHOD(Phar, offsetUnset) } /* }}} */ +/* {{{ proto int Phar::setStub(string|stream stub [, int len]) + * set the pre-phar stub for the current writeable phar + */ +PHP_METHOD(Phar, setStub) +{ + zval *stub; + phar_entry_data *idata; + long len = -1; + php_stream *stream; + PHAR_ARCHIVE_OBJECT(); + if (PHAR_G(readonly)) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "Cannot set stub, phar is read-only"); + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &stub, &len) == FAILURE) { + return; + } + + idata = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); + idata->phar = phar_obj->arc.archive; + idata->fp = 0; + idata->internal_file = 0; + + if (Z_TYPE_P(stub) == IS_STRING) { + phar_flush(idata, Z_STRVAL_P(stub), Z_STRLEN_P(stub) TSRMLS_CC); + efree(idata); + } else if (Z_TYPE_P(stub) == IS_RESOURCE && (php_stream_from_zval_no_verify(stream, &stub))) { + if (len > 0) { + len = -len; + } + phar_flush(idata, (char *) &stub, len TSRMLS_CC); + efree(idata); + } else { + efree(idata); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "Cannot only set stub to a string or read from a stream resource"); + } +} +/* }}} */ + /* {{{ proto void PharFileInfo::__construct(string entry) * Construct a Phar entry object */ @@ -573,6 +614,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mapPhar, 0, 0, 0) ZEND_ARG_INFO(0, alias) ZEND_END_ARG_INFO(); +static +ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setStub, 0, 0, 0) + ZEND_ARG_INFO(0, newstub) +ZEND_END_ARG_INFO(); + static ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_loadPhar, 0, 0, 1) ZEND_ARG_INFO(0, fname) @@ -601,6 +647,7 @@ zend_function_entry php_archive_methods[] = { PHP_ME(Phar, getVersion, NULL, 0) PHP_ME(Phar, getSignature, NULL, 0) PHP_ME(Phar, getModified, NULL, 0) + PHP_ME(Phar, setStub, arginfo_phar_setStub, ZEND_ACC_PUBLIC) PHP_ME(Phar, offsetGet, arginfo_phar_offsetExists, ZEND_ACC_PUBLIC) PHP_ME(Phar, offsetSet, arginfo_phar_offsetSet, ZEND_ACC_PUBLIC) PHP_ME(Phar, offsetUnset, arginfo_phar_offsetExists, ZEND_ACC_PUBLIC) diff --git a/ext/phar/tests/phar_stub.phpt b/ext/phar/tests/phar_stub.phpt new file mode 100644 index 00000000000..97f7f15ce24 --- /dev/null +++ b/ext/phar/tests/phar_stub.phpt @@ -0,0 +1,80 @@ +--TEST-- +Phar stub +--SKIPIF-- + +--INI-- +phar.require_hash=0 +--FILE-- +'; + +$files = array(); +$files['a'] = 'a'; +$files['b'] = 'b'; +$files['c'] = 'c'; + +include 'phar_test.inc'; + +$file = ''; +$fp = fopen($fname, 'rb'); +//// 1 +echo fread($fp, strlen($file)) . "\n"; +fclose($fp); +$phar = new Phar($fname); +$file = ''; + +//// 2 +$phar->setStub($file); +$fp = fopen($fname, 'rb'); +echo fread($fp, strlen($file)) . "\n"; +fclose($fp); + +$fname2 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phartmp.php'; +$file = ''; +$fp = fopen($fname2, 'wb'); +fwrite($fp, $file); +fclose($fp); +$fp = fopen($fname2, 'rb'); + +//// 3 +$phar->setStub($fp); +fclose($fp); + +$fp = fopen($fname, 'rb'); +echo fread($fp, strlen($file)) . "\n"; +fclose($fp); + +$fp = fopen($fname2, 'ab'); +fwrite($fp, 'booya'); +fclose($fp); +echo file_get_contents($fname2) . "\n"; + +$fp = fopen($fname2, 'rb'); + +//// 4 +$phar->setStub($fp, strlen($file)); +fclose($fp); + +$fp = fopen($fname, 'rb'); +echo fread($fp, strlen($file)) . "\n"; +if (fread($fp, strlen('booya')) == 'booya') { + echo 'failed - copied booya'; +} +fclose($fp); +?> +===DONE=== +--CLEAN-- + +--EXPECT-- +===DONE=== + + + +booya + \ No newline at end of file