re-organize, create util.c, move entry_info/archive_data/entry_data access methods to this file
refactor entry->fp, now this is abstracted with phar_get_efp() and phar_seek_efp(), fixes all weird dependency issues
permanently solve the "millions of file pointers" issue for read access. All compressed files are read into a single
temporary stream, and their constraints are controlled by the entry->fp abstraction
Improvements in this zip implementation over ext/zip:
* full read/write support for bzip2 compressed files
* much more efficient access for accessing only a few files within large zip files, as crc/header validation is
done just-in-time
* full stream support for opendir/rename/rmdir/mkdir as well as all of the other stream funcs
* full support for setting file perms via Phar::chmod(), stored as zip-standard extra field
* no problem with large zips and many open file pointers
# TODO: add big-endian system support for tar/zip file format headers, otherwise the implementation is complete
# TODO: test on windows and fix any windows-specific issues
# TODO: verify zips created work with unzip/winzip/windows explorer and so on
[ --enable-phar Enable phar support])
if test "$PHP_PHAR" != "no"; then
- AC_MSG_CHECKING([for ZIP includes])
- if test -f $abs_srcdir/include/php/ext/zip/lib/zip.h; then
- zip_inc_path=$abs_srcdir/ext
- AC_DEFINE(HAVE_PHAR_ZIP,1,[ ])
- AC_MSG_RESULT($zip_inc_path)
- PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared,,-I$zip_inc_path $PHAR_DEFS)
- elif test -f $abs_srcdir/ext/zip/lib/zip.h; then
- zip_inc_path=$abs_srcdir/ext
- AC_DEFINE(HAVE_PHAR_ZIP,1,[ ])
- AC_MSG_RESULT($zip_inc_path)
- PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared,,-I$zip_inc_path $PHAR_DEFS)
- elif test -f $prefix/include/php/ext/zip/lib/zip.h; then
- zip_inc_path=$prefix/include/php/ext
- AC_DEFINE(HAVE_PHAR_ZIP,1,[ ])
- AC_MSG_RESULT($zip_inc_path)
- PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared,,-I$zip_inc_path $PHAR_DEFS)
- else
- zip_inc_path=/dev/null
- AC_DEFINE(HAVE_PHAR_ZIP,0,[ ])
- AC_MSG_RESULT([not found, disabling ZIP-based phar support])
- PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared)
- fi
+ PHP_NEW_EXTENSION(phar, util.c tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared)
PHP_ADD_BUILD_DIR($ext_builddir/lib, 1)
PHP_SUBST(PHAR_SHARED_LIBADD)
- PHP_ADD_EXTENSION_DEP(phar, zip, true)
PHP_ADD_EXTENSION_DEP(phar, zlib, true)
PHP_ADD_EXTENSION_DEP(phar, bz2, true)
PHP_ADD_EXTENSION_DEP(phar, spl, true)
ARG_ENABLE("phar", "enable phar support", "no");
if (PHP_PHAR != "no") {
- EXTENSION("phar", "dirstream.c func_interceptors.c phar.c phar_object.c phar_path_check.c stream.c tar.c zip.c");
+ EXTENSION("phar", "util.c dirstream.c func_interceptors.c phar.c phar_object.c phar_path_check.c stream.c tar.c zip.c");
if (PHP_PHAR_SHARED) {
ADD_FLAG("CFLAGS_PHAR", "/D COMPILE_DL_PHAR ");
}
- if (PHP_ZIP_SHARED) {
- if (PHP_PHAR_SHARED) {
- ADD_FLAG("CFLAGS_PHAR", "/D COMPILE_DL_ZIP ");
- } else {
- ERROR("Phar cannot be built statically with shared Zip extension");
- }
- }
- if (PHP_ZIP != "no") {
- ADD_EXTENSION_DEP('phar', 'zip', true);
- AC_DEFINE('HAVE_PHAR_ZIP', 1);
- }
ADD_EXTENSION_DEP('phar', 'bz2', true);
ADD_EXTENSION_DEP('phar', 'spl', true);
ADD_EXTENSION_DEP('phar', 'zlib', true);
memset((void *) &entry, 0, sizeof(phar_entry_info));
/* strip leading "/" */
-#if HAVE_PHAR_ZIP
if (phar->is_zip) {
entry.is_zip = 1;
- /* prevent attempts to check the CRC */
- entry.index = -1;
}
-#endif
entry.filename = estrdup(resource->path + 1);
if (phar->is_tar) {
entry.is_tar = 1;
* When all uses of a phar have been concluded, this frees the manifest
* and the phar slot
*/
-static void phar_destroy_phar_data(phar_archive_data *data TSRMLS_DC) /* {{{ */
+static void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */
{
-#if HAVE_PHAR_ZIP
- if (data->zip) {
- _zip_free(data->zip);
- data->zip = 0;
+ if (phar->alias && phar->alias != phar->fname) {
+ efree(phar->alias);
+ phar->alias = NULL;
}
-#endif
- if (data->alias && data->alias != data->fname) {
- efree(data->alias);
- data->alias = NULL;
+ if (phar->fname) {
+ efree(phar->fname);
+ phar->fname = NULL;
}
- if (data->fname) {
- efree(data->fname);
- data->fname = NULL;
+ if (phar->signature) {
+ efree(phar->signature);
}
- if (data->signature) {
- efree(data->signature);
+ if (phar->manifest.arBuckets) {
+ zend_hash_destroy(&phar->manifest);
+ phar->manifest.arBuckets = NULL;
}
- if (data->manifest.arBuckets) {
- zend_hash_destroy(&data->manifest);
- data->manifest.arBuckets = NULL;
+ if (phar->metadata) {
+ zval_ptr_dtor(&phar->metadata);
+ phar->metadata = 0;
}
- if (data->metadata) {
- zval_ptr_dtor(&data->metadata);
- data->metadata = 0;
+ if (phar->fp) {
+ php_stream_close(phar->fp);
+ phar->fp = 0;
}
- if (data->fp) {
- php_stream_close(data->fp);
- data->fp = 0;
+ if (phar->ufp) {
+ php_stream_close(phar->ufp);
+ phar->fp = 0;
}
- efree(data);
+ efree(phar);
}
/* }}}*/
entry->cfp = 0;
}
if (entry->fp) {
- if (entry->fp != entry->phar->fp) {
- php_stream_close(entry->fp);
- }
+ php_stream_close(entry->fp);
entry->fp = 0;
}
if (entry->metadata) {
entry->metadata_str.c = 0;
}
efree(entry->filename);
-#if HAVE_PHAR_ZIP
- if (entry->zip) {
- zip_fclose(entry->zip);
- entry->zip = 0;
- }
-#endif
if (entry->link) {
efree(entry->link);
entry->link = 0;
}
/* }}} */
-/**
- * Looks up a phar archive in the filename map, connecting it to the alias
- * (if any) or returns null
- */
-int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
-{
- phar_archive_data *fd, **fd_ptr;
- char *my_realpath, *save;
- int save_len;
-
- phar_request_initialize(TSRMLS_C);
-
- if (error) {
- *error = NULL;
- }
- *archive = NULL;
- if (alias && alias_len) {
- if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void**)&fd_ptr)) {
- if (fname && (fname_len != (*fd_ptr)->fname_len || strncmp(fname, (*fd_ptr)->fname, fname_len))) {
- if (error) {
- spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname);
- }
- return FAILURE;
- }
- *archive = *fd_ptr;
- return SUCCESS;
- }
- }
- my_realpath = NULL;
- save = fname;
- save_len = fname_len;
- if (fname && fname_len) {
- if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) {
- *archive = *fd_ptr;
- fd = *fd_ptr;
- if (alias && alias_len) {
- zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd, sizeof(phar_archive_data*), NULL);
- }
- return SUCCESS;
- }
- if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, (void**)&fd_ptr)) {
- *archive = *fd_ptr;
- return SUCCESS;
- }
-
- /* not found, try converting \ to / */
- my_realpath = expand_filepath(fname, my_realpath TSRMLS_CC);
- if (my_realpath) {
- fname_len = strlen(my_realpath);
- fname = my_realpath;
- } else {
- return FAILURE;
- }
-#ifdef PHP_WIN32
- phar_unixify_path_separators(fname, fname_len);
-#endif
- if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) {
- *archive = *fd_ptr;
- fd = *fd_ptr;
- if (alias && alias_len) {
- zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd, sizeof(phar_archive_data*), NULL);
- }
- efree(my_realpath);
- return SUCCESS;
- }
- efree(my_realpath);
- }
- return FAILURE;
-}
-/* }}} */
-
-/**
- * retrieve information on a file contained within a phar, or null if it ain't there
- */
-phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, int path_len, char **error TSRMLS_DC) /* {{{ */
-{
- return phar_get_entry_info_dir(phar, path, path_len, 0, error TSRMLS_CC);
-}
-/* }}} */
-/**
- * retrieve information on a file or directory contained within a phar, or null if none found
- * allow_dir is 0 for none, 1 for both empty directories in the phar and temp directories, and 2 for only
- * valid pre-existing empty directory entries
- */
-phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, int path_len, char dir, char **error TSRMLS_DC) /* {{{ */
-{
- const char *pcr_error;
- phar_entry_info *entry;
- char is_dir = (path[path_len - 1] == '/');
-
- if (error) {
- *error = NULL;
- }
-
- if (!path_len && !dir) {
- if (error) {
- spprintf(error, 4096, "phar error: invalid path \"%s\" must not be empty", path);
- }
- return NULL;
- }
- if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
- if (error) {
- spprintf(error, 4096, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
- }
- return NULL;
- }
-
- if (!&phar->manifest.arBuckets) {
- return NULL;
- }
- if (is_dir) {
- path_len--;
- }
- if (SUCCESS == zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) {
- if (entry->is_deleted) {
- /* entry is deleted, but has not been flushed to disk yet */
- return NULL;
- }
- if (entry->is_dir && !dir) {
- if (error) {
- spprintf(error, 4096, "phar error: path \"%s\" is a directory", path);
- }
- return NULL;
- }
- if (!entry->is_dir && is_dir) {
- /* user requested a directory, we must return one */
- if (error) {
- spprintf(error, 4096, "phar error: path \"%s\" exists and is a not a directory", path);
- }
- return NULL;
- }
- return entry;
- }
- if (dir == 1) {
- /* try to find a directory */
- HashTable *manifest;
- char *key;
- uint keylen;
- ulong unused;
-
- if (!path_len) {
- path = "/";
- }
- manifest = &phar->manifest;
- zend_hash_internal_pointer_reset(manifest);
- while (FAILURE != zend_hash_has_more_elements(manifest)) {
- if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
- break;
- }
- if (0 != memcmp(key, path, path_len)) {
- /* entry in directory not found */
- if (SUCCESS != zend_hash_move_forward(manifest)) {
- break;
- }
- continue;
- } else {
- if (key[path_len] != '/') {
- if (SUCCESS != zend_hash_move_forward(manifest)) {
- break;
- }
- continue;
- }
- /* found a file in this path */
- entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info));
- /* this next line tells PharFileInfo->__destruct() to efree the filename */
- entry->is_temp_dir = entry->is_dir = 1;
- entry->filename = (char *) estrndup(path, path_len + 1);
- entry->filename_len = path_len;
- return entry;
- }
- }
- }
- return NULL;
-}
-/* }}} */
-
-#if defined(PHP_VERSION_ID) && PHP_VERSION_ID < 50202
-typedef struct {
- char *data;
- size_t fpos;
- size_t fsize;
- size_t smax;
- int mode;
- php_stream **owner_ptr;
-} php_stream_memory_data;
-#endif
-
-/* this is only called for non-zip/tar-based phars */
-static int phar_open_entry_file(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */
-{
- if (error) {
- *error = NULL;
- }
- /* open a new temp file for writing */
- entry->fp = php_stream_fopen_tmpfile();
- if (!entry->fp) {
- if (error) {
- spprintf(error, 0, "phar error: unable to create temporary file");
- }
- return FAILURE;
- }
- entry->old_flags = entry->flags;
- entry->is_modified = 1;
- phar->is_modified = 1;
- /* reset file size */
- entry->uncompressed_filesize = 0;
- entry->compressed_filesize = 0;
- entry->crc32 = 0;
- entry->flags = PHAR_ENT_PERM_DEF_FILE;
- return SUCCESS;
-}
-/* }}} */
-
-/**
- * Retrieve a copy of the file information on a single file within a phar, or null.
- * This also transfers the open file pointer, if any, to the entry.
- *
- * If the file does not already exist, this will fail. Pre-existing files can be
- * appended, truncated, or read. For read, if the entry is marked unmodified, it is
- * assumed that the file pointer, if present, is opened for reading
- */
-int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC) /* {{{ */
-{
- phar_archive_data *phar;
- phar_entry_info *entry;
- int for_write = mode[0] != 'r' || mode[1] == '+';
- int for_append = mode[0] == 'a';
- int for_create = mode[0] != 'r';
- int for_trunc = mode[0] == 'w';
-
- if (!ret) {
- return FAILURE;
- }
- *ret = NULL;
- if (error) {
- *error = NULL;
- }
- if (for_write && PHAR_G(readonly)) {
- if (error) {
- spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, disabled by ini setting", path, fname);
- }
- return FAILURE;
- }
- if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
- return FAILURE;
- }
- if (!path_len) {
- if (error) {
- spprintf(error, 4096, "phar error: file \"\" in phar \"%s\" cannot be empty", fname);
- }
- return FAILURE;
- }
- if (allow_dir) {
- if ((entry = phar_get_entry_info_dir(phar, path, path_len, 2, for_create && !PHAR_G(readonly) ? NULL : error TSRMLS_CC)) == NULL) {
- if (for_create && !PHAR_G(readonly)) {
- return SUCCESS;
- }
- return FAILURE;
- }
- } else {
- if ((entry = phar_get_entry_info(phar, path, path_len, for_create && !PHAR_G(readonly) ? NULL : error TSRMLS_CC)) == NULL) {
- if (for_create && !PHAR_G(readonly)) {
- return SUCCESS;
- }
- return FAILURE;
- }
- }
- if (entry->is_modified && !for_write) {
- if (error) {
- spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for reading, writable file pointers are open", path, fname);
- }
- return FAILURE;
- }
- if (entry->fp_refcount && for_write) {
- if (error) {
- spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, readable file pointers are open", path, fname);
- }
- return FAILURE;
- }
- if (entry->is_deleted) {
- if (!for_create) {
- return FAILURE;
- }
- entry->is_deleted = 0;
- }
- *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
- (*ret)->position = 0;
- (*ret)->zero = 0;
- (*ret)->phar = phar;
- (*ret)->for_write = for_write;
- (*ret)->internal_file = entry;
- (*ret)->is_zip = entry->is_zip;
- (*ret)->is_tar = entry->is_tar;
- if (entry->is_dir) {
- entry->phar->refcount++;
- entry->fp_refcount++;
- return SUCCESS;
- }
- if (entry->fp) {
- /* make a copy */
- if (for_trunc) {
- if (entry->fp == phar->fp) {
- /* duplicate entry if we are writing and are recycling the phar fp */
- if (FAILURE == phar_open_entry_file(phar, entry, error TSRMLS_CC)) {
- return FAILURE;
- }
- (*ret)->fp = entry->fp;
- }
-#if PHP_VERSION_ID >= 50202
- php_stream_truncate_set_size(entry->fp, 0);
-#else
- if (php_stream_is(entry->fp, PHP_STREAM_IS_TEMP)) {
- if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_MEMORY)) {
- php_stream *inner = *(php_stream**)entry->fp->abstract;
- php_stream_memory_data *memfp = (php_stream_memory_data*)inner->abstract;
- memfp->fpos = 0;
- memfp->fsize = 0;
- } else if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_STDIO)) {
- php_stream_truncate_set_size(*(php_stream**)entry->fp->abstract, 0);
- } else {
- efree(*ret);
- *ret = NULL;
- if (error) {
- spprintf(error, 0, "phar error: file \"%s\" cannot be opened for writing, no truncate support", fname);
- }
- return FAILURE;
- }
- } else if (php_stream_is(entry->fp, PHP_STREAM_IS_STDIO)) {
- php_stream_truncate_set_size(entry->fp, 0);
- } else {
- efree(*ret);
- *ret = NULL;
- if (error) {
- spprintf(error, 0, "phar error: file \"%s\" cannot be opened for writing, no truncate support", fname);
- }
- return FAILURE;
- }
-#endif
- entry->old_flags = entry->flags;
- entry->is_modified = 1;
- phar->is_modified = 1;
- /* reset file size */
- entry->uncompressed_filesize = 0;
- entry->compressed_filesize = 0;
- entry->crc32 = 0;
- } else if (for_append) {
- php_stream_seek(entry->fp, 0, SEEK_END);
- }
- (*ret)->fp = entry->fp;
- if (entry->fp == phar->fp) {
- (*ret)->zero = entry->offset_within_phar + phar->internal_file_start;
- }
- } else {
- (*ret)->fp = 0;
- if (for_write) {
- if (FAILURE == phar_open_entry_file(phar, entry, error TSRMLS_CC)) {
- return FAILURE;
- }
- (*ret)->fp = entry->fp;
- }
- }
- entry->fp_refcount++;
- entry->phar->refcount++;
- return SUCCESS;
-}
-/* }}} */
-
int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */
{
int ret = 0;
if (--idata->internal_file->fp_refcount < 0) {
idata->internal_file->fp_refcount = 0;
}
- if (idata->fp && idata->fp != idata->internal_file->fp) {
+ if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
php_stream_close(idata->fp);
}
}
phar = idata->phar;
if (idata->internal_file->fp_refcount < 2) {
- if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->internal_file->fp) {
+ if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
php_stream_close(idata->fp);
}
-#if HAVE_PHAR_ZIP
- if (idata->internal_file->is_zip) {
- if (idata->internal_file->zip) {
- zip_fclose(idata->internal_file->zip);
- idata->internal_file->zip = 0;
- }
- zip_delete(phar->zip, idata->internal_file->index);
- }
-#endif
zend_hash_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
idata->phar->refcount--;
efree(idata);
}
/* }}} */
-/**
- * Create a new dummy file slot within a writeable phar for a newly created file
- */
-phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC) /* {{{ */
-{
- phar_archive_data *phar;
- phar_entry_info *entry, etemp;
- phar_entry_data *ret;
- const char *pcr_error;
- char is_dir = path[path_len - 1] == '/';
-
- if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
- return NULL;
- }
-
- if (FAILURE == phar_get_entry_data(&ret, fname, fname_len, path, path_len, mode, allow_dir, error TSRMLS_CC)) {
- return NULL;
- } else if (ret) {
- return ret;
- }
-
- if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
- if (error) {
- spprintf(error, 0, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
- }
- return NULL;
- }
-
- /* create a new phar data holder */
- ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
-
- /* create an entry, this is a new file */
- memset(&etemp, 0, sizeof(phar_entry_info));
- etemp.filename_len = path_len;
- etemp.fp = php_stream_fopen_tmpfile();
- if (!etemp.fp) {
- if (error) {
- spprintf(error, 0, "phar error: unable to create temporary file");
- }
- return NULL;
- }
- if (is_dir) {
- etemp.fp_refcount = 1;
- etemp.is_dir = 1;
- etemp.flags = PHAR_ENT_PERM_DEF_DIR;
- etemp.old_flags = PHAR_ENT_PERM_DEF_DIR;
- etemp.filename_len--; /* strip trailing / */
- path_len--;
- } else {
- etemp.fp_refcount = 1;
- etemp.flags = PHAR_ENT_PERM_DEF_FILE;
- etemp.old_flags = PHAR_ENT_PERM_DEF_FILE;
- }
- etemp.is_modified = 1;
- etemp.timestamp = time(0);
- etemp.offset_within_phar = (phar->is_zip ? 0 : -1);
- etemp.is_crc_checked = 1;
- etemp.phar = phar;
-#if HAVE_PHAR_ZIP
- if (phar->is_zip) {
- etemp.is_zip = 1;
- /* prevent attempts to check the CRC */
- etemp.is_crc_checked = 1;
- etemp.index = -1;
- }
-#endif
- etemp.filename = estrndup(path, path_len);
- if (phar->is_tar) {
- etemp.is_tar = phar->is_tar;
- etemp.tar_type = TAR_FILE;
- }
- zend_hash_add(&phar->manifest, etemp.filename, path_len, (void*)&etemp, sizeof(phar_entry_info), (void **) &entry);
-
- if (!entry) {
- return NULL;
- }
-
- phar->refcount++;
- ret->phar = phar;
- ret->fp = entry->fp;
- ret->position = 0;
- ret->for_write = 1;
- ret->is_zip = entry->is_zip;
- ret->is_tar = entry->is_tar;
- ret->internal_file = entry;
- return ret;
-}
-/* }}} */
-
#define MAPPHAR_ALLOC_FAIL(msg) \
php_stream_close(fp);\
if (error) {\
/* set up our manifest */
zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
zend_get_hash_value, destroy_phar_manifest_entry, 0);
- offset = 0;
+ offset = halt_offset + manifest_len + 4;
+ memset(&entry, 0, sizeof(phar_entry_info));
+ entry.phar = mydata;
+ entry.fp_type = PHAR_FP;
+
for (manifest_index = 0; manifest_index < manifest_count; manifest_index++) {
if (buffer + 4 > endbuffer) {
MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)")
}
- memset(&entry, 0, sizeof(phar_entry_info));
PHAR_GET_32(buffer, entry.filename_len);
if (entry.filename_len == 0) {
MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\"");
}
if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') {
entry.is_dir = 1;
- entry.filename_len--;
- entry.flags |= PHAR_ENT_PERM_DEF_DIR;
} else {
entry.is_dir = 0;
}
entry.filename = estrndup(buffer, entry.filename_len);
- buffer += entry.filename_len + (entry.is_dir ? 1 : 0);
+ buffer += entry.filename_len;
PHAR_GET_32(buffer, entry.uncompressed_filesize);
PHAR_GET_32(buffer, entry.timestamp);
- if (offset == 0) {
+ if (offset == halt_offset + manifest_len + 4) {
mydata->min_timestamp = entry.timestamp;
mydata->max_timestamp = entry.timestamp;
} else {
PHAR_GET_32(buffer, entry.compressed_filesize);
PHAR_GET_32(buffer, entry.crc32);
PHAR_GET_32(buffer, entry.flags);
+ if (entry.is_dir) {
+ entry.filename_len--;
+ entry.flags |= PHAR_ENT_PERM_DEF_DIR;
+ }
if (phar_parse_metadata(&buffer, &entry.metadata, 0 TSRMLS_CC) == FAILURE) {
efree(entry.filename);
MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
}
- entry.offset_within_phar = offset;
+ entry.offset = entry.offset_abs = offset;
offset += entry.compressed_filesize;
switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
case PHAR_ENT_COMPRESSED_GZ:
manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK);
/* if signature matched, no need to check CRC32 for each file */
entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0);
- entry.fp = NULL;
- entry.phar = mydata;
zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
}
phar_archive_data *mydata;
int register_alias;
php_stream *fp;
+ char *actual = NULL;
if (!pphar) {
pphar = &mydata;
}
/* first open readonly so it won't be created if not present */
- fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
+ fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual);
+ if (actual) {
+ fname = actual;
+ fname_len = strlen(actual);
+ }
if (fp) {
if (phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC) == SUCCESS) {
if (!PHAR_G(readonly)) {
(*pphar)->is_writeable = 1;
}
+ if (actual) {
+ efree(actual);
+ }
return SUCCESS;
} else {
/* file exists, but is either corrupt or not a phar archive */
php_stream_close(fp);
+ if (actual) {
+ efree(actual);
+ }
return FAILURE;
}
- php_stream_close(fp);
+ }
+ if (actual) {
+ efree(actual);
}
/* set up our manifest */
mydata = ecalloc(sizeof(phar_archive_data), 1);
- /* re-open for writing */
- fp = php_stream_open_wrapper(fname, "r+b", IGNORE_URL|STREAM_MUST_SEEK|0, &mydata->fname);
+ /* re-open for writing (we only reach here if the file does not exist) */
+ fp = php_stream_open_wrapper(fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|0, &mydata->fname);
+ if (!fp) {
+ if (options & REPORT_ERRORS) {
+ if (error) {
+ spprintf(error, 0, "creating archive \"%s\" failed", fname);
+ }
+ }
+ return FAILURE;
+ }
if (mydata->fname) {
fname = mydata->fname;
#ifdef PHP_WIN32
int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
{
php_stream *fp;
+ char *actual;
+ int ret;
if (error) {
*error = NULL;
return FAILURE;
}
- fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, NULL);
-
+ fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
if (!fp) {
if (options & REPORT_ERRORS) {
if (error) {
spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
}
}
+ if (actual) {
+ efree(actual);
+ }
return FAILURE;
}
- return phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC);
+ if (actual) {
+ fname = actual;
+ fname_len = strlen(actual);
+ }
+
+ ret = phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC);
+ if (actual) {
+ efree(actual);
+ }
+ return ret;
}
/* }}}*/
continue;
}
if (!memcmp(pos, zip_magic, 4)) {
- php_stream_close(fp);
- return phar_open_zipfile(fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC);
+ php_stream_seek(fp, 0, SEEK_END);
+ return phar_open_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC);
}
if (got > 512) {
if (phar_is_tar(pos)) {
/**
* Validate the CRC32 of a file opened from within the phar
*/
-int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC) /* {{{ */
+int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32, char **error TSRMLS_DC) /* {{{ */
{
php_uint32 crc = ~0;
int len = idata->internal_file->uncompressed_filesize;
php_stream *fp = idata->fp;
-
- php_stream_seek(fp, 0 + idata->zero, SEEK_SET);
- while (len--) {
- CRC32(crc, php_stream_getc(fp));
- }
- php_stream_seek(fp, 0 + idata->zero, SEEK_SET);
- if (~crc == crc32) {
- return SUCCESS;
- } else {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, idata->internal_file->filename);
- return FAILURE;
- }
-}
-/* }}} */
-
-/**
- * Determine which stream compression filter (if any) we need to read this file
- */
-static char * phar_compress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
-{
- switch (entry->flags & PHAR_ENT_COMPRESSION_MASK) {
- case PHAR_ENT_COMPRESSED_GZ:
- return "zlib.deflate";
- case PHAR_ENT_COMPRESSED_BZ2:
- return "bzip2.compress";
- default:
- return return_unknown ? "unknown" : NULL;
- }
-}
-/* }}} */
-
-/**
- * Determine which stream decompression filter (if any) we need to read this file
- */
-static char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
-{
- php_uint32 flags;
-
- if (entry->is_modified) {
- flags = entry->old_flags;
- } else {
- flags = entry->flags;
- }
- switch (flags & PHAR_ENT_COMPRESSION_MASK) {
- case PHAR_ENT_COMPRESSED_GZ:
- return "zlib.inflate";
- case PHAR_ENT_COMPRESSED_BZ2:
- return "bzip2.decompress";
- default:
- return return_unknown ? "unknown" : NULL;
- }
-}
-/* }}} */
-
-/**
- * helper function to open an internal file's fp just-in-time
- */
-phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, php_stream *fp,
- char **error, int for_write TSRMLS_DC)
-{
- php_uint32 offset, flags;
- php_stream_filter *filter/*, *consumed */;
- char *filter_name;
- char *buffer;
+ phar_entry_info *entry = idata->internal_file;
if (error) {
*error = NULL;
}
-#if HAVE_PHAR_ZIP
if (entry->is_zip) {
- char readbuf[8192];
- int got;
- if (entry->fp) {
- return entry;
- }
- if (!entry->zip) {
- if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
- char *filter_name;
- php_stream_filter *filter;
- /* we have to decompress this by hand */
-
- if (!phar_has_bz2) {
- if (error) {
- spprintf(error, 4096, "phar error, cannot decompress bzip2-compressed entry");
- }
- return NULL;
- }
-
- /* first, read into a temp stream */
- fp = php_stream_temp_new();
- entry->zip = zip_fopen_index(phar->zip, entry->index, ZIP_FL_COMPRESSED);
- if (!entry->zip) {
- if (error) {
- spprintf(error, 4096, "phar error: %s", zip_strerror(phar->zip));
- }
- zip_error_clear(phar->zip);
- return NULL;
- }
- do {
- size_t check;
- got = zip_fread(entry->zip, (void *)readbuf, 8192);
- if (-1 == got) break;
- check = got;
- if (check != php_stream_write(fp, readbuf, got)) {
- if (error) {
- spprintf(error, 4096, "phar error: could not copy full zip file contents of entry \"%s\"", entry->filename);
- }
- php_stream_close(fp);
- entry->fp = NULL;
- zip_fclose(entry->zip);
- entry->zip = NULL;
- return NULL;
- }
- } while (got == 8192);
- zip_fclose(entry->zip);
- entry->zip = NULL;
- php_stream_seek(fp, 0, SEEK_SET);
-
- /* now use a decompression filter to inflate into our temp file */
- if ((filter_name = phar_decompress_filter(entry, 0)) != NULL) {
- filter = php_stream_filter_create(filter_name, NULL, php_stream_is_persistent(fp) TSRMLS_CC);
- } else {
- filter = NULL;
- }
- if (!filter) {
- spprintf(error, 0, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", entry->phar->fname, phar_decompress_filter(entry, 1), entry->filename);
- return NULL;
- }
+ /* verify local file header */
+ phar_zip_file_header local;
- /* now we can safely use proper decompression */
- entry->fp = php_stream_temp_new();
- php_stream_filter_append(&entry->fp->writefilters, filter);
- if (php_stream_copy_to_stream(fp, entry->fp, entry->compressed_filesize) != entry->compressed_filesize || php_stream_tell(entry->fp) != (off_t) entry->uncompressed_filesize) {
- spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
- php_stream_filter_remove(filter, 1 TSRMLS_CC);
- return NULL;
- }
- php_stream_filter_flush(filter, 1);
- php_stream_filter_remove(filter, 1 TSRMLS_CC);
- php_stream_close(fp);
- return entry;
- } else {
- /* uncompressed or zlib-compressed */
- entry->zip = zip_fopen_index(phar->zip, entry->index, 0);
- if (!entry->zip) {
- if (error) {
- spprintf(error, 4096, "phar error: %s", zip_strerror(phar->zip));
- }
- zip_error_clear(phar->zip);
- return NULL;
- }
- }
+ if (SUCCESS != phar_open_archive_fp(idata->phar TSRMLS_CC)) {
+ spprintf(error, 0, "phar error: unable to open zip-based phar archive \"%s\" to verify local file header for file \"%s\"", idata->phar->fname, entry->filename);
+ return FAILURE;
}
+ php_stream_seek(idata->phar->fp, entry->header_offset, SEEK_SET);
- /* load contents of zip file to temp stream */
- entry->fp = php_stream_temp_new();
- do {
- size_t check;
- got = zip_fread(entry->zip, (void *)readbuf, 8192);
- if (-1 == got) break;
- check = got;
- if (check != php_stream_write(entry->fp, readbuf, got)) {
- if (error) {
- spprintf(error, 4096, "phar error: could not copy full zip file contents of entry \"%s\"", entry->filename);
- }
- php_stream_close(entry->fp);
- entry->fp = NULL;
- zip_fclose(entry->zip);
- entry->zip = NULL;
- return NULL;
- }
- } while (got == 8192);
- zip_fclose(entry->zip);
- entry->zip = NULL;
- return entry;
- }
-#endif /* #if HAVE_PHAR_ZIP */
- /* seek to start of internal file and read it */
- offset = phar->internal_file_start + entry->offset_within_phar;
- if (-1 == php_stream_seek(fp, offset, SEEK_SET)) {
- spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (cannot seek to start of file \"%s\" at offset \"%d\")",
- phar->fname, entry->filename, offset);
- return NULL;
- }
-
- if (entry->is_modified) {
- flags = entry->old_flags;
- } else {
- flags = entry->flags;
- }
+ if (sizeof(local) != php_stream_read(idata->phar->fp, (char *) &local, sizeof(local))) {
- if ((flags & PHAR_ENT_COMPRESSION_MASK) != 0) {
- if ((filter_name = phar_decompress_filter(entry, 0)) != NULL) {
- filter = php_stream_filter_create(phar_decompress_filter(entry, 0), NULL, php_stream_is_persistent(fp) TSRMLS_CC);
- } else {
- filter = NULL;
- }
- if (!filter) {
- spprintf(error, 0, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", phar->fname, phar_decompress_filter(entry, 1), entry->filename);
- return NULL;
+ spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, entry->filename);
+ return FAILURE;
}
- /* now we can safely use proper decompression */
- entry->old_flags = entry->flags;
- buffer = (char *) emalloc(8192);
-
- entry->fp = php_stream_temp_new();
- php_stream_filter_append(&entry->fp->writefilters, filter);
- if (php_stream_copy_to_stream(fp, entry->fp, entry->compressed_filesize) != entry->compressed_filesize || php_stream_tell(entry->fp) != (off_t) entry->uncompressed_filesize) {
- efree(buffer);
- spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
- php_stream_filter_remove(filter, 1 TSRMLS_CC);
- return NULL;
+ /* fix up for big-endian systems */
+ /* verify local header if not yet verified */
+ if (entry->filename_len != local.filename_len || entry->crc32 != local.crc32 || entry->uncompressed_filesize != local.uncompsize || entry->compressed_filesize != local.compsize) {
+ spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (local head of file \"%s\" does not match central directory)", idata->phar->fname, entry->filename);
+ return FAILURE;
}
- efree(buffer);
- php_stream_filter_flush(filter, 1);
- php_stream_filter_remove(filter, 1 TSRMLS_CC);
- php_stream_seek(fp, offset + entry->compressed_filesize, SEEK_SET);
- } else { /* from here is for non-compressed */
- if (!for_write && !entry->is_modified) {
- /* recycle the phar fp */
- entry->fp = phar->fp;
- return entry;
- }
- /* bypass to temp stream */
- entry->fp = php_stream_temp_new();
- if (php_stream_copy_to_stream(fp, entry->fp, entry->uncompressed_filesize) != entry->uncompressed_filesize) {
- spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
- return NULL;
+ if (-1 == php_stream_seek(idata->phar->fp, local.filename_len + local.extra_len, SEEK_CUR)) {
+ spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot seek to start of file data for file \"%s\")", idata->phar->fname, entry->filename);
+ return FAILURE;
}
}
- return entry;
+ php_stream_seek(fp, idata->zero, SEEK_SET);
+ while (len--) {
+ CRC32(crc, php_stream_getc(fp));
+ }
+ php_stream_seek(fp, idata->zero, SEEK_SET);
+ if (~crc == crc32) {
+ entry->is_crc_checked = 1;
+ return SUCCESS;
+ } else {
+ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
+ return FAILURE;
+ }
}
+/* }}} */
static inline void phar_set_32(char *buffer, int var) /* {{{ */
{
char *newstub;
phar_entry_info *entry, *newentry;
int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
- char *buf, *pos, has_dirs = 0;
+ char *pos, has_dirs = 0;
char manifest[18], entry_buffer[24];
off_t manifest_ftell;
long offset;
return EOF;
}
-#if HAVE_PHAR_ZIP
if (phar->is_zip) {
return phar_zip_flush(phar, user_stub, len, error TSRMLS_CC);
}
-#endif
if (phar->is_tar) {
return phar_tar_flush(phar, user_stub, len, error TSRMLS_CC);
}
}
new_manifest_count = 0;
offset = 0;
- buf = (char *) emalloc(8192);
for (zend_hash_internal_pointer_reset(&phar->manifest);
zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
zend_hash_move_forward(&phar->manifest)) {
php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
} else {
+ if (entry->metadata_str.c) {
+ smart_str_free(&entry->metadata_str);
+ }
entry->metadata_str.c = 0;
entry->metadata_str.len = 0;
}
if (oldfile && !entry->is_modified) {
continue;
}
- if (!entry->fp || (entry->is_modified && entry->fp == phar->fp)) {
+ if (!phar_get_efp(entry)) {
/* re-open internal file pointer just-in-time */
newentry = phar_open_jit(phar, entry, oldfile, error, 0 TSRMLS_CC);
if (!newentry) {
}
entry = newentry;
}
- file = entry->fp;
- if (file == phar->fp) {
- if (-1 == php_stream_seek(file, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
- }
- return EOF;
+ file = phar_get_efp(entry);
+ if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+ if (closeoldfile) {
+ php_stream_close(oldfile);
}
- } else {
- php_stream_rewind(file);
+ php_stream_close(newfile);
+ if (error) {
+ spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
+ }
+ return EOF;
}
newcrc32 = ~0;
mytime = entry->uncompressed_filesize;
spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
}
}
- efree(buf);
return EOF;
}
php_stream_close(oldfile);
}
php_stream_close(newfile);
- efree(buf);
return EOF;
}
php_stream_flush(file);
- if (file == phar->fp) {
- if (-1 == php_stream_seek(file, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
- }
- return EOF;
+ if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+ if (closeoldfile) {
+ php_stream_close(oldfile);
}
- } else {
- php_stream_rewind(file);
+ php_stream_close(newfile);
+ if (error) {
+ spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
+ }
+ return EOF;
+ }
+ php_stream_filter_append((&entry->cfp->writefilters), filter);
+ if (entry->uncompressed_filesize != php_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize)) {
+ if (closeoldfile) {
+ php_stream_close(oldfile);
+ }
+ php_stream_close(newfile);
+ if (error) {
+ spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
+ }
+ return EOF;
}
- php_stream_filter_append((&file->readfilters), filter);
- entry->compressed_filesize = (php_uint32) php_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize+8192);
php_stream_filter_flush(filter, 1);
- entry->compressed_filesize += (php_uint32) php_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize+8192);
+ php_stream_flush(entry->cfp);
php_stream_filter_remove(filter, 1 TSRMLS_CC);
+ php_stream_seek(entry->cfp, 0, SEEK_END);
+ entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
/* generate crc on compressed file */
php_stream_rewind(entry->cfp);
entry->old_flags = entry->flags;
entry->is_modified = 1;
global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
}
- efree(buf);
global_flags |= PHAR_HDR_SIGNATURE;
/* write out manifest pre-header */
}
/* now copy the actual file data to the new phar */
- offset = 0;
+ offset = php_stream_tell(newfile);
for (zend_hash_internal_pointer_reset(&phar->manifest);
zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
zend_hash_move_forward(&phar->manifest)) {
if (entry->cfp) {
file = entry->cfp;
php_stream_rewind(file);
- } else if (entry->fp && (entry->is_modified || !oldfile)) {
- file = entry->fp;
- if (file == phar->fp) {
- if (-1 == php_stream_seek(file, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
- }
- return EOF;
- }
- } else {
- php_stream_rewind(file);
- }
} else {
- if (!oldfile) {
+ file = phar_get_efp(entry);
+ if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
if (closeoldfile) {
php_stream_close(oldfile);
}
}
return EOF;
}
- if (-1 == php_stream_seek(oldfile, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) {
- if (closeoldfile) {
- php_stream_close(oldfile);
- }
- php_stream_close(newfile);
- if (error) {
- spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
- }
- return EOF;
+ }
+ if (!file) {
+ if (closeoldfile) {
+ php_stream_close(oldfile);
}
- file = oldfile;
+ php_stream_close(newfile);
+ if (error) {
+ spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
+ }
+ return EOF;
}
/* this will have changed for all files that have either
changed compression or been modified */
- entry->offset_within_phar = offset;
+ entry->offset = entry->offset_abs = offset;
offset += entry->compressed_filesize;
wrote = php_stream_copy_to_stream(file, newfile, entry->compressed_filesize);
if (entry->compressed_filesize != wrote) {
entry->is_modified = 0;
if (entry->cfp) {
php_stream_close(entry->cfp);
- entry->cfp = 0;
+ entry->cfp = NULL;
}
- if (entry->fp && entry->fp_refcount == 0) {
- if (entry->fp != phar->fp) {
+ if (entry->fp_type == PHAR_MOD) {
+ /* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed
+ when the phar_entry_data is phar_entry_delref'ed */
+ if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
php_stream_close(entry->fp);
}
- entry->fp = 0;
+ entry->fp = NULL;
+ entry->fp_type = PHAR_FP;
+ } else if (entry->fp_type == PHAR_UFP) {
+ entry->fp_type = PHAR_FP;
}
}
if (phar->fp) {
php_stream_close(phar->fp);
}
+ if (phar->ufp) {
+ php_stream_close(phar->ufp);
+ phar->ufp = NULL;
+ }
if (closeoldfile) {
php_stream_close(oldfile);
}
phar->internal_file_start = halt_offset + manifest_len + 4;
+ phar->halt_offset = halt_offset;
phar->is_brandnew = 0;
php_stream_rewind(newfile);
PHP_MINIT_FUNCTION(phar) /* {{{ */
{
- zend_module_entry *test;
-
ZEND_INIT_MODULE_GLOBALS(phar, php_phar_init_globals_module, NULL);
REGISTER_INI_ENTRIES();
phar_has_bz2 = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
phar_has_zlib = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
- if (SUCCESS == zend_hash_find(&module_registry, "zip", sizeof("zip"), (void **) &test)) {
- if (php_version_compare((const char *) test->version, "1.8.11") != -1) {
- phar_has_zip = 1;
- } else {
- phar_has_zip = 0;
- }
- phar_zip_ver = (char *) test->version;
- } else {
- phar_has_zip = 0;
- phar_zip_ver = NULL;
- }
phar_orig_compile_file = zend_compile_file;
zend_compile_file = phar_compile_file;
php_info_print_table_row(2, "CVS revision", "$Revision$");
php_info_print_table_row(2, "Phar-based phar archives", "enabled");
php_info_print_table_row(2, "Tar-based phar archives", "enabled");
-#if HAVE_PHAR_ZIP
- if (phar_has_zip) {
- php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
- } else {
- if (phar_zip_ver) {
- char *tmp;
- spprintf(&tmp, 0, "disabled (pecl/zip version %s installed, we need zip >= 1.8.11)", phar_zip_ver);
- php_info_print_table_row(2, "ZIP-based phar archives", tmp);
- efree(tmp);
- } else {
- php_info_print_table_row(2, "ZIP-based phar archives", "disabled (install pecl/zip)");
- }
- }
-#else
- php_info_print_table_row(2, "ZIP-based phar archives", "unavailable");
-#endif
+ php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
if (phar_has_zlib) {
php_info_print_table_row(2, "gzip compression", "enabled");
} else {
/* {{{ phar_module_entry
*/
static zend_module_dep phar_deps[] = {
-#if HAVE_PHAR_ZIP
- ZEND_MOD_OPTIONAL("zip")
-#endif
ZEND_MOD_OPTIONAL("zlib")
ZEND_MOD_OPTIONAL("bz2")
#if HAVE_SPL
#include <time.h>
#include "php.h"
-#if HAVE_PHAR_ZIP
-#include "ext/zip/lib/zip.h"
-#include "ext/zip/lib/zipint.h"
-#endif
#include "tar.h"
#include "php_ini.h"
#include "zend_constants.h"
int phar_has_bz2;
int phar_has_zlib;
-int phar_has_zip;
-char *phar_zip_ver;
#ifdef ZTS
# include "TSRM.h"
# define php_uint16 uint16_t
# endif
#endif
+#include "pharzip.h"
#if HAVE_SPL
typedef union _phar_archive_object phar_archive_object;
typedef union _phar_entry_object phar_entry_object;
#endif
+/*
+ * used in phar_entry_info->fp_type to
+ */
+enum phar_fp_type {
+ /* regular file pointer phar_archive_data->fp */
+ PHAR_FP,
+ /* uncompressed file pointer phar_archive_data->uncompressed_fp */
+ PHAR_UFP,
+ /* modified file pointer phar_entry_info->fp */
+ PHAR_MOD
+};
+
typedef struct _phar_archive_data phar_archive_data;
/* entry for one file in a phar file */
typedef struct _phar_entry_info {
zval *metadata;
php_uint32 filename_len;
char *filename;
- long offset_within_phar;
+ enum phar_fp_type fp_type;
+ /* offset within original phar file of the file contents */
+ long offset_abs;
+ /* offset within fp of the file contents */
+ long offset;
+ /* offset within original phar file of the file header (for zip-based/tar-based) */
+ long header_offset;
php_stream *fp;
php_stream *cfp;
int fp_refcount;
char tar_type;
/* zip-based phar file stuff */
int is_zip:1;
-#if HAVE_PHAR_ZIP
- int index;
- struct zip_file *zip;
-#endif
} phar_entry_info;
/* information about a phar file (the archive itself) */
php_uint32 min_timestamp;
php_uint32 max_timestamp;
php_stream *fp;
+ /* decompressed file contents are stored here */
+ php_stream *ufp;
int refcount;
php_uint32 sig_flags;
int sig_len;
int is_zip:1;
/* tar-based phar variables */
int is_tar:1;
-#if HAVE_PHAR_ZIP
- struct zip *zip;
-#endif
};
#define PHAR_MIME_PHP '\0'
/* stream access data for one file entry in a phar file */
typedef struct _phar_entry_data {
phar_archive_data *phar;
- /* stream position proxy, allows multiple open streams referring to the same fp */
php_stream *fp;
+ /* stream position proxy, allows multiple open streams referring to the same fp */
off_t position;
/* for copies of the phar fp, defines where 0 is */
off_t zero;
void phar_object_init(TSRMLS_D);
+int phar_open_entry_file(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC);
int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
int phar_open_compiled_file(char *alias, int alias_len, char **error TSRMLS_DC);
int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC);
int phar_open_loaded(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
+
+/* utility functions */
char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC);
+char * phar_decompress_filter(phar_entry_info * entry, int return_unknown);
+char * phar_compress_filter(phar_entry_info * entry, int return_unknown);
char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC);
phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, php_stream *fp,
char **error, int for_write TSRMLS_DC);
int phar_parse_metadata(char **buffer, zval **metadata, int is_zip TSRMLS_DC);
void destroy_phar_manifest_entry(void *pDest);
+int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position TSRMLS_DC);
+php_stream *phar_get_efp(phar_entry_info *entry);
+int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error TSRMLS_DC);
+int phar_open_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC);
+int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC);
+int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC);
+int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC);
/* tar functions in tar.c */
int phar_is_tar(char *buf);
int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, char **error TSRMLS_DC);
/* zip functions in zip.c */
-int phar_open_zipfile(char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC);
+int phar_open_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC);
int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
-#if HAVE_PHAR_ZIP
int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC);
-#endif
#ifdef PHAR_MAIN
static int phar_open_fp(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
}
/* prepare to output */
- if (!phar->fp) {
+ if (!phar_get_efp(phar->internal_file)) {
char *error;
if (!phar_open_jit(phar->phar, phar->internal_file, phar->phar->fp, &error, 0 TSRMLS_CC)) {
if (error) {
}
return -1;
}
- phar->fp = phar->internal_file->fp;
- if (phar->internal_file->fp == phar->phar->fp) {
- phar->zero = phar->internal_file->offset_within_phar;
- if (!phar->is_tar && !phar->is_zip) {
- phar->zero += phar->phar->internal_file_start;
- }
- }
+ phar->fp = phar_get_efp(phar->internal_file);
+ phar->zero = phar->internal_file->offset;
}
- php_stream_seek(phar->fp, phar->zero, SEEK_SET);
+ phar_seek_efp(phar->internal_file, 0, SEEK_SET, 0 TSRMLS_CC);
do {
- if (!phar->zero) {
- got = php_stream_read(phar->fp, buf, 8192);
- PHPWRITE(buf, got);
- if (phar->fp->eof) {
- break;
- }
- } else {
- got = php_stream_read(phar->fp, buf, MIN(8192, phar->internal_file->uncompressed_filesize - phar->position));
- PHPWRITE(buf, got);
- phar->position = php_stream_tell(phar->fp) - phar->zero;
- if (phar->position == (off_t) phar->internal_file->uncompressed_filesize) {
- break;
- }
+ got = php_stream_read(phar->fp, buf, MIN(8192, phar->internal_file->uncompressed_filesize - phar->position));
+ PHPWRITE(buf, got);
+ phar->position = php_stream_tell(phar->fp) - phar->zero;
+ if (phar->position == (off_t) phar->internal_file->uncompressed_filesize) {
+ break;
}
} while (1);
static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp TSRMLS_DC) /* {{{ */
{
- /* if not available, open the file for reading */
- if (!entry->fp) {
- char *error;
- if (!phar_open_jit(entry->phar, entry, entry->phar->fp, &error, 0 TSRMLS_CC)) {
- if (error) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents: %s", entry->phar->fname, entry->filename, error);
- efree(error);
- } else {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents", entry->phar->fname, entry->filename);
- }
- return FAILURE;
+ char *error;
+ off_t offset;
+
+ if (FAILURE == phar_open_entry_fp(entry, &error TSRMLS_CC)) {
+ if (error) {
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+ "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents: %s", entry->phar->fname, entry->filename, error);
+ efree(error);
+ } else {
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+ "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents", entry->phar->fname, entry->filename);
}
+ return FAILURE;
}
/* copy old contents in entirety */
- php_stream_seek(entry->fp, entry->phar->internal_file_start + entry->offset_within_phar, SEEK_SET);
- if (entry->uncompressed_filesize != php_stream_copy_to_stream(entry->fp, fp, entry->uncompressed_filesize)) {
+ phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC);
+ offset = php_stream_tell(fp);
+ if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry), fp, entry->uncompressed_filesize)) {
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
"Cannot convert phar archive \"%s\", unable to copy entry \"%s\" contents", entry->phar->fname, entry->filename);
return FAILURE;
}
- if (entry->fp != entry->phar->fp) {
- php_stream_close(entry->fp);
+ if (entry->fp_type == PHAR_MOD) {
+ /* save for potential restore on error */
+ entry->cfp = entry->fp;
entry->fp = NULL;
}
+ /* set new location of file contents */
+ entry->fp_type = PHAR_FP;
+ entry->offset = offset;
return SUCCESS;
}
/* }}} */
+static int phar_restore_apply(void *data TSRMLS_DC) /* {{{ */
+{
+ phar_entry_info *entry = (phar_entry_info *)data;
+
+ if (entry->cfp) {
+ entry->fp_type = PHAR_MOD;
+ entry->fp = entry->cfp;
+ entry->cfp = NULL;
+ entry->offset = 0;
+ } else {
+ entry->fp_type = PHAR_FP;
+ entry->fp = NULL;
+ entry->offset = entry->offset_abs;
+ }
+ return ZEND_HASH_APPLY_KEEP;
+}
+
static void phar_convert_to_other(phar_archive_data *source, int convert, php_uint32 flags TSRMLS_DC) /* {{{ */
{
phar_archive_data phar = {0};
- long offset = 0;
- char *error, *opened_path = NULL;
-#if HAVE_PHAR_ZIP
- int fd, ziperror;
-#endif
+ char *error;
+ zval *userz;
+ long tmp;
+ php_stream *fp;
+ phar_entry_info *entry, newentry;
/* set whole-archive compression from parameter */
phar.flags = flags;
break;
case 2 :
phar.is_zip = 1;
-#if HAVE_PHAR_ZIP
- if (!((fd = php_open_temporary_fd(NULL, "pharzip", &opened_path TSRMLS_CC)) >= 0)) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "Cannot convert phar archive \"%s\", unable to open temporary zip archive", source->fname);
- return;
- }
- close(fd);
- unlink(opened_path);
- phar.zip = zip_open(opened_path, ZIP_CREATE, &ziperror);
- if (!phar.zip) {
- /* now for the stupid hoops libzip forces... */
- char *tmp;
- int tmp_len;
-
- efree(opened_path);
- tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
- if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "Cannot convert phar archive \"%s\", unable to open temporary zip archive", source->fname);
- } else {
- if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
- efree(tmp);
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "Cannot convert phar archive \"%s\", unable to open temporary zip archive", source->fname);
- } else {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "Cannot convert phar archive \"%s\", unable to open temporary zip archive: %s", source->fname, tmp);
- efree(tmp);
- }
- }
- return;
- }
-#endif
break;
}
phar.fname = source->fname;
/* first copy each file's uncompressed contents to a temporary file and set per-file flags */
for (zend_hash_internal_pointer_reset(&source->manifest); SUCCESS == zend_hash_has_more_elements(&source->manifest); zend_hash_move_forward(&source->manifest)) {
- phar_entry_info *entry, newentry;
+
if (FAILURE == zend_hash_get_current_data(&source->manifest, (void **) &entry)) {
zend_hash_destroy(&(phar.manifest));
php_stream_close(phar.fp);
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
"Cannot convert phar archive \"%s\"", source->fname);
- if (opened_path) {
- efree(opened_path);
+ zend_hash_apply(&source->manifest, phar_restore_apply TSRMLS_CC);
+ return;
+ }
+ if (!convert) {
+ /* converting to Phar */
+ if (entry->filename_len == sizeof(".phar/stub.php")-1 && !strncmp(entry->filename, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
+ /* do not copy stub file */
+ continue;
}
-#if HAVE_PHAR_ZIP
- if (phar.is_zip) {
- _zip_free(phar.zip);
+ if (entry->filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry->filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
+ /* do not copy alias file */
+ continue;
}
-#endif
- return;
}
if (entry->fp_refcount > 0) {
zend_hash_destroy(&(phar.manifest));
php_stream_close(phar.fp);
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
"Cannot convert phar archive \"%s\", open file pointers for entry \"%s\"", source->fname, entry->filename);
-#if HAVE_PHAR_ZIP
- if (phar.is_zip) {
- _zip_free(phar.zip);
- }
-#endif
- if (opened_path) {
- efree(opened_path);
- }
+ zend_hash_apply(&source->manifest, phar_restore_apply TSRMLS_CC);
return;
}
newentry = *entry;
- if (FAILURE == phar_copy_file_contents(entry, phar.fp TSRMLS_CC)) {
+ if (FAILURE == phar_copy_file_contents(&newentry, phar.fp TSRMLS_CC)) {
zend_hash_destroy(&(phar.manifest));
php_stream_close(phar.fp);
/* exception already thrown */
-#if HAVE_PHAR_ZIP
- if (phar.is_zip) {
- _zip_free(phar.zip);
- }
-#endif
- if (opened_path) {
- efree(opened_path);
- }
+ zend_hash_apply(&source->manifest, phar_restore_apply TSRMLS_CC);
return;
}
newentry.filename = estrndup(newentry.filename, newentry.filename_len);
if (newentry.metadata) {
SEPARATE_ZVAL(&(newentry.metadata));
}
- newentry.offset_within_phar = offset;
- offset += newentry.uncompressed_filesize;
newentry.is_zip = phar.is_zip;
newentry.is_tar = phar.is_tar;
if (newentry.is_tar) {
newentry.tar_type = (entry->is_dir ? TAR_DIR : TAR_FILE);
}
- newentry.fp = phar.fp;
-#if HAVE_PHAR_ZIP
- newentry.index = -1;
-#endif
newentry.is_modified = 1;
newentry.phar = &phar;
newentry.old_flags = newentry.flags & ~PHAR_ENT_COMPRESSION_MASK; /* remove compression from old_flags */
}
/* next copy the stub and flush */
- if (phar.is_zip) {
- phar.fname = opened_path;
- }
-
- if (source->is_zip) {
- phar_entry_info *entry;
- long tmp = 0;
- zval *userz;
+ if (source->is_tar || source->is_zip) {
if (FAILURE == (zend_hash_find(&source->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&entry))) {
/* use default stub - the existing one in the manifest will be used if present */
phar_flush(&phar, NULL, 0, &error TSRMLS_CC);
goto finalize;
}
- if (!entry->fp && !phar_open_jit(source, entry, NULL, &error, 0 TSRMLS_CC)) {
- if (error) {
- efree(error);
- }
- php_stream_close(phar.fp);
- zend_hash_destroy(&(phar.manifest));
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "Cannot convert phar archive \"%s\": unable to retrieve stub", source->fname, error);
-#if HAVE_PHAR_ZIP
- if (phar.is_zip) {
- _zip_free(phar.zip);
- }
-#endif
- if (opened_path) {
- efree(opened_path);
- }
- return;
- }
- /* copy the other phar's stub */
- php_stream_seek(entry->fp, 0, SEEK_SET);
- ALLOC_ZVAL(userz);
- php_stream_to_zval(entry->fp, userz);
- tmp = entry->uncompressed_filesize;
- phar_flush(&phar, (char *) &userz, -tmp, &error TSRMLS_CC);
- efree(userz);
- php_stream_close(entry->fp);
- if (error) {
- zend_hash_destroy(&(phar.manifest));
- php_stream_close(phar.fp);
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "Cannot convert phar archive \"%s\": %s", source->fname, error);
- efree(error);
-#if HAVE_PHAR_ZIP
- if (phar.is_zip) {
- _zip_free(phar.zip);
- }
-#endif
- if (opened_path) {
- efree(opened_path);
- }
- return;
- }
+ fp = php_stream_open_wrapper(source->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
+ php_stream_seek(fp, entry->offset, SEEK_SET);
+ /* use this unused value to set the stub size */
+ source->internal_file_start = entry->uncompressed_filesize;
} else {
- zval *userz;
- long tmp;
-
- if (source->is_tar) {
- phar_entry_info *entry;
-
- if (FAILURE == (zend_hash_find(&source->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&entry))) {
- /* use default stub - the existing one in the manifest will be used if present */
- phar_flush(&phar, NULL, 0, &error TSRMLS_CC);
- goto finalize;
- }
- php_stream_seek(source->fp, entry->offset_within_phar, SEEK_SET);
- /* use this unused value to set the stub size */
- source->internal_file_start = entry->uncompressed_filesize;
- } else {
- php_stream_seek(source->fp, 0, SEEK_SET);
- }
- /* copy the other phar's stub */
- ALLOC_ZVAL(userz);
- php_stream_to_zval(source->fp, userz);
- tmp = source->internal_file_start;
- phar_flush(&phar, (char *) &userz, -tmp, &error TSRMLS_CC);
- efree(userz);
- if (source->is_tar) {
- source->internal_file_start = 0;
- }
+ fp = php_stream_open_wrapper(source->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
}
+ /* copy the other phar's stub */
+ ALLOC_ZVAL(userz);
+ php_stream_to_zval(fp, userz);
+ tmp = source->internal_file_start;
+ source->internal_file_start = 0;
+ phar_flush(&phar, (char *) &userz, -tmp, &error TSRMLS_CC);
+ php_stream_close(fp);
+ efree(userz);
if (error) {
zend_hash_destroy(&(phar.manifest));
"Cannot convert phar archive \"%s\": %s", source->fname, error);
efree(error);
php_stream_close(phar.fp);
-#if HAVE_PHAR_ZIP
- if (phar.is_zip) {
- _zip_free(phar.zip);
- }
-#endif
- if (opened_path) {
- efree(opened_path);
- }
return;
}
php_stream_close(phar.fp);
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
"Cannot convert phar archive \"%s\"", source->fname);
-#if HAVE_PHAR_ZIP
- if (phar.is_zip) {
- _zip_free(phar.zip);
- }
-#endif
- if (opened_path) {
- efree(opened_path);
- }
/* we can't throw an exception or bad crap will happen */
zend_error(E_ERROR, "Error: could not convert phar archive \"%s\", phar is in unstable state, shutting down", source->fname);
return;
/* a little hack to prevent destruction of data */
dtor_func_t save;
+ entry->phar = source;
save = phar.manifest.pDestructor;
phar.manifest.pDestructor = NULL;
zend_hash_add(&source->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1, (void*)entry, sizeof(phar_entry_info), NULL);
/* a little hack to prevent destruction of data */
dtor_func_t save;
+ entry->phar = source;
save = phar.manifest.pDestructor;
phar.manifest.pDestructor = NULL;
zend_hash_add(&source->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1, (void*)entry, sizeof(phar_entry_info), NULL);
}
zend_hash_destroy(&(phar.manifest));
php_stream_close(phar.fp);
-#if HAVE_PHAR_ZIP
- if (phar.is_zip) {
- _zip_free(phar.zip);
- }
-#endif
- if (opened_path) {
- efree(opened_path);
- }
/* we can't throw an exception or bad crap will happen */
zend_error(E_ERROR, "Error: could not convert phar archive \"%s\", phar is in unstable state, shutting down", source->fname);
return;
zval_dtor(mine->metadata);
efree(mine->metadata);
}
+ if (mine->fp_type == PHAR_MOD && mine->fp != source->fp && mine->fp != source->ufp) {
+ php_stream_close(mine->fp);
+ }
*mine = *entry;
mine->phar = source;
}
+ if (source->fp && source->refcount == 1) {
+ php_stream_close(source->fp);
+ }
source->fp = phar.fp;
/* don't free stuff */
phar.manifest.pDestructor = NULL;
zend_hash_destroy(&(phar.manifest));
source->is_zip = phar.is_zip;
+ if (phar.is_zip || phar.is_tar) {
+ source->internal_file_start = source->halt_offset = 0;
+ }
source->is_tar = phar.is_tar;
+ if (source->signature) {
+ efree(source->signature);
+ }
if (phar.signature) {
- if (source->signature) {
- efree(source->signature);
- }
source->signature = phar.signature;
+ } else {
+ source->signature = 0;
}
-#if HAVE_PHAR_ZIP
- if (phar.is_zip) {
- _zip_free(phar.zip);
- _zip_free(source->zip);
- if (FAILURE == php_copy_file(opened_path, source->fname TSRMLS_CC)) {
- /* we can't throw an exception or bad crap will happen */
- zend_error(E_ERROR, "Error: could not copy newly created zip to \"%s\", phar is in unstable state, shutting down", source->fname);
- }
- source->zip = zip_open(source->fname, 0, &ziperror);
- if (!source->zip) {
- /* now for the stupid hoops libzip forces... */
- char *tmp;
- int tmp_len;
-
- efree(opened_path);
- tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
- if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
- zend_error(E_ERROR, "Error: could not re-open newly created zip \"%s\", phar is in unstable state, shutting down", source->fname);
- } else {
- if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
- efree(tmp);
- zend_error(E_ERROR, "Error: could not re-open newly created zip \"%s\", phar is in unstable state, shutting down", source->fname);
- } else {
- zend_error(E_ERROR, "Error: could not re-open newly created zip \"%s\" (%s), phar is in unstable state, shutting down", source->fname, tmp);
- efree(tmp);
- }
- }
- return;
- }
- efree(opened_path);
- }
-#endif
}
/* {{{ proto bool Phar::convertToTar([int compression])
return;
}
-#if HAVE_PHAR_ZIP
- if (!phar_has_zip) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
- "Cannot convert phar archive to zip format, zip-based phar archives are disabled (enable ext/zip in php.ini)");
- return;
- }
-
- if (!zend_hash_num_elements(&phar_obj->arc.archive->manifest)) {
- int ziperror;
- /* nothing need be done specially, this has no files in it */
- phar_obj->arc.archive->is_zip = 1;
- phar_obj->arc.archive->internal_file_start = 0;
- phar_obj->arc.archive->is_modified = 1;
- phar_obj->arc.archive->zip = zip_open(phar_obj->arc.archive->fname, ZIP_CREATE, &ziperror);
- if (!phar_obj->arc.archive->zip) {
- /* now for the stupid hoops libzip forces... */
- char *tmp;
- int tmp_len;
- tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
- if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
- "Cannot convert phar archive to zip format, unable to open", phar_obj->arc.archive->fname);
- } else {
- if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
- efree(tmp);
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
- "Cannot convert phar archive to zip format, unable to open", phar_obj->arc.archive->fname);
- } else {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
- "Cannot convert phar archive to zip format, unable to open: %s", phar_obj->arc.archive->fname, tmp);
- efree(tmp);
- }
- }
- return;
- }
- RETURN_TRUE;
- }
-
if (phar_obj->arc.archive->donotflush) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
"Cannot convert phar archive to zip format, call stopBuffering() first");
}
phar_convert_to_other(phar_obj->arc.archive, 2, 0 TSRMLS_CC);
RETURN_TRUE;
-#else
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
- "Cannot convert phar archive to zip format, zip-based phar archives are unavailable");
- return;
-#endif
}
/* }}} */
char *error;
PHAR_ARCHIVE_OBJECT();
- if (phar_obj->arc.archive->is_zip) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
- "Cannot compress all files as Bzip2, not possible with zip-based phar archives");
- return;
- }
-
if (PHAR_G(readonly)) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
"Phar is readonly, cannot change compression");
const char *pcr_error;
int oldfile_len, newfile_len;
phar_entry_info *oldentry, newentry = {0}, *temp;
- php_stream *fp;
PHAR_ARCHIVE_OBJECT();
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &oldfile, &oldfile_len, &newfile, &newfile_len) == FAILURE) {
RETURN_FALSE;
}
- fp = oldentry->fp;
- if (fp && fp != phar_obj->arc.archive->fp) {
- /* new file */
- newentry.fp = php_stream_temp_new();
- fp = newentry.fp;
- php_stream_seek(oldentry->fp, 0, SEEK_SET);
- if (oldentry->uncompressed_filesize != php_stream_copy_to_stream(oldentry->fp, fp, oldentry->uncompressed_filesize)) {
- php_stream_close(fp);
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "file \"%s\" could not be copied to file \"%s\" in %s, copy failed", oldfile, newfile, phar_obj->arc.archive->fname);
- return;
- }
- } else {
- newentry.fp = NULL;
- }
-
memcpy((void *) &newentry, oldentry, sizeof(phar_entry_info));
if (newentry.metadata) {
SEPARATE_ZVAL(&(newentry.metadata));
newentry.metadata_str.c = NULL;
newentry.metadata_str.len = 0;
}
-#if HAVE_PHAR_ZIP
- if (oldentry->is_zip) {
- int zindex;
- /* for new files, start with an empty string */
- struct zip_source *s;
- if (!newentry.fp) {
- if (!phar_open_jit(phar_obj->arc.archive, oldentry, NULL, &error, 0 TSRMLS_CC)) {
- php_stream_close(fp);
- if (error) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "file \"%s\" could not be copied to file \"%s\" in %s, open of source file failed: %s", oldfile, newfile, phar_obj->arc.archive->fname, error);
- efree(error);
- } else {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "file \"%s\" could not be copied to file \"%s\" in %s, open of source file failed", oldfile, newfile, phar_obj->arc.archive->fname);
- }
- return;
- }
- fp = oldentry->fp;
- oldentry->fp = NULL;
- }
- s = zip_source_buffer(phar_obj->arc.archive->zip, (void *)"", 0, 0);
- if (-1 == (zindex = zip_add(phar_obj->arc.archive->zip, newfile, s))) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
- "file \"%s\" could not be copied to file \"%s\" in %s, creation of destination file failed: %s", oldfile, newfile, phar_obj->arc.archive->fname, zip_strerror(phar_obj->arc.archive->zip));
- zip_error_clear(phar_obj->arc.archive->zip);
- return;
- }
- newentry.index = zindex;
- newentry.zip = NULL;
- }
-#endif
- newentry.fp = fp;
newentry.filename = estrndup(newfile, newfile_len);
newentry.filename_len = newfile_len;
- newentry.is_modified = 1;
newentry.fp_refcount = 0;
+
+ if (oldentry->fp_type != PHAR_FP) {
+ if (FAILURE == phar_copy_entry_fp(oldentry, &newentry, &error TSRMLS_CC)) {
+ efree(newentry.filename);
+ php_stream_close(newentry.fp);
+ zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error);
+ efree(error);
+ return;
+ }
+ }
+
+ zend_hash_add(&oldentry->phar->manifest, newfile, newfile_len, (void*)&newentry, sizeof(phar_entry_info), NULL);
phar_obj->arc.archive->is_modified = 1;
- zend_hash_add(&phar_obj->arc.archive->manifest, newfile, newfile_len, (void*)&newentry, sizeof(phar_entry_info), NULL);
phar_flush(phar_obj->arc.archive, 0, 0, &error TSRMLS_CC);
if (error) {
}
entry->is_modified = 0;
entry->is_deleted = 1;
-#if HAVE_PHAR_ZIP
- if (entry->is_zip) {
- if (entry->zip) {
- zip_fclose(entry->zip);
- entry->zip = 0;
- }
- zip_delete(phar_obj->arc.archive->zip, entry->index);
- }
-#endif
/* we need to "flush" the stream to save the newly deleted file on disk */
phar_flush(phar_obj->arc.archive, 0, 0, &error TSRMLS_CC);
if (error) {
*/
PHP_METHOD(Phar, getStub)
{
- char *buf;
size_t len;
+ char *buf;
php_stream *fp;
+ php_stream_filter *filter = NULL;
+ phar_entry_info *stub;
PHAR_ARCHIVE_OBJECT();
-#if HAVE_PHAR_ZIP
- if (phar_obj->arc.archive->is_zip) {
- struct zip_stat zs;
- struct zip_file *zf;
- int index;
-
- if (-1 == zip_stat(phar_obj->arc.archive->zip, ".phar/stub.php", 0, &zs)) {
- zip_error_clear(phar_obj->arc.archive->zip);
- RETURN_STRINGL("", 0, 1);
- }
- index = zs.index;
- len = zs.size;
- zf = zip_fopen_index(phar_obj->arc.archive->zip, index, 0);
- if (!zf) {
- zip_error_clear(phar_obj->arc.archive->zip);
- RETURN_STRINGL("", 0, 1);
- }
- buf = safe_emalloc(len, 1, 1);
- if (len != zip_fread(zf, buf, len)) {
- zip_fclose(zf);
- efree(buf);
- zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
- "Unable to read stub");
- return;
- }
- buf[len] = '\0';
-
- RETURN_STRINGL(buf, len, 0);
- }
-#endif
- if (phar_obj->arc.archive->is_tar) {
- phar_entry_info *stub;
+ if (phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) {
if (SUCCESS == zend_hash_find(&(phar_obj->arc.archive->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
- if (phar_obj->arc.archive->fp && !phar_obj->arc.archive->is_brandnew) {
+ if (phar_obj->arc.archive->fp && !phar_obj->arc.archive->is_brandnew && !(stub->flags & PHAR_ENT_COMPRESSION_MASK)) {
fp = phar_obj->arc.archive->fp;
} else {
fp = php_stream_open_wrapper(phar_obj->arc.archive->fname, "rb", 0, NULL);
+ if (stub->flags & PHAR_ENT_COMPRESSION_MASK) {
+ char *filter_name;
+
+ if ((filter_name = phar_decompress_filter(stub, 0)) != NULL) {
+ filter = php_stream_filter_create(phar_decompress_filter(stub, 0), NULL, php_stream_is_persistent(fp) TSRMLS_CC);
+ } else {
+ filter = NULL;
+ }
+ if (!filter) {
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "phar error: unable to read stub of phar \"%s\" (cannot create %s filter)", phar_obj->arc.archive->fname, phar_decompress_filter(stub, 1));
+ return;
+ }
+ php_stream_filter_append(&fp->readfilters, filter);
+ }
}
if (!fp) {
return;
}
- php_stream_seek(fp, stub->offset_within_phar, SEEK_SET);
+ php_stream_seek(fp, stub->offset_abs, SEEK_SET);
len = stub->uncompressed_filesize;
goto carry_on;
} else {
efree(buf);
return;
}
+ if (filter) {
+ php_stream_filter_flush(filter, 1);
+ php_stream_filter_remove(filter, 1 TSRMLS_CC);
+ }
if (fp != phar_obj->arc.archive->fp) {
php_stream_close(fp);
}
zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Cannot modify permissions for file \"%s\" in phar \"%s\", write operations are prohibited", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname);
return;
}
-#if HAVE_PHAR_ZIP
- if (entry_obj->ent.entry->is_zip) {
- zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Cannot modify permissions for file \"%s\" in phar \"%s\", not supported for zip-based phars", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname);
- return;
- }
-#endif
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &perms) == FAILURE) {
return;
}
+++ /dev/null
-/*
- +----------------------------------------------------------------------+
- | phar php single-file executable PHP extension, zip implementation |
- +----------------------------------------------------------------------+
- | Copyright (c) 2005-2008 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$ */
-
-#include "phar_internal.h"
-
-#ifdef PHP_WIN32
-static inline void phar_unixify_path_separators(char *path, int path_len) /* {{{ */
-{
- char *s;
-
- /* unixify win paths */
- for (s = path; s - path < path_len; s++) {
- if (*s == '\\') {
- *s = '/';
- }
- }
-}
-/* }}} */
-#endif
-
-static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len TSRMLS_DC)
-{
- php_uint16 left = len;
- union {
- phar_zip_extra_field_header header;
- phar_zip_unix3 unix3;
- } h;
-
- do {
- if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) {
- return FAILURE;
- }
- /* clean up header for big-endian systems */
- if (h.header.tag != "\x75\x6e") {
- /* skip to next header */
- php_stream_seek(fp, h.header.size, SEEK_CUR);
- len -= h.header.size;
- continue;
- }
- /* unix3 header found */
- /* clean up header for big-endian systems */
- if (sizeof(h.unix3) != php_stream_read(fp, (char *) &h.unix3, sizeof(h.unix3))) {
- if (h.unix3.size > sizeof(h.unix3) - 4) {
- /* skip symlink filename - we may add this support in later */
- php_stream_seek(fp, h.unix3.size - sizeof(h.unix3.size), SEEK_CUR);
- }
- /* set permissions */
- entry->flags &= PHAR_ENT_COMPRESSION_MASK;
- if (entry->is_dir) {
- entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_DIR;
- } else {
- entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_FILE;
- }
- }
- } while (len);
- return SUCCESS;
-}
-
-/*
- extracted from libzip
- zip_dirent.c -- read directory entry (local or central), clean dirent
- Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
-
- This function is part of libzip, a library to manipulate ZIP archives.
- The authors can be contacted at <nih@giga.or.at>
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- 3. The names of the authors may not be used to endorse or promote
- products derived from this software without specific prior
- written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
- OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-static time_t phar_zip_d2u_time(int dtime, int ddate)
-{
- struct tm *tm, tmbuf;
- time_t now;
-
- now = time(NULL);
- tm = php_localtime_r(&now, &tmbuf);
-
- tm->tm_year = ((ddate>>9)&127) + 1980 - 1900;
- tm->tm_mon = ((ddate>>5)&15) - 1;
- tm->tm_mday = ddate&31;
-
- tm->tm_hour = (dtime>>11)&31;
- tm->tm_min = (dtime>>5)&63;
- tm->tm_sec = (dtime<<1)&62;
-
- return mktime(tm);
-}
-
-int phar_zip_parse(char *fname, int fname_len, char **error TSRMLS_DC)
-{
- php_stream *fp;
- char buf[8192], *metadata;
- phar_zip_dir_end locator;
- long size;
- size_t read;
- php_uint16 i;
- phar_archive_data *mydata = NULL;
- phar_entry_info entry = {0};
-
- fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
-
- php_stream_seek(fp, 0, SEEK_END);
- size = php_stream_tell(fp);
- if (size > sizeof(locator) + 65536) {
- /* seek to max comment length + end of central directory record */
- size = sizeof(locator) + 65536;
- if (FAILURE == php_stream_seek(fp, -size, SEEK_END)) {
- php_stream_close(fp);
- return FAILURE;
- }
- } else {
- php_stream_seek(fp, 0, SEEK_SET);
- }
- do {
- char *p = buf;
- if (!(read = php_stream_read(fp, buf, 8192))) {
- php_stream_close(fp);
- return FAILURE;
- }
- while ((p=(char *) memchr(p + 1, 'P', (size_t)(buf - (p+1) + 8192 - 4 + 1))) != NULL) {
- if (!memcmp(p + 1, "K\5\6", 3)) {
- if (p - buf < sizeof(locator)) {
- /* didn't read in the whole thing, back up */
- php_stream_seek(fp, 8192 - (p - buf), SEEK_CUR);
- if (sizeof(locator) != php_stream_read(fp, (char *) &locator, sizeof(locator))) {
- php_stream_close(fp);
- return FAILURE;
- }
- } else {
- memcpy((void *)&locator, (void *) p, sizeof(locator));
- }
- goto foundit;
- }
- }
- } while (read == 8192);
- php_stream_close(fp);
- return FAILURE;
-foundit:
- if (locator.centraldisk != 0 || locator.disknumber != 0) {
- /* split archives not handled */
- php_stream_close(fp);
- return FAILURE;
- }
- mydata = ecalloc(sizeof(phar_archive_data), 1);
- mydata->fname = estrndup(fname, fname_len);
-#ifdef PHP_WIN32
- phar_unixify_path_separators(mydata->fname, fname_len);
-#endif
- mydata->is_zip = 1;
- mydata->fname_len = fname_len;
- /* clean up on big-endian systems */
- /* read in archive comment, if any */
- if (locator.comment_len) {
- metadata = (char *) emalloc(locator.comment_len);
- if (locator.comment_len != php_stream_read(fp, metadata, locator.comment_len)) {
- php_stream_close(fp);
- efree(mydata->fname);
- efree(mydata);
- return FAILURE;
- }
- if (phar_parse_metadata(&metadata, &mydata->metadata, locator.comment_len TSRMLS_CC) == FAILURE) {
- /* if not valid serialized data, it is a regular string */
- ALLOC_INIT_ZVAL(mydata->metadata);
- Z_STRVAL_P(mydata->metadata) = metadata;
- Z_STRLEN_P(mydata->metadata) = locator.comment_len;
- Z_TYPE_P(mydata->metadata) = IS_STRING;
- }
- } else {
- mydata->metadata = NULL;
- }
- /* seek to central directory */
- php_stream_seek(fp, locator.cdir_offset, SEEK_SET);
- /* read in central directory */
- zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
- zend_get_hash_value, destroy_phar_manifest_entry, 0);
- entry.phar = mydata;
- entry.is_zip = 1;
-#define PHAR_ZIP_FAIL \
- zend_hash_destroy(&mydata->manifest); \
- php_stream_close(fp); \
- efree(mydata->fname); \
- if (mydata->metadata) { \
- zval_dtor(mydata->metadata); \
- } \
- efree(mydata); \
- return FAILURE
-
- /* add each central directory item to the manifest */
- for (i = 0; i < locator.cdir_size; i++) {
- phar_zip_central_dir_file zipentry;
-
- /* sizeof(zipentry) reports size of contents + 2, no idea why */
- if (sizeof(zipentry)-2 != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry)-2)) {
- PHAR_ZIP_FAIL;
- }
- /* clean up for bigendian systems */
- if (memcmp("PK\1\2", zipentry.signature, 4)) {
- /* corrupted entry */
- PHAR_ZIP_FAIL;
- }
- entry.compressed_filesize = zipentry.compsize;
- entry.uncompressed_filesize = zipentry.uncompsize;
- entry.crc32 = zipentry.crc32;
- entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
- entry.flags = PHAR_ENT_PERM_DEF_FILE;
- if (zipentry.flags & PHAR_ZIP_FLAG_ENCRYPTED) {
- PHAR_ZIP_FAIL;
- }
- if (!zipentry.filename_len) {
- PHAR_ZIP_FAIL;
- }
- entry.filename_len = zipentry.filename_len;
- entry.filename = (char *) emalloc(zipentry.filename_len+1);
- if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
- efree(entry.filename);
- PHAR_ZIP_FAIL;
- }
- entry.filename[entry.filename_len] = '\0';
- if (entry.filename[entry.filename_len - 1] == '/') {
- entry.is_dir = 1;
- entry.filename_len--;
- entry.flags |= PHAR_ENT_PERM_DEF_DIR;
- } else {
- entry.is_dir = 0;
- }
- if (zipentry.extra_len) {
- if (FAILURE == phar_zip_process_extra(fp, &entry, zipentry.extra_len TSRMLS_CC)) {
- PHAR_ZIP_FAIL;
- }
- }
- switch (zipentry.compressed) {
- case PHAR_ZIP_COMP_NONE :
- /* compression flag already set */
- break;
- case PHAR_ZIP_COMP_DEFLATE :
- entry.flags |= PHAR_ENT_COMPRESSED_GZ;
- if (!phar_has_zlib) {
- if (error) {
- spprintf(error, 0, "zlib extension is required for zlib compressed zip-based .phar file \"%s\"", fname);
- }
- PHAR_ZIP_FAIL;
- }
- break;
- case PHAR_ZIP_COMP_BZIP2 :
- entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
- if (!phar_has_bz2) {
- if (error) {
- spprintf(error, 0, "bz2 extension is required for Bzip2 compressed zip-based .phar file \"%s\"", fname);
- }
- PHAR_ZIP_FAIL;
- }
- break;
- default :
- PHAR_ZIP_FAIL;
- }
- /* get file metadata */
- if (zipentry.comment_len) {
- metadata = (char *) emalloc(zipentry.comment_len);
- if (zipentry.comment_len != php_stream_read(fp, metadata, zipentry.comment_len)) {
- PHAR_ZIP_FAIL;
- }
- if (phar_parse_metadata(&metadata, &(entry.metadata), zipentry.comment_len TSRMLS_CC) == FAILURE) {
- /* if not valid serialized data, it is a regular string */
- ALLOC_INIT_ZVAL(entry.metadata);
- Z_STRVAL_P(entry.metadata) = metadata;
- Z_STRLEN_P(entry.metadata) = zipentry.comment_len;
- Z_TYPE_P(entry.metadata) = IS_STRING;
- } else {
- efree(metadata);
- }
- } else {
- entry.metadata = NULL;
- }
- if (entry.filename_len == sizeof(".phar/alias.txt")-1 && strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
- size_t remember = php_stream_tell(fp);
- php_stream_filter *filter;
- /* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
- zval filterparams;
-
- /* archive alias found, seek to file contents, do not validate local header. Potentially risky, but
- not very. */
- php_stream_seek(fp, zipentry.offset + sizeof(phar_zip_file_header) + entry.filename_len + zipentry.extra_len, SEEK_SET);
- if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
-/* this is defined in zlib's zconf.h */
-#ifndef MAX_WBITS
-#define MAX_WBITS 15
-#endif
- array_init(&filterparams);
- add_assoc_long(&filterparams, "window", MAX_WBITS + 32);
- filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
- if (!filter) {
- add_assoc_long(&filterparams, "window", MAX_WBITS);
- filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
- if (!filter) {
- PHAR_ZIP_FAIL;
- }
- }
- zval_dtor(&filterparams);
- php_stream_filter_append(&fp->readfilters, filter);
- if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
- PHAR_ZIP_FAIL;
- }
- php_stream_filter_flush(filter, 1);
- php_stream_filter_remove(filter, 1 TSRMLS_CC);
- } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
- php_stream_filter *filter;
- filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
- if (!filter) {
- PHAR_ZIP_FAIL;
- }
- php_stream_filter_append(&fp->readfilters, filter);
- php_stream_filter_append(&fp->readfilters, filter);
- if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
- PHAR_ZIP_FAIL;
- }
- php_stream_filter_flush(filter, 1);
- php_stream_filter_remove(filter, 1 TSRMLS_CC);
- }
-
- mydata->is_explicit_alias = 1;
- mydata->alias_len = zipentry.uncompsize;
- zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), mydata->alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
- }
- zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
- }
- zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
- if (pphar) {
- *pphar = mydata;
- }
- return SUCCESS;
-}
/* $Id$ */
+#ifdef PHP_WIN32
+#pragma pack(1)
+# define PHAR_ZIP_PACK
+#else
+# define PHAR_ZIP_PACK __attribute__((__packed__))
+#endif
typedef struct _phar_zip_file_header {
- char magic[4]; /* local file header signature 4 bytes (0x04034b50) */
+ char signature[4]; /* local file header signature 4 bytes (0x04034b50) */
char zipversion[2]; /* version needed to extract 2 bytes */
php_uint16 flags; /* general purpose bit flag 2 bytes */
php_uint16 compressed; /* compression method 2 bytes */
php_uint16 extra_len; /* extra field length 2 bytes */
/* file name (variable size) */
/* extra field (variable size) */
-} phar_zip_file_header;
+} PHAR_ZIP_PACK phar_zip_file_header;
typedef struct _phar_zip_file_datadesc {
php_uint32 crc32; /* crc-32 4 bytes */
php_uint32 compsize; /* compressed size 4 bytes */
php_uint32 uncompsize; /* uncompressed size 4 bytes */
-} phar_zip_data_desc;
+} PHAR_ZIP_PACK phar_zip_data_desc;
typedef struct _phar_zip_file_datadesc_zip64 {
php_uint32 crc32; /* crc-32 4 bytes */
php_uint32 compsize2;
php_uint32 uncompsize; /* uncompressed size 8 bytes */
php_uint32 uncompsize2;
-} phar_zip_data_desc_zip64;
+} PHAR_ZIP_PACK phar_zip_data_desc_zip64;
typedef struct _phar_zip_archive_extra_data_record {
char signature[4]; /* archive extra data signature 4 bytes (0x08064b50) */
php_uint32 len; /* extra field length 4 bytes */
/* extra field data (variable size) */
-} phar_zip_archive_extra_data_record;
+} PHAR_ZIP_PACK phar_zip_archive_extra_data_record;
/* madeby/extractneeded value if bzip2 compression is used */
#define PHAR_ZIP_BZIP2 "46"
typedef struct _phar_zip_extra_field_header {
char tag[2];
php_uint16 size;
-} phar_zip_extra_field_header;
+} PHAR_ZIP_PACK phar_zip_extra_field_header;
typedef struct _phar_zip_unix3 {
char tag[2]; /* 0x756e Short tag for this extra block type ("nu") */
php_uint16 size; /* TSize Short total data size for this block */
php_uint32 crc32; /* CRC Long CRC-32 of the remaining data */
php_uint16 perms; /* Mode Short file permissions */
- php_uint32 smylinksize; /* SizDev Long symlink'd size OR major/minor dev num */
+ php_uint32 symlinksize; /* SizDev Long symlink'd size OR major/minor dev num */
php_uint16 uid; /* UID Short user ID */
php_uint16 gid; /* GID Short group ID */
/* (var.) variable symbolic link filename */
-} phar_zip_unix3;
+} PHAR_ZIP_PACK phar_zip_unix3;
typedef struct _phar_zip_central_dir_file {
char signature[4]; /* central file header signature 4 bytes (0x02014b50) */
/* file name (variable size) */
/* extra field (variable size) */
/* file comment (variable size) */
-} phar_zip_central_dir_file;
+} PHAR_ZIP_PACK phar_zip_central_dir_file;
typedef struct _phar_zip_dir_signature {
char signature[4]; /* header signature 4 bytes (0x05054b50) */
php_uint16 size; /* size of data 2 bytes */
-} phar_zip_dir_signature;
+} PHAR_ZIP_PACK phar_zip_dir_signature;
typedef struct _phar_zip64_dir_end {
char signature[4]; /* zip64 end of central dir
the starting disk number 8 bytes */
php_uint32 offset2;
/* zip64 extensible data sector (variable size) */
-} phar_zip64_dir_end;
+} PHAR_ZIP_PACK phar_zip64_dir_end;
typedef struct _phar_zip64_dir_locator {
char signature[4]; /* zip64 end of central dir locator
end of central directory record 8 bytes */
php_uint32 diroffset2;
php_uint32 totaldisks; /* total number of disks 4 bytes */
-} phar_zip64_dir_locator;
+} PHAR_ZIP_PACK phar_zip64_dir_locator;
typedef struct _phar_zip_dir_end {
char signature[4]; /* end of central dir signature 4 bytes (0x06054b50) */
the starting disk number 4 bytes */
php_uint16 comment_len; /* .ZIP file comment length 2 bytes */
/* .ZIP file comment (variable size) */
-} phar_zip_dir_end;
-
-BEGIN_EXTERN_C()
-int phar_zip_parse(char *fname, int fname_len, char **error TSRMLS_DC);
-END_EXTERN_C()
+} PHAR_ZIP_PACK phar_zip_dir_end;
+#ifdef PHP_WIN32
+#pragma pack()
+#endif
/*
* Local variables:
* tab-width: 4
if (error) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
efree(error);
+ } else {
+ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host);
}
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host);
efree(internal_file);
php_url_free(resource);
return NULL;
fprintf(stderr, "Cached: %s\n", idata->internal_file->filedata ? "yes" : "no");
#endif
- /* do we have the data already? */
- if (idata->fp) {
- fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
- efree(internal_file);
- return fpf;
- }
-
-#if PHP_MAJOR_VERSION < 6
- if (PG(safe_mode) && (!php_checkuid(idata->phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- phar_entry_delref(idata TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
-#endif
-
- if (php_check_open_basedir(idata->phar->fname TSRMLS_CC)) {
- phar_entry_delref(idata TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
-
- fp = idata->phar->fp;
-
- if (!idata->phar->is_zip && !fp) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot open phar \"%s\"", idata->phar->fname);
- phar_entry_delref(idata TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
-
- if (!phar_open_jit(idata->phar, idata->internal_file, fp, &error, idata->for_write TSRMLS_CC)) {
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
- efree(error);
- phar_entry_delref(idata TSRMLS_CC);
- efree(internal_file);
- return NULL;
- }
- idata->fp = idata->internal_file->fp;
- if (idata->fp == idata->phar->fp) {
- idata->zero = idata->internal_file->offset_within_phar + idata->phar->internal_file_start;
- }
-
/* check length, crc32 */
- if (!idata->internal_file->is_crc_checked && phar_postprocess_file(wrapper, options, idata, idata->internal_file->crc32 TSRMLS_CC) != SUCCESS) {
+ if (!idata->internal_file->is_crc_checked && phar_postprocess_file(wrapper, options, idata, idata->internal_file->crc32, &error TSRMLS_CC) != SUCCESS) {
/* already issued the error */
phar_entry_delref(idata TSRMLS_CC);
efree(internal_file);
return NULL;
}
- idata->internal_file->is_crc_checked = 1;
fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
efree(internal_file);
/* use our proxy position */
php_stream_seek(data->fp, data->position + data->zero, SEEK_SET);
- if (!data->zero) {
- got = php_stream_read(data->fp, buf, count);
- if (data->fp->eof) {
- stream->eof = 1;
- }
- /* note the position, and restore the stream for the next fp */
- data->position = php_stream_tell(data->fp);
- } else {
- got = php_stream_read(data->fp, buf, MIN(count, data->internal_file->uncompressed_filesize - data->position));
- data->position = php_stream_tell(data->fp) - data->zero;
- stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize);
- }
+ got = php_stream_read(data->fp, buf, MIN(count, data->internal_file->uncompressed_filesize - data->position));
+ data->position = php_stream_tell(data->fp) - data->zero;
+ stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize);
return got;
phar_entry_data *data = (phar_entry_data *)stream->abstract;
int res;
- if (data->zero) {
- off_t temp;
- switch (whence) {
- case SEEK_END :
- temp = data->zero + data->internal_file->uncompressed_filesize + offset;
- break;
- case SEEK_CUR :
- temp = data->zero + data->position + offset;
- break;
- case SEEK_SET :
- temp = data->zero + offset;
- break;
- }
- if (temp > data->zero + (off_t) data->internal_file->uncompressed_filesize) {
- *newoffset = -1;
- return -1;
- }
- if (temp < data->zero) {
- *newoffset = -1;
- return -1;
- }
- res = php_stream_seek(data->fp, temp, SEEK_SET);
- *newoffset = php_stream_tell(data->fp) - data->zero;
- data->position = *newoffset;
- return res;
- }
- if (whence != SEEK_SET) {
- /* use our proxy position, so the relative stuff works */
- php_stream_seek(data->fp, data->position, SEEK_SET);
- }
- /* now do the actual seek */
- res = php_stream_seek(data->fp, offset, whence);
- *newoffset = php_stream_tell(data->fp);
+ off_t temp;
+ switch (whence) {
+ case SEEK_END :
+ temp = data->zero + data->internal_file->uncompressed_filesize + offset;
+ break;
+ case SEEK_CUR :
+ temp = data->zero + data->position + offset;
+ break;
+ case SEEK_SET :
+ temp = data->zero + offset;
+ break;
+ }
+ if (temp > data->zero + (off_t) data->internal_file->uncompressed_filesize) {
+ *newoffset = -1;
+ return -1;
+ }
+ if (temp < data->zero) {
+ *newoffset = -1;
+ return -1;
+ }
+ res = php_stream_seek(data->fp, temp, SEEK_SET);
+ *newoffset = php_stream_tell(data->fp) - data->zero;
data->position = *newoffset;
return res;
}
}
if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) {
- phar_entry_info new;
+ phar_entry_info new, *source;
/* perform rename magic */
if (entry->is_deleted) {
entry->fp = NULL;
entry->metadata = 0;
entry->link = NULL;
-#if HAVE_PHAR_ZIP
- entry->zip = NULL;
-#endif
+ source = entry;
+ /* add to the manifest, and then store the pointer to the new guy in entry */
zend_hash_add(&(phar->manifest), resource_to->path+1, strlen(resource_to->path)-1, (void **)&new, sizeof(phar_entry_info), (void **) &entry);
- if (!entry->is_modified) {
- /* copy file contents into a new temp stream */
- if (!phar_open_jit(phar, entry, phar->fp, &error, 1 TSRMLS_CC)) {
- php_url_free(resource_from);
- php_url_free(resource_to);
- php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
- efree(error);
- return 0;
- }
+
+ entry->filename = estrdup(resource_to->path+1);
+ if (FAILURE == phar_copy_entry_fp(source, entry, &error TSRMLS_CC)) {
+ php_url_free(resource_from);
+ php_url_free(resource_to);
+ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
+ efree(error);
+ zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename));
+ return 0;
}
entry->is_modified = 1;
- entry->filename = estrdup(resource_to->path+1);
entry->filename_len = strlen(entry->filename);
phar_flush(phar, 0, 0, &error TSRMLS_CC);
if (error) {
php_url_free(resource_to);
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
efree(error);
+ zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename));
return 0;
}
}
/* $Id$ */
BEGIN_EXTERN_C()
-int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC);
+int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32, char **error TSRMLS_DC);
php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC);
void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC);
}
entry.tar_type = ((old & (hdr->typeflag == 0))?'0':hdr->typeflag);
- entry.offset_within_phar = pos;
+ entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */
+ entry.fp_type = PHAR_FP;
entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK;
entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime));
size_t pos;
phar_entry_info *entry = (phar_entry_info *) pDest;
struct _phar_pass_tar_info *fp = (struct _phar_pass_tar_info *)argument;
- php_stream *file;
char padding[512];
if (entry->is_deleted) {
}
/* write header */
+ entry->header_offset = php_stream_tell(fp->new);
if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) {
if (fp->error) {
spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for file \"%s\" could not be written", entry->phar->fname, entry->filename);
pos = php_stream_tell(fp->new); /* save start of file within tar */
/* write contents */
- if (entry->fp) {
- /* new file */
- file = entry->fp;
- if (file == entry->phar->fp) {
- php_stream_seek(file, entry->offset_within_phar, SEEK_SET);
- } else {
- php_stream_seek(file, 0, SEEK_SET);
+ if (FAILURE == phar_open_entry_fp(entry, fp->error TSRMLS_CC)) {
+ return ZEND_HASH_APPLY_STOP;
+ }
+ if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+ if (fp->error) {
+ spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename);
}
- } else {
- file = fp->old;
- php_stream_seek(file, entry->offset_within_phar, SEEK_SET);
+ return ZEND_HASH_APPLY_STOP;
}
- if (entry->uncompressed_filesize != php_stream_copy_to_stream(file, fp->new, entry->uncompressed_filesize)) {
+ if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry), fp->new, entry->uncompressed_filesize)) {
if (fp->error) {
spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename);
}
php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize);
entry->is_modified = 0;
- if (entry->fp && entry->fp_refcount == 0) {
- if (entry->fp != entry->phar->fp) {
- php_stream_close(entry->fp);
- }
+ if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
+ php_stream_close(entry->fp);
entry->fp = NULL;
}
+ entry->fp_type = PHAR_FP;
/* note new location within tar */
- entry->offset_within_phar = pos;
+ entry->offset = entry->offset_abs = pos;
return ZEND_HASH_APPLY_KEEP;
}
entry.is_tar = 1;
entry.tar_type = '0';
entry.phar = phar;
+ entry.fp_type = PHAR_MOD;
/* set alias */
if (phar->is_explicit_alias) {
entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
if (phar->fp) {
php_stream_close(phar->fp);
}
+ if (phar->ufp) {
+ php_stream_close(phar->ufp);
+ phar->ufp = NULL;
+ }
phar->is_brandnew = 0;
<?php
unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.1.phar.php');
?>
---EXPECTF--
+--EXPECT--
bool(false)
bool(true)
bool(false)
<?php
unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php');
unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phartmp.php');
+unlink(dirname(__FILE__) . '/notphar.phar');
__HALT_COMPILER();
?>
--EXPECTF--
<?php
unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.1.phar.php');
?>
---EXPECTF--
+--EXPECT--
bool(false)
-Cannot modify permissions for file "a.php" in phar "%s033.1.phar.php", not supported for zip-based phars
+bool(true)
+bool(false)
+test dir
+bool(true)
+bool(true)
+bool(false)
+bool(true)
===DONE===
--TEST--
-Phar::setStub()/stopBuffering() tar-based
+Phar::setStub()/stopBuffering() zip-based
--SKIPIF--
<?php if (!extension_loaded("phar")) die("skip"); ?>
-<?php if (!extension_loaded("zip")) die("skip"); ?>
--INI--
phar.require_hash=0
phar.readonly=0
Phar::convertToPhar() from zip
--SKIPIF--
<?php if (!extension_loaded("phar")) die("skip"); ?>
-<?php if (!extension_loaded("zip")) die("skip"); ?>
--INI--
phar.require_hash=0
phar.readonly=0
--SKIPIF--
<?php if (!extension_loaded("phar")) die("skip"); ?>
<?php if (!extension_loaded("spl")) die("skip SPL not available"); ?>
-<?php if (!extension_loaded("zip")) die("skip"); ?>
--INI--
phar.readonly=0
phar.require_hash=1
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | phar php single-file executable PHP extension |
+ | utility functions |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2005-2008 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$ */
+
+#include "phar_internal.h"
+
+/* retrieve a phar_entry_info's current file pointer for reading contents */
+php_stream *phar_get_efp(phar_entry_info *entry)
+{
+ if (entry->fp_type == PHAR_FP) {
+ return entry->phar->fp;
+ } else if (entry->fp_type == PHAR_UFP) {
+ return entry->phar->ufp;
+ } else {
+ return entry->fp;
+ }
+}
+
+int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position TSRMLS_DC)
+{
+ php_stream *fp = phar_get_efp(entry);
+ off_t temp;
+
+ switch (whence) {
+ case SEEK_END :
+ temp = entry->offset + entry->uncompressed_filesize + offset;
+ break;
+ case SEEK_CUR :
+ temp = entry->offset + position + offset;
+ break;
+ case SEEK_SET :
+ temp = entry->offset + offset;
+ break;
+ }
+ if (temp > entry->offset + (off_t) entry->uncompressed_filesize) {
+ return -1;
+ }
+ if (temp < entry->offset) {
+ return -1;
+ }
+ return php_stream_seek(fp, temp, SEEK_SET);
+}
+
+/**
+ * Retrieve a copy of the file information on a single file within a phar, or null.
+ * This also transfers the open file pointer, if any, to the entry.
+ *
+ * If the file does not already exist, this will fail. Pre-existing files can be
+ * appended, truncated, or read. For read, if the entry is marked unmodified, it is
+ * assumed that the file pointer, if present, is opened for reading
+ */
+int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC) /* {{{ */
+{
+ phar_archive_data *phar;
+ phar_entry_info *entry;
+ int for_write = mode[0] != 'r' || mode[1] == '+';
+ int for_append = mode[0] == 'a';
+ int for_create = mode[0] != 'r';
+ int for_trunc = mode[0] == 'w';
+
+ if (!ret) {
+ return FAILURE;
+ }
+ *ret = NULL;
+ if (error) {
+ *error = NULL;
+ }
+ if (for_write && PHAR_G(readonly)) {
+ if (error) {
+ spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, disabled by ini setting", path, fname);
+ }
+ return FAILURE;
+ }
+ if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
+ return FAILURE;
+ }
+ if (!path_len) {
+ if (error) {
+ spprintf(error, 4096, "phar error: file \"\" in phar \"%s\" cannot be empty", fname);
+ }
+ return FAILURE;
+ }
+ if (allow_dir) {
+ if ((entry = phar_get_entry_info_dir(phar, path, path_len, 2, for_create && !PHAR_G(readonly) ? NULL : error TSRMLS_CC)) == NULL) {
+ if (for_create && !PHAR_G(readonly)) {
+ return SUCCESS;
+ }
+ return FAILURE;
+ }
+ } else {
+ if ((entry = phar_get_entry_info(phar, path, path_len, for_create && !PHAR_G(readonly) ? NULL : error TSRMLS_CC)) == NULL) {
+ if (for_create && !PHAR_G(readonly)) {
+ return SUCCESS;
+ }
+ return FAILURE;
+ }
+ }
+ if (entry->is_modified && !for_write) {
+ if (error) {
+ spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for reading, writable file pointers are open", path, fname);
+ }
+ return FAILURE;
+ }
+ if (entry->fp_refcount && for_write) {
+ if (error) {
+ spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, readable file pointers are open", path, fname);
+ }
+ return FAILURE;
+ }
+ if (entry->is_deleted) {
+ if (!for_create) {
+ return FAILURE;
+ }
+ entry->is_deleted = 0;
+ }
+ if (entry->is_dir) {
+ *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
+ (*ret)->position = 0;
+ (*ret)->phar = phar;
+ (*ret)->for_write = for_write;
+ (*ret)->internal_file = entry;
+ (*ret)->is_zip = entry->is_zip;
+ (*ret)->is_tar = entry->is_tar;
+ entry->phar->refcount++;
+ entry->fp_refcount++;
+ return SUCCESS;
+ }
+ if (entry->fp_type == PHAR_MOD) {
+ if (for_trunc) {
+ if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) {
+ return FAILURE;
+ }
+ } else if (for_append) {
+ phar_seek_efp(entry, 0, SEEK_END, 0 TSRMLS_CC);
+ }
+ } else {
+ if (for_write) {
+ if (for_trunc) {
+ if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) {
+ return FAILURE;
+ }
+ } else {
+ if (FAILURE == phar_separate_entry_fp(entry, error TSRMLS_CC)) {
+ return FAILURE;
+ }
+ }
+ } else {
+ if (FAILURE == phar_open_entry_fp(entry, error TSRMLS_CC)) {
+ return FAILURE;
+ }
+ }
+ }
+ *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
+ (*ret)->position = 0;
+ (*ret)->phar = phar;
+ (*ret)->for_write = for_write;
+ (*ret)->internal_file = entry;
+ (*ret)->is_zip = entry->is_zip;
+ (*ret)->is_tar = entry->is_tar;
+ (*ret)->fp = phar_get_efp(entry);
+ (*ret)->zero = entry->offset;
+ entry->fp_refcount++;
+ entry->phar->refcount++;
+ return SUCCESS;
+}
+/* }}} */
+
+/**
+ * Create a new dummy file slot within a writeable phar for a newly created file
+ */
+phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC) /* {{{ */
+{
+ phar_archive_data *phar;
+ phar_entry_info *entry, etemp;
+ phar_entry_data *ret;
+ const char *pcr_error;
+ char is_dir = path[path_len - 1] == '/';
+
+ if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
+ return NULL;
+ }
+
+ if (FAILURE == phar_get_entry_data(&ret, fname, fname_len, path, path_len, mode, allow_dir, error TSRMLS_CC)) {
+ return NULL;
+ } else if (ret) {
+ return ret;
+ }
+
+ if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
+ if (error) {
+ spprintf(error, 0, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
+ }
+ return NULL;
+ }
+
+ /* create a new phar data holder */
+ ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
+
+ /* create an entry, this is a new file */
+ memset(&etemp, 0, sizeof(phar_entry_info));
+ etemp.filename_len = path_len;
+ etemp.fp_type = PHAR_MOD;
+ etemp.fp = php_stream_fopen_tmpfile();
+ if (!etemp.fp) {
+ if (error) {
+ spprintf(error, 0, "phar error: unable to create temporary file");
+ }
+ return NULL;
+ }
+ etemp.fp_refcount = 1;
+
+ if (is_dir) {
+ etemp.is_dir = 1;
+ etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_DIR;
+ etemp.filename_len--; /* strip trailing / */
+ path_len--;
+ } else {
+ etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_FILE;
+ }
+ etemp.is_modified = 1;
+ etemp.timestamp = time(0);
+ etemp.is_crc_checked = 1;
+ etemp.phar = phar;
+ etemp.filename = estrndup(path, path_len);
+ etemp.is_zip = phar->is_zip;
+ if (phar->is_tar) {
+ etemp.is_tar = phar->is_tar;
+ etemp.tar_type = TAR_FILE;
+ }
+ zend_hash_add(&phar->manifest, etemp.filename, path_len, (void*)&etemp, sizeof(phar_entry_info), (void **) &entry);
+
+ if (!entry) {
+ php_stream_close(etemp.fp);
+ efree(etemp.filename);
+ return NULL;
+ }
+
+ phar->refcount++;
+ ret->phar = phar;
+ ret->fp = entry->fp;
+ ret->position = ret->zero = 0;
+ ret->for_write = 1;
+ ret->is_zip = entry->is_zip;
+ ret->is_tar = entry->is_tar;
+ ret->internal_file = entry;
+ return ret;
+}
+/* }}} */
+
+/* initialize a phar_archive_data's read-only fp for existing phar data */
+int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC)
+{
+ if (phar->fp) {
+ return SUCCESS;
+ }
+
+#if PHP_MAJOR_VERSION < 6
+ if (PG(safe_mode) && (!php_checkuid(phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
+ return FAILURE;
+ }
+#endif
+
+ if (php_check_open_basedir(phar->fname TSRMLS_CC)) {
+ return FAILURE;
+ }
+
+ phar->fp = php_stream_open_wrapper(phar->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
+ if (!phar->fp) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+/* copy file data from an existing to a new phar_entry_info that is not in the manifest */
+int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error TSRMLS_DC)
+{
+ if (FAILURE == phar_open_entry_fp(source, error TSRMLS_CC)) {
+ return FAILURE;
+ }
+ dest->fp_type = PHAR_MOD;
+ dest->offset = 0;
+ dest->is_modified = 1;
+ dest->fp = php_stream_fopen_tmpfile();
+
+ phar_seek_efp(source, 0, SEEK_SET, 0 TSRMLS_CC);
+ if (source->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(source), dest->fp, source->uncompressed_filesize)) {
+ php_stream_close(dest->fp);
+ dest->fp_type = PHAR_FP;
+ if (error) {
+ spprintf(error, 4096, "phar error: unable to copy contents of file \"%s\" to \"%s\" in phar archive \"%s\"", source->filename, dest->filename, source->phar->fname);
+ }
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+/* open and decompress a compressed phar entry
+ */
+int phar_open_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC)
+{
+ php_stream_filter *filter;
+ phar_archive_data *phar = entry->phar;
+ char *filtername;
+ off_t loc;
+
+ if (entry->fp_type != PHAR_FP) {
+ /* either newly created or already modified */
+ return SUCCESS;
+ }
+ if (!phar->fp) {
+ if (FAILURE == phar_open_archive_fp(phar TSRMLS_CC)) {
+ spprintf(error, 4096, "phar error: Cannot open phar archive \"%s\" for reading", phar->fname);
+ return FAILURE;
+ }
+ }
+ if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
+ return SUCCESS;
+ }
+ if (!phar->ufp) {
+ phar->ufp = php_stream_fopen_tmpfile();
+ if (!phar->ufp) {
+ spprintf(error, 4096, "phar error: Cannot open temporary file for decompressing phar archive \"%s\" file \"%s\"", phar->fname, entry->filename);
+ return FAILURE;
+ }
+ }
+
+ if ((filtername = phar_decompress_filter(entry, 0)) != NULL) {
+ filter = php_stream_filter_create(filtername, NULL, php_stream_is_persistent(phar->ufp) TSRMLS_CC);
+ } else {
+ filter = NULL;
+ }
+ if (!filter) {
+ spprintf(error, 4096, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", phar->fname, phar_decompress_filter(entry, 1), entry->filename);
+ return FAILURE;
+ }
+ /* now we can safely use proper decompression */
+ /* save the new offset location within ufp */
+ php_stream_seek(phar->ufp, 0, SEEK_END);
+ loc = php_stream_tell(phar->ufp);
+ php_stream_filter_append(&phar->ufp->writefilters, filter);
+ php_stream_seek(phar->fp, entry->offset, SEEK_SET);
+ if (php_stream_copy_to_stream(phar->fp, phar->ufp, entry->compressed_filesize) != entry->compressed_filesize) {
+ spprintf(error, 4096, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
+ php_stream_filter_remove(filter, 1 TSRMLS_CC);
+ return FAILURE;
+ }
+ php_stream_filter_flush(filter, 1);
+ php_stream_flush(phar->ufp);
+ php_stream_filter_remove(filter, 1 TSRMLS_CC);
+ if (php_stream_tell(phar->ufp) - loc != (off_t) entry->uncompressed_filesize) {
+ spprintf(error, 4096, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
+ return FAILURE;
+ }
+
+ entry->old_flags = entry->flags;
+ entry->fp_type = PHAR_UFP;
+ /* this is now the new location of the file contents within this fp */
+ entry->offset = loc;
+
+ return SUCCESS;
+}
+
+#if defined(PHP_VERSION_ID) && PHP_VERSION_ID < 50202
+typedef struct {
+ char *data;
+ size_t fpos;
+ size_t fsize;
+ size_t smax;
+ int mode;
+ php_stream **owner_ptr;
+} php_stream_memory_data;
+#endif
+
+int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */
+{
+ if (entry->fp_type == PHAR_MOD) {
+ /* already newly created, truncate */
+#if PHP_VERSION_ID >= 50202
+ php_stream_truncate_set_size(entry->fp, 0);
+#else
+ if (php_stream_is(entry->fp, PHP_STREAM_IS_TEMP)) {
+ if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_MEMORY)) {
+ php_stream *inner = *(php_stream**)entry->fp->abstract;
+ php_stream_memory_data *memfp = (php_stream_memory_data*)inner->abstract;
+ memfp->fpos = 0;
+ memfp->fsize = 0;
+ } else if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_STDIO)) {
+ php_stream_truncate_set_size(*(php_stream**)entry->fp->abstract, 0);
+ } else {
+ efree(*ret);
+ *ret = NULL;
+ if (error) {
+ spprintf(error, 0, "phar error: file \"%s\" cannot be opened for writing, no truncate support", fname);
+ }
+ return FAILURE;
+ }
+ } else if (php_stream_is(entry->fp, PHP_STREAM_IS_STDIO)) {
+ php_stream_truncate_set_size(entry->fp, 0);
+ } else {
+ efree(*ret);
+ *ret = NULL;
+ if (error) {
+ spprintf(error, 0, "phar error: file \"%s\" cannot be opened for writing, no truncate support", fname);
+ }
+ return FAILURE;
+ }
+#endif
+ entry->old_flags = entry->flags;
+ entry->is_modified = 1;
+ phar->is_modified = 1;
+ /* reset file size */
+ entry->uncompressed_filesize = 0;
+ entry->compressed_filesize = 0;
+ entry->crc32 = 0;
+ entry->flags = PHAR_ENT_PERM_DEF_FILE;
+ entry->fp_type = PHAR_MOD;
+ entry->offset = 0;
+ return SUCCESS;
+ }
+ if (error) {
+ *error = NULL;
+ }
+ /* open a new temp file for writing */
+ entry->fp = php_stream_fopen_tmpfile();
+ if (!entry->fp) {
+ if (error) {
+ spprintf(error, 0, "phar error: unable to create temporary file");
+ }
+ return FAILURE;
+ }
+ entry->old_flags = entry->flags;
+ entry->is_modified = 1;
+ phar->is_modified = 1;
+ /* reset file size */
+ entry->uncompressed_filesize = 0;
+ entry->compressed_filesize = 0;
+ entry->crc32 = 0;
+ entry->flags = PHAR_ENT_PERM_DEF_FILE;
+ entry->fp_type = PHAR_MOD;
+ entry->offset = 0;
+ return SUCCESS;
+}
+/* }}} */
+
+int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC)
+{
+ php_stream *fp;
+
+ if (FAILURE == phar_open_entry_fp(entry, error TSRMLS_CC)) {
+ return FAILURE;
+ }
+
+ if (entry->fp_type == PHAR_MOD) {
+ return SUCCESS;
+ }
+
+ fp = php_stream_fopen_tmpfile();
+ phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC);
+ if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry), fp, entry->uncompressed_filesize)) {
+ if (error) {
+ spprintf(error, 4096, "phar error: cannot separate entry file \"%s\" contents in phar archive \"%s\" for write access", entry->filename, entry->phar->fname);
+ }
+ return FAILURE;
+ }
+
+ entry->offset = 0;
+ entry->fp = fp;
+ entry->fp_type = PHAR_MOD;
+ entry->is_modified = 1;
+ return SUCCESS;
+}
+
+/**
+ * helper function to open an internal file's fp just-in-time
+ */
+phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, php_stream *fp,
+ char **error, int for_write TSRMLS_DC)
+{
+ if (error) {
+ *error = NULL;
+ }
+ /* seek to start of internal file and read it */
+ if (FAILURE == phar_open_entry_fp(entry, error TSRMLS_CC)) {
+ return NULL;
+ }
+ if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+ spprintf(error, 4096, "phar error: cannot seek to start of file \"%s\" in phar \"%s\"", entry->filename, phar->fname);
+ return NULL;
+ }
+ return entry;
+}
+
+/**
+ * Looks up a phar archive in the filename map, connecting it to the alias
+ * (if any) or returns null
+ */
+int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
+{
+ phar_archive_data *fd, **fd_ptr;
+ char *my_realpath, *save;
+ int save_len;
+
+ phar_request_initialize(TSRMLS_C);
+
+ if (error) {
+ *error = NULL;
+ }
+ *archive = NULL;
+ if (alias && alias_len) {
+ if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void**)&fd_ptr)) {
+ if (fname && (fname_len != (*fd_ptr)->fname_len || strncmp(fname, (*fd_ptr)->fname, fname_len))) {
+ if (error) {
+ spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname);
+ }
+ return FAILURE;
+ }
+ *archive = *fd_ptr;
+ return SUCCESS;
+ }
+ }
+ my_realpath = NULL;
+ save = fname;
+ save_len = fname_len;
+ if (fname && fname_len) {
+ if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) {
+ *archive = *fd_ptr;
+ fd = *fd_ptr;
+ if (alias && alias_len) {
+ zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd, sizeof(phar_archive_data*), NULL);
+ }
+ return SUCCESS;
+ }
+ if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, (void**)&fd_ptr)) {
+ *archive = *fd_ptr;
+ return SUCCESS;
+ }
+
+ /* not found, try converting \ to / */
+ my_realpath = expand_filepath(fname, my_realpath TSRMLS_CC);
+ if (my_realpath) {
+ fname_len = strlen(my_realpath);
+ fname = my_realpath;
+ } else {
+ return FAILURE;
+ }
+#ifdef PHP_WIN32
+ phar_unixify_path_separators(fname, fname_len);
+#endif
+ if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) {
+ *archive = *fd_ptr;
+ fd = *fd_ptr;
+ if (alias && alias_len) {
+ zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd, sizeof(phar_archive_data*), NULL);
+ }
+ efree(my_realpath);
+ return SUCCESS;
+ }
+ efree(my_realpath);
+ }
+ return FAILURE;
+}
+/* }}} */
+
+/**
+ * Determine which stream compression filter (if any) we need to read this file
+ */
+char * phar_compress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
+{
+ switch (entry->flags & PHAR_ENT_COMPRESSION_MASK) {
+ case PHAR_ENT_COMPRESSED_GZ:
+ return "zlib.deflate";
+ case PHAR_ENT_COMPRESSED_BZ2:
+ return "bzip2.compress";
+ default:
+ return return_unknown ? "unknown" : NULL;
+ }
+}
+/* }}} */
+
+/**
+ * Determine which stream decompression filter (if any) we need to read this file
+ */
+char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
+{
+ php_uint32 flags;
+
+ if (entry->is_modified) {
+ flags = entry->old_flags;
+ } else {
+ flags = entry->flags;
+ }
+ switch (flags & PHAR_ENT_COMPRESSION_MASK) {
+ case PHAR_ENT_COMPRESSED_GZ:
+ return "zlib.inflate";
+ case PHAR_ENT_COMPRESSED_BZ2:
+ return "bzip2.decompress";
+ default:
+ return return_unknown ? "unknown" : NULL;
+ }
+}
+/* }}} */
+
+/**
+ * retrieve information on a file contained within a phar, or null if it ain't there
+ */
+phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, int path_len, char **error TSRMLS_DC) /* {{{ */
+{
+ return phar_get_entry_info_dir(phar, path, path_len, 0, error TSRMLS_CC);
+}
+/* }}} */
+/**
+ * retrieve information on a file or directory contained within a phar, or null if none found
+ * allow_dir is 0 for none, 1 for both empty directories in the phar and temp directories, and 2 for only
+ * valid pre-existing empty directory entries
+ */
+phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, int path_len, char dir, char **error TSRMLS_DC) /* {{{ */
+{
+ const char *pcr_error;
+ phar_entry_info *entry;
+ char is_dir = (path[path_len - 1] == '/');
+
+ if (error) {
+ *error = NULL;
+ }
+
+ if (!path_len && !dir) {
+ if (error) {
+ spprintf(error, 4096, "phar error: invalid path \"%s\" must not be empty", path);
+ }
+ return NULL;
+ }
+ if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
+ if (error) {
+ spprintf(error, 4096, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
+ }
+ return NULL;
+ }
+
+ if (!&phar->manifest.arBuckets) {
+ return NULL;
+ }
+ if (is_dir) {
+ path_len--;
+ }
+ if (SUCCESS == zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) {
+ if (entry->is_deleted) {
+ /* entry is deleted, but has not been flushed to disk yet */
+ return NULL;
+ }
+ if (entry->is_dir && !dir) {
+ if (error) {
+ spprintf(error, 4096, "phar error: path \"%s\" is a directory", path);
+ }
+ return NULL;
+ }
+ if (!entry->is_dir && is_dir) {
+ /* user requested a directory, we must return one */
+ if (error) {
+ spprintf(error, 4096, "phar error: path \"%s\" exists and is a not a directory", path);
+ }
+ return NULL;
+ }
+ return entry;
+ }
+ if (dir == 1) {
+ /* try to find a directory */
+ HashTable *manifest;
+ char *key;
+ uint keylen;
+ ulong unused;
+
+ if (!path_len) {
+ path = "/";
+ }
+ manifest = &phar->manifest;
+ zend_hash_internal_pointer_reset(manifest);
+ while (FAILURE != zend_hash_has_more_elements(manifest)) {
+ if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
+ break;
+ }
+ if (0 != memcmp(key, path, path_len)) {
+ /* entry in directory not found */
+ if (SUCCESS != zend_hash_move_forward(manifest)) {
+ break;
+ }
+ continue;
+ } else {
+ if (key[path_len] != '/') {
+ if (SUCCESS != zend_hash_move_forward(manifest)) {
+ break;
+ }
+ continue;
+ }
+ /* found a file in this path */
+ entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info));
+ /* this next line tells PharFileInfo->__destruct() to efree the filename */
+ entry->is_temp_dir = entry->is_dir = 1;
+ entry->filename = (char *) estrndup(path, path_len + 1);
+ entry->filename_len = path_len;
+ return entry;
+ }
+ }
+ }
+ return NULL;
+}
+/* }}} */
*/
#include "phar_internal.h"
-#include "php_stream_unlink.h"
#ifdef PHP_WIN32
static inline void phar_unixify_path_separators(char *path, int path_len) /* {{{ */
/* }}} */
#endif
+static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len TSRMLS_DC)
+{
+ union {
+ phar_zip_extra_field_header header;
+ phar_zip_unix3 unix3;
+ } h;
+
+ do {
+ if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) {
+ return FAILURE;
+ }
+ /* clean up header for big-endian systems */
+ if (h.header.tag != "nu") {
+ /* skip to next header */
+ php_stream_seek(fp, h.header.size, SEEK_CUR);
+ len -= h.header.size + 4;
+ continue;
+ }
+ /* unix3 header found */
+ /* clean up header for big-endian systems */
+ if (sizeof(h.unix3) != php_stream_read(fp, (char *) &h.unix3, sizeof(h.unix3))) {
+ if (h.unix3.size > sizeof(h.unix3) - 4) {
+ /* skip symlink filename - we may add this support in later */
+ php_stream_seek(fp, h.unix3.size - sizeof(h.unix3.size), SEEK_CUR);
+ }
+ /* set permissions */
+ entry->flags &= PHAR_ENT_COMPRESSION_MASK;
+ if (entry->is_dir) {
+ entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_DIR;
+ } else {
+ entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_FILE;
+ }
+ }
+ } while (len);
+ return SUCCESS;
+}
+
+/*
+ extracted from libzip
+ zip_dirent.c -- read directory entry (local or central), clean dirent
+ Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
+
+ This function is part of libzip, a library to manipulate ZIP archives.
+ The authors can be contacted at <nih@giga.or.at>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The names of the authors may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+static time_t phar_zip_d2u_time(int dtime, int ddate)
+{
+ struct tm *tm, tmbuf;
+ time_t now;
+
+ now = time(NULL);
+ tm = php_localtime_r(&now, &tmbuf);
+
+ tm->tm_year = ((ddate>>9)&127) + 1980 - 1900;
+ tm->tm_mon = ((ddate>>5)&15) - 1;
+ tm->tm_mday = ddate&31;
+
+ tm->tm_hour = (dtime>>11)&31;
+ tm->tm_min = (dtime>>5)&63;
+ tm->tm_sec = (dtime<<1)&62;
+
+ return mktime(tm);
+}
+
+static void phar_zip_u2d_time(time_t time, php_uint16 *dtime, php_uint16 *ddate)
+{
+ struct tm *tm, tmbuf;
+
+ tm = php_localtime_r(&time, &tmbuf);
+ *ddate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday;
+ *dtime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1);
+}
+
/**
* Does not check for a previously opened phar in the cache.
*
* This is used by phar_open_fp to process a zip-based phar, but can be called
* directly.
*/
-int phar_open_zipfile(char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
+int phar_open_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
{
-#if HAVE_PHAR_ZIP
- struct zip *zip;
- int ziperror, i, phar_alias_index, register_alias, metadata_len;
- phar_entry_info entry = {0};
- struct zip_stat zs;
- char tmp_buf[1024], *metadata;
+ char buf[8192], *metadata;
+ phar_zip_dir_end locator;
+ long size;
+ size_t read;
+ php_uint16 i;
phar_archive_data *mydata = NULL;
+ phar_entry_info entry = {0};
- if (error) {
- *error = NULL;
- }
-
- if (!phar_has_zip) {
- spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\", ext/zip is not enabled", fname);
- return FAILURE;
+ size = php_stream_tell(fp);
+ if (size > sizeof(locator) + 65536) {
+ /* seek to max comment length + end of central directory record */
+ size = sizeof(locator) + 65536;
+ if (FAILURE == php_stream_seek(fp, -size, SEEK_END)) {
+ php_stream_close(fp);
+ return FAILURE;
+ }
+ } else {
+ php_stream_seek(fp, 0, SEEK_SET);
}
-
- zip = zip_open(fname, 0, &ziperror);
- if (!zip) {
- if (error) {
- /* now for the stupid hoops libzip forces... */
- char *tmp;
- int tmp_len;
- tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
- if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
- spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\"", fname);
- } else {
- if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
- efree(tmp);
- spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\"", fname);
+ do {
+ char *p = buf;
+ if (!(read = php_stream_read(fp, buf, 8192))) {
+ php_stream_close(fp);
+ return FAILURE;
+ }
+ while ((p=(char *) memchr(p + 1, 'P', (size_t)(buf - (p+1) + 8192 - 4 + 1))) != NULL) {
+ if (!memcmp(p + 1, "K\5\6", 3)) {
+ if (p - buf < sizeof(locator)) {
+ /* didn't read in the whole thing, back up */
+ php_stream_seek(fp, 8192 - (p - buf), SEEK_CUR);
+ if (sizeof(locator) != php_stream_read(fp, (char *) &locator, sizeof(locator))) {
+ php_stream_close(fp);
+ return FAILURE;
+ }
} else {
- spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\": %s", fname, tmp);
- efree(tmp);
+ memcpy((void *)&locator, (void *) p, sizeof(locator));
}
+ goto foundit;
}
}
+ } while (read == 8192);
+ php_stream_close(fp);
+ return FAILURE;
+foundit:
+ if (locator.centraldisk != 0 || locator.disknumber != 0) {
+ /* split archives not handled */
+ php_stream_close(fp);
return FAILURE;
}
mydata = ecalloc(sizeof(phar_archive_data), 1);
#ifdef PHP_WIN32
phar_unixify_path_separators(mydata->fname, fname_len);
#endif
+ mydata->is_zip = 1;
mydata->fname_len = fname_len;
- if (-1 != (phar_alias_index = zip_name_locate(zip, ".phar/alias.txt", 0))) {
- struct zip_file *zf = zip_fopen_index(zip, phar_alias_index, 0);
- int tmp_len;
-
- tmp_len = zip_fread(zf, tmp_buf, 1024);
- zip_fclose(zf);
- /* if the alias is stored we enforce it (implicit overrides explicit) */
- if (tmp_len != -1 && alias && alias_len && (alias_len != tmp_len || strncmp(alias, tmp_buf, tmp_len)))
- {
- if (error) {
- spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, tmp_buf, alias);
- }
+ /* clean up on big-endian systems */
+ /* read in archive comment, if any */
+ if (locator.comment_len) {
+ metadata = (char *) emalloc(locator.comment_len);
+ if (locator.comment_len != php_stream_read(fp, metadata, locator.comment_len)) {
+ php_stream_close(fp);
+ efree(mydata->fname);
+ efree(mydata);
return FAILURE;
}
- if (tmp_len != -1) {
- /* use implicit alias */
- alias = tmp_buf;
- alias_len = tmp_len;
- register_alias = 0;
- }
- } else {
- register_alias = alias ? 1 : 0;
- }
- mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, alias_len);
- mydata->alias_len = alias ? alias_len : fname_len;
- mydata->is_zip = 1;
- mydata->zip = zip;
-
- phar_request_initialize(TSRMLS_C);
-
- /* read in phar metadata (zip file comment) */
- metadata = (char *) zip_get_archive_comment(zip, &metadata_len, 0);
- if (metadata) {
- if (phar_parse_metadata(&metadata, &mydata->metadata, metadata_len TSRMLS_CC) == FAILURE) {
+ if (phar_parse_metadata(&metadata, &mydata->metadata, locator.comment_len TSRMLS_CC) == FAILURE) {
/* if not valid serialized data, it is a regular string */
ALLOC_INIT_ZVAL(mydata->metadata);
- Z_STRVAL_P(mydata->metadata) = estrndup(metadata, metadata_len);
- Z_STRLEN_P(mydata->metadata) = metadata_len;
+ Z_STRVAL_P(mydata->metadata) = metadata;
+ Z_STRLEN_P(mydata->metadata) = locator.comment_len;
Z_TYPE_P(mydata->metadata) = IS_STRING;
}
} else {
mydata->metadata = NULL;
}
- /* set up our manifest */
+ /* seek to central directory */
+ php_stream_seek(fp, locator.cdir_offset, SEEK_SET);
+ /* read in central directory */
zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
zend_get_hash_value, destroy_phar_manifest_entry, 0);
entry.phar = mydata;
entry.is_zip = 1;
- /* prevent CRC checking */
- entry.is_crc_checked = 1;
- for (i = 0; i < zip_get_num_files(zip); i++) {
- char *name;
- name = (char *) zip_get_name(zip, i, 0);
- if (name) {
- /* get file stat */
- if (-1 != zip_stat_index(zip, i, 0, &zs)) {
- entry.compressed_filesize = zs.comp_size;
- entry.uncompressed_filesize = zs.size;
- entry.crc32 = zs.crc;
- entry.timestamp = (php_uint32) zs.mtime;
- entry.flags = PHAR_ENT_PERM_DEF_FILE;
- switch (zs.comp_method) {
- case ZIP_CM_DEFLATE :
- /* if we have zip, we have zlib decompression */
- entry.flags |= PHAR_ENT_COMPRESSED_GZ;
- break;
- case ZIP_CM_BZIP2 :
- if (!phar_has_bz2) {
- if (mydata->metadata) {
- zval_dtor(mydata->metadata);
- }
- efree(mydata->fname);
- if (mydata->alias) {
- efree(mydata->alias);
- }
- zip_close(zip);
- zend_hash_destroy(&(mydata->manifest));
- mydata->manifest.arBuckets = NULL;
- efree(mydata);
- if (error) {
- spprintf(error, 0, "bz2 extension is required for Bzip2 compressed zip-based .phar file \"%s\"", fname);
- }
- return FAILURE;
- }
- entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
- break;
+ entry.fp_type = PHAR_FP;
+#define PHAR_ZIP_FAIL(errmsg) \
+ zend_hash_destroy(&mydata->manifest); \
+ php_stream_close(fp); \
+ if (mydata->metadata) { \
+ zval_dtor(mydata->metadata); \
+ } \
+ if (error) { \
+ spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
+ } \
+ efree(mydata->fname); \
+ if (mydata->alias) { \
+ efree(mydata->alias); \
+ } \
+ efree(mydata); \
+ return FAILURE
+
+ /* add each central directory item to the manifest */
+ for (i = 0; i < locator.count; i++) {
+ phar_zip_central_dir_file zipentry;
+
+ if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) {
+ PHAR_ZIP_FAIL("unable to read central directory entry, truncated");
+ }
+ /* clean up for bigendian systems */
+ if (memcmp("PK\1\2", zipentry.signature, 4)) {
+ /* corrupted entry */
+ PHAR_ZIP_FAIL("corrupted central directory entry, no magic signature");
+ }
+ entry.compressed_filesize = zipentry.compsize;
+ entry.uncompressed_filesize = zipentry.uncompsize;
+ entry.crc32 = zipentry.crc32;
+ entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
+ entry.flags = PHAR_ENT_PERM_DEF_FILE;
+ entry.header_offset = zipentry.offset;
+ entry.offset = entry.offset_abs = zipentry.offset + sizeof(phar_zip_file_header) + zipentry.filename_len +
+ zipentry.extra_len;
+ if (zipentry.flags & PHAR_ZIP_FLAG_ENCRYPTED) {
+ PHAR_ZIP_FAIL("Cannot process encrypted zip files");
+ }
+ if (!zipentry.filename_len) {
+ PHAR_ZIP_FAIL("Cannot process zips created from stdin (zero-length filename)");
+ }
+ entry.filename_len = zipentry.filename_len;
+ entry.filename = (char *) emalloc(zipentry.filename_len+1);
+ if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
+ efree(entry.filename);
+ PHAR_ZIP_FAIL("unable to read in filename from central directory, truncated");
+ }
+ entry.filename[entry.filename_len] = '\0';
+ if (entry.filename[entry.filename_len - 1] == '/') {
+ entry.is_dir = 1;
+ entry.filename_len--;
+ entry.flags |= PHAR_ENT_PERM_DEF_DIR;
+ } else {
+ entry.is_dir = 0;
+ }
+ if (zipentry.extra_len) {
+ off_t loc = php_stream_tell(fp);
+ if (FAILURE == phar_zip_process_extra(fp, &entry, zipentry.extra_len TSRMLS_CC)) {
+ PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
+ }
+ php_stream_seek(fp, loc + zipentry.extra_len, SEEK_SET);
+ }
+ switch (zipentry.compressed) {
+ case PHAR_ZIP_COMP_NONE :
+ /* compression flag already set */
+ break;
+ case PHAR_ZIP_COMP_DEFLATE :
+ entry.flags |= PHAR_ENT_COMPRESSED_GZ;
+ if (!phar_has_zlib) {
+ PHAR_ZIP_FAIL("zlib extension is required");
+ }
+ break;
+ case PHAR_ZIP_COMP_BZIP2 :
+ entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
+ if (!phar_has_bz2) {
+ PHAR_ZIP_FAIL("bzip2 extension is required");
}
+ break;
+ default :
+ PHAR_ZIP_FAIL("unsupported compression method used in this zip");
+ }
+ /* get file metadata */
+ if (zipentry.comment_len) {
+ metadata = (char *) emalloc(zipentry.comment_len);
+ if (zipentry.comment_len != php_stream_read(fp, metadata, zipentry.comment_len)) {
+ PHAR_ZIP_FAIL("unable to read in file comment, truncated");
}
- entry.index = i;
- entry.filename_len = strlen(name);
- if (name[entry.filename_len - 1] == '/') {
- entry.is_dir = 1;
- entry.filename_len--;
- entry.flags |= PHAR_ENT_PERM_DEF_DIR;
+ if (phar_parse_metadata(&metadata, &(entry.metadata), zipentry.comment_len TSRMLS_CC) == FAILURE) {
+ /* if not valid serialized data, it is a regular string */
+ ALLOC_INIT_ZVAL(entry.metadata);
+ Z_STRVAL_P(entry.metadata) = metadata;
+ Z_STRLEN_P(entry.metadata) = zipentry.comment_len;
+ Z_TYPE_P(entry.metadata) = IS_STRING;
} else {
- entry.is_dir = 0;
+ efree(metadata);
}
- entry.filename = estrndup(name, entry.filename_len);
- /* get file metadata */
- metadata = (char *) zip_get_file_comment(zip, i, &metadata_len, 0);
- if (metadata) {
- if (phar_parse_metadata(&metadata, &(entry.metadata), metadata_len TSRMLS_CC) == FAILURE) {
- /* if not valid serialized data, it is a regular string */
- ALLOC_INIT_ZVAL(entry.metadata);
- Z_STRVAL_P(entry.metadata) = estrndup(metadata, metadata_len);
- Z_STRLEN_P(entry.metadata) = metadata_len;
- Z_TYPE_P(entry.metadata) = IS_STRING;
+ } else {
+ entry.metadata = NULL;
+ }
+ if (entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
+ php_stream_filter *filter;
+ off_t saveloc;
+
+ /* archive alias found, seek to file contents, do not validate local header. Potentially risky, but
+ not very. */
+ saveloc = php_stream_tell(fp);
+ php_stream_seek(fp, zipentry.offset + sizeof(phar_zip_file_header) + entry.filename_len + zipentry.extra_len, SEEK_SET);
+ if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
+ filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
+ if (!filter) {
+ PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
}
+ php_stream_filter_append(&fp->readfilters, filter);
+ if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
+ PHAR_ZIP_FAIL("unable to read in alias, truncated");
+ }
+ php_stream_filter_flush(filter, 1);
+ php_stream_filter_remove(filter, 1 TSRMLS_CC);
+ } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
+ php_stream_filter *filter;
+ filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
+ if (!filter) {
+ PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
+ }
+ php_stream_filter_append(&fp->readfilters, filter);
+ php_stream_filter_append(&fp->readfilters, filter);
+ if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
+ PHAR_ZIP_FAIL("unable to read in alias, truncated");
+ }
+ php_stream_filter_flush(filter, 1);
+ php_stream_filter_remove(filter, 1 TSRMLS_CC);
} else {
- entry.metadata = NULL;
+ if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
+ PHAR_ZIP_FAIL("unable to read in alias, truncated");
+ }
}
- zend_hash_add(&mydata->manifest, name, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
- }
- }
- /* ignore all errors in loading up manifest */
- zip_error_clear(zip);
- 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;
+ mydata->is_explicit_alias = 1;
+ mydata->alias_len = zipentry.uncompsize;
+ zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), mydata->alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
+ /* return to central directory parsing */
+ php_stream_seek(fp, saveloc, SEEK_SET);
+ }
+ zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
}
+ mydata->fp = fp;
+ zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
if (pphar) {
*pphar = mydata;
}
return SUCCESS;
-#else
- spprintf(error, 4096, "phar zip error: Cannot open zip-based phar \"%s\", phar not compiled with zip enabled", fname);
- return FAILURE;
-#endif
}
/* }}} */
*/
int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
{
-#if HAVE_PHAR_ZIP
phar_archive_data *phar;
int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, options, &phar, error TSRMLS_CC);
return ret;
}
- if (!phar_has_zip) {
- if (error) {
- spprintf(error, 4096, "phar zip error: phar \"%s\" cannot be created as zip-based phar, zip-based phars are disabled (enable ext/zip)", fname);
- }
- return FAILURE;
- }
if (phar->is_brandnew) {
- int *errorp = NULL;
+ phar->internal_file_start = 0;
phar->is_zip = 1;
- if (phar->fp) {
- php_stream_close(phar->fp);
- phar->fp = NULL;
- php_stream_unlink(phar->fname, 0, NULL);
- }
- phar->zip = zip_open(fname, 0 | ZIP_CREATE | ZIP_EXCL, errorp);
- if (NULL != phar->zip) {
- return SUCCESS;
- }
- /* fail - free newly created manifest entry */
- zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), phar->alias, phar->alias_len);
- zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len);
- efree(phar->fname);
- efree(phar->alias);
- efree(phar);
- *pphar = NULL;
-
- if (error) {
- /* now for the stupid hoops libzip forces... */
- char *tmp;
- int tmp_len;
- tmp_len = zip_error_to_str(NULL, 0, *errorp, *errorp);
- if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
- spprintf(error, 4096, "phar zip error: cannot create zip-based phar \"%s\"", fname);
- } else {
- if (!zip_error_to_str(tmp, tmp_len + 1, *errorp, *errorp)) {
- spprintf(error, 4096, "phar zip error: cannot create zip-based phar \"%s\"", fname);
- } else {
- spprintf(error, 4096, "phar zip error: cannot create zip-based phar \"%s\": %s", fname, tmp);
- efree(tmp);
- }
- }
- }
- return FAILURE;
+ return SUCCESS;
}
/* we've reached here - the phar exists and is a regular phar */
spprintf(error, 4096, "phar zip error: phar \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a zip-based phar", fname);
}
return FAILURE;
-#else
- if (error) {
- spprintf(error, 4096, "phar zip error: phar \"%s\" cannot be created as zip-based phar, zip-based phars are disabled and cannot be enabled", fname);
- }
- return FAILURE;
-#endif /* #if HAVE_PHAR_ZIP */
}
/* }}} */
-#if HAVE_PHAR_ZIP
-static ssize_t phar_zip_source(void *state, void *data, size_t len, enum zip_source_cmd cmd)
-{
- char *buf = (char *) data;
- phar_entry_info *entry = (phar_entry_info *) state;
- size_t read;
- struct zip_stat *sb = (struct zip_stat *) data;
- TSRMLS_FETCH();
-
- switch (cmd) {
- case ZIP_SOURCE_OPEN :
- /* offset_within_phar is only non-zero when converting from tar/phar-based to zip-based */
- php_stream_seek(entry->fp, entry->offset_within_phar, SEEK_SET);
- return 0;
- case ZIP_SOURCE_READ :
- read = php_stream_read(entry->fp, buf, len);
- if (read < 0) return 0;
- return read;
- case ZIP_SOURCE_STAT :
- zip_stat_init(sb);
- sb->mtime = time(NULL);
- sb->size = entry->uncompressed_filesize;
- return sizeof(struct zip_stat);
- case ZIP_SOURCE_FREE:
- entry->is_modified = 0;
- /* phar->fp is set only if we're converting from a tar/phar-based archive */
- if (entry->fp && entry->fp_refcount == 0 && entry->fp != entry->phar->fp) {
- php_stream_close(entry->fp);
- entry->fp = NULL;
- }
- default:
- return len;
- }
-}
-
-/* reconstruct the zip index of each manifest entry */
-static int phar_zip_reconstruct_apply(void *data TSRMLS_DC) /* {{{ */
+struct _phar_zip_pass {
+ php_stream *filefp;
+ php_stream *centralfp;
+ php_stream *old;
+ char **error;
+};
+/* perform final modification of zip contents for each file in the manifest before saving */
+static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */
{
- phar_entry_info *entry = (phar_entry_info *)data;
+ phar_entry_info *entry;
+ phar_zip_file_header local;
+ phar_zip_unix3 perms;
+ phar_zip_central_dir_file central;
+ struct _phar_zip_pass *p;
+ php_uint32 newcrc32;
+ off_t offset;
+
+ entry = (phar_entry_info *)data;
+ p = (struct _phar_zip_pass*) arg;
if (entry->is_deleted) {
- entry->index = -1;
if (entry->fp_refcount <= 0) {
return ZEND_HASH_APPLY_REMOVE;
} else {
return ZEND_HASH_APPLY_KEEP;
}
}
- if (entry->is_dir) {
- char *myname = estrndup(entry->filename, entry->filename_len+2);
- myname[entry->filename_len] = '/';
- myname[entry->filename_len+1] = '\0';
- entry->index = zip_name_locate(entry->phar->zip, entry->filename, ZIP_FL_UNCHANGED);
- efree(myname);
- } else {
- entry->index = zip_name_locate(entry->phar->zip, entry->filename, ZIP_FL_UNCHANGED);
+ memset(&local, 0, sizeof(local));
+ memset(¢ral, 0, sizeof(central));
+ memset(&perms, 0, sizeof(perms));
+ strncpy(local.signature, "PK\3\4", 4);
+ strncpy(central.signature, "PK\1\2", 4);
+ central.extra_len = local.extra_len = sizeof(perms);
+ perms.tag[0] = 'n';
+ perms.tag[1] = 'u';
+ perms.size = sizeof(perms) - 4;
+ perms.perms = entry->flags & PHAR_ENT_PERM_MASK;
+ perms.crc32 = ~0;
+ CRC32(perms.crc32, (char)perms.perms & 0xFF);
+ CRC32(perms.crc32, (char)perms.perms & 0xFF00 >> 8);
+ perms.crc32 = ~(perms.crc32);
+ if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
+ local.compressed = central.compressed = PHAR_ZIP_COMP_DEFLATE;
}
- return ZEND_HASH_APPLY_KEEP;
-}
-
-/* perform final modification of zip contents for each file in the manifest before saving */
-static int phar_zip_changed_apply(void *data TSRMLS_DC) /* {{{ */
-{
- phar_entry_info *entry = (phar_entry_info *)data;
-
- if (entry->is_deleted) {
- if (entry->fp_refcount <= 0) {
- return ZEND_HASH_APPLY_REMOVE;
- } else {
- /* we can't delete this in-memory until it is closed */
- return ZEND_HASH_APPLY_KEEP;
- }
+ if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
+ local.compressed = central.compressed = PHAR_ZIP_COMP_BZIP2;
}
+ phar_zip_u2d_time(entry->timestamp, &local.timestamp, &local.datestamp);
+ central.timestamp = local.timestamp;
+ central.datestamp = local.datestamp;
+ central.filename_len = local.filename_len = entry->filename_len;
+ central.offset = php_stream_tell(p->filefp);
+ /* do extra field for perms later */
if (entry->is_modified) {
- ssize_t (*cb)(void *state, void *data, size_t len, enum zip_source_cmd cmd) = phar_zip_source;
- if (entry->fp) {
- php_stream *fp = entry->fp;
- struct zip_source *s = zip_source_function(entry->phar->zip, cb, entry);
-
- /* we have to prevent free of this fp by mistake */
- entry->fp = NULL;
- if (-1 == (entry->index = _zip_replace(entry->phar->zip, entry->index, (const char *) entry->filename, s))) {
- zip_error_clear(entry->phar->zip);
- return ZEND_HASH_APPLY_REMOVE;
+ php_uint32 loc;
+ php_stream_filter *filter;
+ php_stream *efp;
+
+ if (entry->is_dir) {
+ entry->is_modified = 0;
+ if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
+ php_stream_close(entry->fp);
+ entry->fp = NULL;
+ entry->fp_type = PHAR_FP;
}
- /* now restore fp and is_modified */
- entry->fp = fp;
- entry->is_modified = 1;
+ goto continue_dir;
+ }
+ if (FAILURE == phar_open_entry_fp(entry, p->error TSRMLS_CC)) {
+ spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
}
+ if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+ spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ efp = phar_get_efp(entry);
- /* set file metadata */
- if (entry->metadata) {
- php_serialize_data_t metadata_hash;
- if (entry->metadata_str.c) {
- smart_str_free(&entry->metadata_str);
+ newcrc32 = ~0;
+ for (loc = 0;loc < entry->uncompressed_filesize; loc++) {
+ CRC32(newcrc32, php_stream_getc(efp));
+ }
+ entry->crc32 = ~newcrc32;
+ central.uncompsize = local.uncompsize = entry->uncompressed_filesize;
+ if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
+ /* not compressed */
+ entry->compressed_filesize = entry->uncompressed_filesize;
+ central.compsize = local.compsize = entry->compressed_filesize;
+ goto not_compressed;
+ }
+ filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
+ if (!filter) {
+ if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
+ spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ } else {
+ spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
}
- entry->metadata_str.c = 0;
- entry->metadata_str.len = 0;
- PHP_VAR_SERIALIZE_INIT(metadata_hash);
- php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
- PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
- if (-1 == zip_set_file_comment(entry->phar->zip, entry->index, entry->metadata_str.c, entry->metadata_str.len)) {
+ return ZEND_HASH_APPLY_STOP;
+ }
+
+ /* create new file that holds the compressed version */
+ /* work around inability to specify freedom in write and strictness
+ in read count */
+ entry->cfp = php_stream_fopen_tmpfile();
+ if (!entry->cfp) {
+ spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ php_stream_flush(efp);
+ if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+ spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ php_stream_filter_append((&entry->cfp->writefilters), filter);
+ if (entry->uncompressed_filesize != php_stream_copy_to_stream(efp, entry->cfp, entry->uncompressed_filesize)) {
+ spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ php_stream_filter_flush(filter, 1);
+ php_stream_flush(entry->cfp);
+ php_stream_filter_remove(filter, 1 TSRMLS_CC);
+ php_stream_seek(entry->cfp, 0, SEEK_END);
+ entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
+ /* generate crc on compressed file */
+ php_stream_rewind(entry->cfp);
+ entry->old_flags = entry->flags;
+ entry->is_modified = 1;
+ } else {
+ central.uncompsize = local.uncompsize = entry->uncompressed_filesize;
+ central.compsize = local.compsize = entry->compressed_filesize;
+ if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
+ spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ }
+not_compressed:
+ central.crc32 = local.crc32 = entry->crc32;
+continue_dir:
+ /* set file metadata */
+ if (entry->metadata) {
+ php_serialize_data_t metadata_hash;
+ if (entry->metadata_str.c) {
+ smart_str_free(&entry->metadata_str);
+ }
+ entry->metadata_str.c = 0;
+ entry->metadata_str.len = 0;
+ PHP_VAR_SERIALIZE_INIT(metadata_hash);
+ php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
+ PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
+ central.comment_len = entry->metadata_str.len;
+ }
+ entry->header_offset = php_stream_tell(p->filefp);
+ offset = entry->header_offset + sizeof(local) + entry->filename_len + sizeof(perms);
+ if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
+ spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ if (sizeof(central) != php_stream_write(p->centralfp, (char *)¢ral, sizeof(central))) {
+ spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
+ spprintf(p->error, 0, "unable to write filename to local directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
+ spprintf(p->error, 0, "unable to write filename to central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
+ spprintf(p->error, 0, "unable to write local extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
+ spprintf(p->error, 0, "unable to write central extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ if (entry->is_modified) {
+ if (entry->cfp) {
+ if (entry->compressed_filesize != php_stream_copy_to_stream(entry->cfp, p->filefp, entry->compressed_filesize)) {
+ spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
return ZEND_HASH_APPLY_STOP;
}
+ php_stream_close(entry->cfp);
+ entry->cfp = NULL;
} else {
- zip_set_file_comment(entry->phar->zip, entry->index, NULL, 0);
+ if (FAILURE == phar_open_entry_fp(entry, p->error TSRMLS_CC)) {
+ return ZEND_HASH_APPLY_STOP;
+ }
+ phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC);
+ if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry), p->filefp, entry->uncompressed_filesize)) {
+ spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ }
+ if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
+ php_stream_close(entry->fp);
+ }
+ entry->is_modified = 0;
+ } else {
+ if (!entry->is_dir && entry->compressed_filesize && entry->compressed_filesize != php_stream_copy_to_stream(p->old, p->filefp, entry->compressed_filesize)) {
+ spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ return ZEND_HASH_APPLY_STOP;
}
}
+ entry->fp = NULL;
+ entry->offset = entry->offset_abs = offset;
+ entry->fp_type = PHAR_FP;
+ if (entry->metadata_str.c) {
+ if (entry->metadata_str.len != php_stream_write(p->centralfp, entry->metadata_str.c, entry->metadata_str.len)) {
+ spprintf(p->error, 0, "unable to write metadata as file comment for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+ smart_str_free(&entry->metadata_str);
+ return ZEND_HASH_APPLY_STOP;
+ }
+ smart_str_free(&entry->metadata_str);
+ }
return ZEND_HASH_APPLY_KEEP;
}
/* }}} */
-int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC) /* {{{ */
+int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, char **error TSRMLS_DC) /* {{{ */
{
char *pos;
smart_str main_metadata_str = {0};
- int ziperrint;
static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
- php_stream *stubfile;
+ php_stream *stubfile, *oldfile;
php_serialize_data_t metadata_hash;
- int free_user_stub;
- int phar_stub_index, phar_alias_index;
+ int free_user_stub, closeoldfile = 0;
phar_entry_info entry = {0};
+ char *temperr = NULL;
+ struct _phar_zip_pass pass;
+ phar_zip_dir_end eocd;
+ pass.error = &temperr;
entry.flags = PHAR_ENT_PERM_DEF_FILE;
entry.timestamp = time(NULL);
entry.is_modified = 1;
- entry.is_crc_checked = 1;
entry.is_zip = 1;
- entry.phar = archive;
-
- /* set phar metadata */
- main_metadata_str.c = 0;
- if (archive->metadata) {
- PHP_VAR_SERIALIZE_INIT(metadata_hash);
- php_var_serialize(&main_metadata_str, &archive->metadata, &metadata_hash TSRMLS_CC);
- PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
- zip_set_archive_comment(archive->zip, main_metadata_str.c, main_metadata_str.len);
- smart_str_free(&main_metadata_str);
- } else {
- zip_set_archive_comment(archive->zip, NULL, 0);
- }
+ entry.phar = phar;
+ entry.fp_type = PHAR_MOD;
/* set alias */
- if (archive->is_explicit_alias) {
- phar_alias_index = zip_name_locate(archive->zip, ".phar/alias.txt", 0);
+ if (phar->is_explicit_alias) {
entry.fp = php_stream_fopen_tmpfile();
- if (archive->alias_len != php_stream_write(entry.fp, archive->alias, archive->alias_len)) {
+ if (phar->alias_len != php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
if (error) {
- spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", archive->fname);
+ spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", phar->fname);
}
return EOF;
}
- entry.uncompressed_filesize = sizeof(newstub) - 1;
+ entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
entry.filename_len = sizeof(".phar/alias.txt")-1;
entry.is_modified = 1;
- entry.index = phar_alias_index;
- if (SUCCESS != zend_hash_update(&archive->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
+ if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
if (error) {
- spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", archive->fname);
+ spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", phar->fname);
}
return EOF;
}
}
/* register alias */
- if (archive->alias_len) {
- phar_get_archive(&archive, archive->fname, archive->fname_len, archive->alias, archive->alias_len, NULL TSRMLS_CC);
+ if (phar->alias_len) {
+ phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, NULL TSRMLS_CC);
}
/* set stub */
- phar_stub_index = zip_name_locate(archive->zip, ".phar/stub.php", 0);
- zip_error_clear(archive->zip);
if (user_stub) {
if (len < 0) {
/* resource passed in */
if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
if (error) {
- spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", archive->fname);
+ spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
}
return EOF;
}
user_stub = 0;
if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
if (error) {
- spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", archive->fname);
+ spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
}
return EOF;
}
if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL)
{
if (error) {
- spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", archive->fname);
+ spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
}
if (free_user_stub) {
efree(user_stub);
if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
|| 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
if (error) {
- spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", archive->fname);
+ spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
}
if (free_user_stub) {
efree(user_stub);
entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
entry.filename_len = sizeof(".phar/stub.php")-1;
entry.is_modified = 1;
- entry.index = phar_stub_index;
- if (SUCCESS != zend_hash_update(&archive->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
+ if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
if (free_user_stub) {
efree(user_stub);
}
if (error) {
- spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", archive->fname);
+ spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", phar->fname);
}
return EOF;
}
efree(user_stub);
}
} else {
- if (-1 == phar_stub_index) {
+ if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
/* this is a brand new phar, add the stub */
entry.fp = php_stream_fopen_tmpfile();
if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
if (error) {
- spprintf(error, 0, "unable to create stub in new zip-based phar \"%s\"", archive->fname);
+ spprintf(error, 0, "unable to create stub in new zip-based phar \"%s\"", phar->fname);
}
return EOF;
}
- entry.uncompressed_filesize = sizeof(newstub) - 1;
+ entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
entry.filename_len = sizeof(".phar/stub.php")-1;
entry.is_modified = 1;
- entry.index = -1;
- if (SUCCESS != zend_hash_add(&archive->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
+ if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
php_stream_close(entry.fp);
efree(entry.filename);
if (error) {
- spprintf(error, 0, "unable to create stub in new zip-based phar \"%s\": %s", archive->fname, zip_strerror(archive->zip));
+ spprintf(error, 0, "unable to create stub in new zip-based phar \"%s\"", phar->fname);
}
return EOF;
}
}
}
+ if (phar->fp && !phar->is_brandnew) {
+ oldfile = phar->fp;
+ closeoldfile = 0;
+ php_stream_rewind(oldfile);
+ } else {
+ oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
+ closeoldfile = oldfile != NULL;
+ }
+
/* save modified files to the zip */
- zend_hash_apply(&archive->manifest, phar_zip_changed_apply TSRMLS_CC);
- if (archive->zip->error.str) {
+ pass.old = oldfile;
+ pass.filefp = php_stream_fopen_tmpfile();
+ if (!pass.filefp) {
+ if (closeoldfile) {
+ php_stream_close(oldfile);
+ }
if (error) {
- spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", archive->fname, zip_strerror(archive->zip));
+ spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
}
return EOF;
}
+ pass.centralfp = php_stream_fopen_tmpfile();
+ if (!pass.centralfp) {
+ if (closeoldfile) {
+ php_stream_close(oldfile);
+ }
+ if (error) {
+ spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
+ }
+ return EOF;
+ }
+ memset(&eocd, 0, sizeof(eocd));
+
+ strncpy(eocd.signature, "PK\5\6", 4);
+ eocd.counthere = eocd.count = zend_hash_num_elements(&phar->manifest);
+ zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass TSRMLS_CC);
+ if (temperr) {
+ php_stream_close(pass.filefp);
+ php_stream_close(pass.centralfp);
+ if (closeoldfile) {
+ php_stream_close(oldfile);
+ }
+ if (error) {
+ spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
+ }
+ efree(temperr);
+ return EOF;
+ }
/* save zip */
- if (-1 == zip_close(archive->zip)) {
+ eocd.cdir_size = php_stream_tell(pass.centralfp);
+ eocd.cdir_offset = php_stream_tell(pass.filefp);
+ /* implement metadata here */
+ php_stream_seek(pass.centralfp, 0, SEEK_SET);
+ if (eocd.cdir_size != php_stream_copy_to_stream(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL)) {
+ php_stream_close(pass.filefp);
+ php_stream_close(pass.centralfp);
if (error) {
- spprintf(error, 4096, "saving of zip-based phar \"%s\" failed: %s", archive->fname, zip_strerror(archive->zip));
+ spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
+ }
+ if (closeoldfile) {
+ php_stream_close(oldfile);
}
return EOF;
}
-
- /* re-open */
- archive->zip = zip_open(archive->fname, 0, &ziperrint);
- if (!archive->zip) {
+ php_stream_close(pass.centralfp);
+ if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
+ php_stream_close(pass.filefp);
if (error) {
- /* now for the stupid hoops libzip forces... */
- char *tmp;
- int tmp_len;
- tmp_len = zip_error_to_str(NULL, 0, ziperrint, ziperrint);
- if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
- spprintf(error, 4096, "phar zip error: cannot re-open zip-based phar \"%s\"", archive->fname);
- } else {
- if (!zip_error_to_str(tmp, tmp_len + 1, ziperrint, ziperrint)) {
- spprintf(error, 4096, "phar zip error: cannot re-open zip-based phar \"%s\"", archive->fname);
- } else {
- spprintf(error, 4096, "phar zip error: cannot re-open zip-based phar \"%s\": %s", archive->fname, tmp);
- efree(tmp);
- }
- }
+ spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
+ }
+ if (closeoldfile) {
+ php_stream_close(oldfile);
}
return EOF;
}
+ if (phar->metadata) {
+ /* set phar metadata */
+ PHP_VAR_SERIALIZE_INIT(metadata_hash);
+ php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
+ PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
+ if (main_metadata_str.len != php_stream_write(pass.filefp, main_metadata_str.c, main_metadata_str.len)) {
+ php_stream_close(pass.filefp);
+ if (error) {
+ spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
+ }
+ if (closeoldfile) {
+ php_stream_close(oldfile);
+ }
+ return EOF;
+ }
+ smart_str_free(&main_metadata_str);
+ }
+ if (phar->fp) {
+ php_stream_close(phar->fp);
+ }
+ if (phar->ufp) {
+ php_stream_close(phar->ufp);
+ phar->ufp = NULL;
+ }
+ /* re-open */
+ phar->is_brandnew = 0;
+ if (phar->donotflush) {
+ /* deferred flush */
+ phar->fp = pass.filefp;
+ } else {
+ phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
+ if (!phar->fp) {
+ if (closeoldfile) {
+ php_stream_close(oldfile);
+ }
+ phar->fp = pass.filefp;
+ if (error) {
+ spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
+ }
+ return EOF;
+ }
+ php_stream_rewind(pass.filefp);
+ php_stream_copy_to_stream(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL);
+ /* we could also reopen the file in "rb" mode but there is no need for that */
+ php_stream_close(pass.filefp);
+ }
- /* reconstruct manifest zip index map */
- zend_hash_apply(&archive->manifest, phar_zip_reconstruct_apply TSRMLS_CC);
+ if (closeoldfile) {
+ php_stream_close(oldfile);
+ }
return EOF;
}
/* }}} */
-#endif /* if HAVE_PHAR_ZIP */