mirror of
https://github.com/php/php-src.git
synced 2025-01-19 18:24:15 +08:00
add tests for copy-on-write support
- fix metadata handling with cached phars - fix virtual_dirs with rmdir - ensure that after copy-on-write, all existing Phar objects link to the newly copied phar data
This commit is contained in:
parent
eaf5d4c8b3
commit
e16636f069
@ -507,12 +507,6 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
|
||||
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", unable to make cached phar writeable", resource->path+1, resource->host);
|
||||
php_url_free(resource);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset((void *) &entry, 0, sizeof(phar_entry_info));
|
||||
|
||||
/* strip leading "/" */
|
||||
@ -631,50 +625,43 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* now for the easy part */
|
||||
if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
|
||||
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", unable to make cached phar writeable", resource->path+1, resource->host);
|
||||
php_url_free(resource);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (zend_hash_internal_pointer_reset(&phar->manifest);
|
||||
if (!entry->is_deleted) {
|
||||
for (zend_hash_internal_pointer_reset(&phar->manifest);
|
||||
HASH_KEY_NON_EXISTANT != zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL);
|
||||
zend_hash_move_forward(&phar->manifest)) {
|
||||
|
||||
PHAR_STR(key, str_key);
|
||||
PHAR_STR(key, str_key);
|
||||
|
||||
if (!entry->is_deleted &&
|
||||
key_len > path_len &&
|
||||
memcmp(str_key, resource->path+1, path_len) == 0 &&
|
||||
IS_SLASH(str_key[path_len])) {
|
||||
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
|
||||
if (entry->is_temp_dir) {
|
||||
efree(entry->filename);
|
||||
efree(entry);
|
||||
if (key_len > path_len &&
|
||||
memcmp(str_key, resource->path+1, path_len) == 0 &&
|
||||
IS_SLASH(str_key[path_len])) {
|
||||
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
|
||||
if (entry->is_temp_dir) {
|
||||
efree(entry->filename);
|
||||
efree(entry);
|
||||
}
|
||||
php_url_free(resource);
|
||||
return 0;
|
||||
}
|
||||
php_url_free(resource);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (zend_hash_internal_pointer_reset(&phar->virtual_dirs);
|
||||
HASH_KEY_NON_EXISTANT != zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL);
|
||||
zend_hash_move_forward(&phar->virtual_dirs)) {
|
||||
|
||||
PHAR_STR(key, str_key);
|
||||
|
||||
if (!entry->is_deleted &&
|
||||
key_len > path_len &&
|
||||
memcmp(str_key, resource->path+1, path_len) == 0 &&
|
||||
IS_SLASH(str_key[path_len])) {
|
||||
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
|
||||
if (entry->is_temp_dir) {
|
||||
efree(entry->filename);
|
||||
efree(entry);
|
||||
for (zend_hash_internal_pointer_reset(&phar->virtual_dirs);
|
||||
HASH_KEY_NON_EXISTANT != zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL);
|
||||
zend_hash_move_forward(&phar->virtual_dirs)) {
|
||||
|
||||
PHAR_STR(key, str_key);
|
||||
|
||||
if (key_len > path_len &&
|
||||
memcmp(str_key, resource->path+1, path_len) == 0 &&
|
||||
IS_SLASH(str_key[path_len])) {
|
||||
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
|
||||
if (entry->is_temp_dir) {
|
||||
efree(entry->filename);
|
||||
efree(entry);
|
||||
}
|
||||
php_url_free(resource);
|
||||
return 0;
|
||||
}
|
||||
php_url_free(resource);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,9 +637,7 @@ int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSR
|
||||
zval_ptr_dtor(metadata);
|
||||
*metadata = (zval *) pemalloc(buf_len, 1);
|
||||
memcpy(*metadata, *buffer, buf_len);
|
||||
if (!zip_metadata_len) {
|
||||
*buffer += buf_len;
|
||||
}
|
||||
*buffer += buf_len;
|
||||
return SUCCESS;
|
||||
}
|
||||
} else {
|
||||
@ -1036,9 +1034,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
|
||||
|
||||
/* check whether we have meta data, zero check works regardless of byte order */
|
||||
if (mydata->is_persistent) {
|
||||
char *mysave = buffer;
|
||||
PHAR_GET_32(buffer, mydata->metadata_len);
|
||||
buffer = mysave;
|
||||
if (phar_parse_metadata(&buffer, &mydata->metadata, mydata->metadata_len TSRMLS_CC) == FAILURE) {
|
||||
MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
|
||||
}
|
||||
@ -2562,6 +2558,8 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert,
|
||||
return EOF;
|
||||
}
|
||||
|
||||
zend_hash_clean(&phar->virtual_dirs);
|
||||
|
||||
if (phar->is_zip) {
|
||||
return phar_zip_flush(phar, user_stub, len, convert, error TSRMLS_CC);
|
||||
}
|
||||
@ -2739,6 +2737,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert,
|
||||
}
|
||||
/* after excluding deleted files, calculate manifest size in bytes and number of entries */
|
||||
++new_manifest_count;
|
||||
phar_add_virtual_dirs(phar, entry->filename, entry->filename_len);
|
||||
|
||||
if (entry->is_dir) {
|
||||
/* we use this to calculate API version, 1.1.1 is used for phars with directories */
|
||||
@ -3545,6 +3544,7 @@ void phar_request_initialize(TSRMLS_D) /* {{{ */
|
||||
PHAR_GLOBALS->request_ends = 0;
|
||||
PHAR_GLOBALS->request_done = 0;
|
||||
zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), 5, zend_get_hash_value, destroy_phar_data, 0);
|
||||
zend_hash_init(&(PHAR_GLOBALS->phar_persist_map), 5, zend_get_hash_value, NULL, 0);
|
||||
zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), 5, zend_get_hash_value, NULL, 0);
|
||||
|
||||
if (PHAR_G(manifest_cached)) {
|
||||
@ -3581,6 +3581,8 @@ PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
|
||||
PHAR_GLOBALS->phar_alias_map.arBuckets = NULL;
|
||||
zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map));
|
||||
PHAR_GLOBALS->phar_fname_map.arBuckets = NULL;
|
||||
zend_hash_destroy(&(PHAR_GLOBALS->phar_persist_map));
|
||||
PHAR_GLOBALS->phar_persist_map.arBuckets = NULL;
|
||||
PHAR_GLOBALS->phar_SERVER_mung_list = 0;
|
||||
|
||||
if (PHAR_GLOBALS->cached_fp) {
|
||||
|
@ -150,6 +150,9 @@ typedef struct _phar_entry_fp phar_entry_fp;
|
||||
typedef struct _phar_archive_data phar_archive_data;
|
||||
|
||||
ZEND_BEGIN_MODULE_GLOBALS(phar)
|
||||
/* a list of phar_archive_data objects that reference a cached phar, so
|
||||
that if copy-on-write is performed, we can swap them out for the new value */
|
||||
HashTable phar_persist_map;
|
||||
HashTable phar_fname_map;
|
||||
/* for cached phars, this is a per-process store of fp/ufp */
|
||||
phar_entry_fp *cached_fp;
|
||||
@ -593,6 +596,7 @@ char *phar_create_default_stub(const char *index_php, const char *web_index, siz
|
||||
char *phar_decompress_filter(phar_entry_info * entry, int return_unknown);
|
||||
char *phar_compress_filter(phar_entry_info * entry, int return_unknown);
|
||||
|
||||
void phar_remove_virtual_dirs(phar_archive_data *phar, char *filename, int filename_len TSRMLS_DC);
|
||||
void phar_add_virtual_dirs(phar_archive_data *phar, char *filename, int filename_len TSRMLS_DC);
|
||||
int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, char *path, int path_len TSRMLS_DC);
|
||||
char *phar_find_in_include_path(char *file, int file_len, phar_archive_data **pphar TSRMLS_DC);
|
||||
|
@ -1254,6 +1254,9 @@ PHP_METHOD(Phar, __construct)
|
||||
|
||||
if (!phar_data->is_persistent) {
|
||||
phar_obj->arc.archive->is_data = is_data;
|
||||
} else if (!EG(exception)) {
|
||||
/* register this guy so we can modify if necessary */
|
||||
zend_hash_add(&PHAR_GLOBALS->phar_persist_map, (const char *) phar_obj->arc.archive, sizeof(phar_obj->arc.archive), (void *) &phar_obj, sizeof(phar_archive_object **), NULL);
|
||||
}
|
||||
|
||||
phar_obj->spl.info_class = phar_ce_entry;
|
||||
@ -1378,6 +1381,19 @@ PHP_METHOD(Phar, unlinkArchive)
|
||||
return; \
|
||||
}
|
||||
|
||||
/* {{{ proto void Phar::__destruct()
|
||||
* if persistent, remove from the cache
|
||||
*/
|
||||
PHP_METHOD(Phar, __destruct)
|
||||
{
|
||||
phar_archive_object *phar_obj = (phar_archive_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
|
||||
|
||||
if (phar_obj->arc.archive && phar_obj->arc.archive->is_persistent) {
|
||||
zend_hash_del(&PHAR_GLOBALS->phar_persist_map, (const char *) &(phar_obj->arc.archive), sizeof(&(phar_obj->arc.archive)));
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
struct _phar_t {
|
||||
phar_archive_object *p;
|
||||
zend_class_entry *c;
|
||||
@ -3923,6 +3939,15 @@ PHP_METHOD(Phar, getMetadata)
|
||||
PHAR_ARCHIVE_OBJECT();
|
||||
|
||||
if (phar_obj->arc.archive->metadata) {
|
||||
if (phar_obj->arc.archive->is_persistent) {
|
||||
zval *ret;
|
||||
char *buf = estrndup((char *) phar_obj->arc.archive->metadata, phar_obj->arc.archive->metadata_len);
|
||||
/* assume success, we would have failed before */
|
||||
phar_parse_metadata(&buf, &ret, phar_obj->arc.archive->metadata_len TSRMLS_CC);
|
||||
efree(buf);
|
||||
RETURN_ZVAL(ret, 0, 1);
|
||||
return;
|
||||
}
|
||||
RETURN_ZVAL(phar_obj->arc.archive->metadata, 1, 0);
|
||||
}
|
||||
}
|
||||
@ -5098,6 +5123,7 @@ zend_function_entry php_archive_methods[] = {
|
||||
PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PRIVATE)
|
||||
#else
|
||||
PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(Phar, __destruct, NULL, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(Phar, addEmptyDir, arginfo_phar_emptydir, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(Phar, addFile, arginfo_phar_addfile, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(Phar, addFromString, arginfo_phar_fromstring, ZEND_ACC_PUBLIC)
|
||||
|
@ -620,6 +620,7 @@ static int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ *
|
||||
}
|
||||
}
|
||||
|
||||
phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len);
|
||||
memset((char *) &header, 0, sizeof(header));
|
||||
|
||||
if (entry->filename_len > 100) {
|
||||
|
17
ext/phar/tests/cache_list/copyonwrite2.phar.phpt
Normal file
17
ext/phar/tests/cache_list/copyonwrite2.phar.phpt
Normal file
@ -0,0 +1,17 @@
|
||||
--TEST--
|
||||
Phar: copy-on-write test 2 [cache_list]
|
||||
--INI--
|
||||
default_charset=UTF-8
|
||||
phar.cache_list={PWD}/copyonwrite2.phar.php
|
||||
phar.readonly=0
|
||||
--SKIPIF--
|
||||
<?php if (!extension_loaded("phar")) die("skip"); ?>
|
||||
--FILE_EXTERNAL--
|
||||
files/write2.phar
|
||||
--EXPECT--
|
||||
string(2) "hi"
|
||||
bool(true)
|
||||
string(2) "hi"
|
||||
bool(true)
|
||||
bool(true)
|
||||
ok
|
16
ext/phar/tests/cache_list/copyonwrite3.phar.phpt
Normal file
16
ext/phar/tests/cache_list/copyonwrite3.phar.phpt
Normal file
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
Phar: copy-on-write test 3 [cache_list]
|
||||
--INI--
|
||||
default_charset=UTF-8
|
||||
phar.cache_list={PWD}/copyonwrite3.phar.php
|
||||
phar.readonly=0
|
||||
--SKIPIF--
|
||||
<?php if (!extension_loaded("phar")) die("skip"); ?>
|
||||
--FILE_EXTERNAL--
|
||||
files/write3.phar
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(false)
|
||||
ok
|
BIN
ext/phar/tests/cache_list/files/write2.phar
Normal file
BIN
ext/phar/tests/cache_list/files/write2.phar
Normal file
Binary file not shown.
23
ext/phar/tests/cache_list/files/write2.phar.inc
Normal file
23
ext/phar/tests/cache_list/files/write2.phar.inc
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
$fname = dirname(__FILE__) . '/write2.phar';
|
||||
@unlink($fname);
|
||||
|
||||
$phar = new Phar($fname);
|
||||
$phar->setStub('<?php
|
||||
$phar = new Phar(__FILE__);
|
||||
var_dump($phar->getMetadata());
|
||||
mkdir("phar://" . __FILE__ . "/test");
|
||||
var_dump(is_dir("phar://" . __FILE__ . "/test"));
|
||||
$phar2 = new Phar(__FILE__);
|
||||
var_dump($phar2->getMetadata());
|
||||
var_dump(isset($phar["test"]));
|
||||
var_dump(isset($phar2["test"]));
|
||||
echo "ok\n";
|
||||
__HALT_COMPILER();
|
||||
?>');
|
||||
$phar->setMetadata('hi');
|
||||
$phar['test.txt'] = "hi
|
||||
";
|
||||
$phar['test.txt']->setMetadata('hi');
|
||||
?>
|
BIN
ext/phar/tests/cache_list/files/write3.phar
Normal file
BIN
ext/phar/tests/cache_list/files/write3.phar
Normal file
Binary file not shown.
21
ext/phar/tests/cache_list/files/write3.phar.inc
Normal file
21
ext/phar/tests/cache_list/files/write3.phar.inc
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
$fname = dirname(__FILE__) . '/write3.phar';
|
||||
@unlink($fname);
|
||||
|
||||
$phar = new Phar($fname);
|
||||
$phar->setStub('<?php
|
||||
clearstatcache();
|
||||
var_dump(file_exists("phar://" . __FILE__ . "/test"), is_dir("phar://" . __FILE__ . "/test"));
|
||||
rmdir("phar://" . __FILE__ . "/test");
|
||||
clearstatcache();
|
||||
var_dump(file_exists("phar://" . __FILE__ . "/test"), is_dir("phar://" . __FILE__ . "/test"));
|
||||
echo "ok\n";
|
||||
__HALT_COMPILER();
|
||||
?>');
|
||||
$phar->setMetadata('hi');
|
||||
$phar['test.txt'] = "hi
|
||||
";
|
||||
$phar['test.txt']->setMetadata('hi');
|
||||
$phar->addEmptyDir('test');
|
||||
?>
|
@ -2219,8 +2219,10 @@ static void phar_update_cached_entry(void *data, void *argument) /* {{{ */
|
||||
|
||||
if (entry->metadata) {
|
||||
if (entry->metadata_len) {
|
||||
char *buf = estrndup((char *) entry->metadata, entry->metadata_len);
|
||||
/* assume success, we would have failed before */
|
||||
phar_parse_metadata((char **) &entry->metadata, &entry->metadata, entry->metadata_len TSRMLS_CC);
|
||||
efree(buf);
|
||||
} else {
|
||||
zval *t;
|
||||
|
||||
@ -2245,6 +2247,7 @@ static void phar_copy_cached_phar(phar_archive_data **pphar TSRMLS_DC) /* {{{ */
|
||||
phar_archive_data *phar;
|
||||
HashTable newmanifest;
|
||||
char *fname;
|
||||
phar_archive_object **objphar;
|
||||
|
||||
phar = (phar_archive_data *) emalloc(sizeof(phar_archive_data));
|
||||
*phar = **pphar;
|
||||
@ -2264,7 +2267,9 @@ static void phar_copy_cached_phar(phar_archive_data **pphar TSRMLS_DC) /* {{{ */
|
||||
if (phar->metadata) {
|
||||
/* assume success, we would have failed before */
|
||||
if (phar->metadata_len) {
|
||||
phar_parse_metadata((char **) &phar->metadata, &phar->metadata, phar->metadata_len TSRMLS_CC);
|
||||
char *buf = estrndup((char *) phar->metadata, phar->metadata_len);
|
||||
phar_parse_metadata(&buf, &phar->metadata, phar->metadata_len TSRMLS_CC);
|
||||
efree(buf);
|
||||
} else {
|
||||
zval *t;
|
||||
|
||||
@ -2291,6 +2296,15 @@ static void phar_copy_cached_phar(phar_archive_data **pphar TSRMLS_DC) /* {{{ */
|
||||
zend_get_hash_value, NULL, 0);
|
||||
zend_hash_copy(&phar->virtual_dirs, &(*pphar)->virtual_dirs, NULL, NULL, sizeof(void *));
|
||||
*pphar = phar;
|
||||
|
||||
/* now, scan the list of persistent Phar objects referencing this phar and update the pointers */
|
||||
for (zend_hash_internal_pointer_reset(&PHAR_GLOBALS->phar_persist_map);
|
||||
SUCCESS == zend_hash_get_current_data(&PHAR_GLOBALS->phar_persist_map, (void **) &objphar);
|
||||
zend_hash_move_forward(&PHAR_GLOBALS->phar_persist_map)) {
|
||||
if (objphar[0]->arc.archive->fname_len == phar->fname_len && !memcmp(objphar[0]->arc.archive->fname, phar->fname, phar->fname_len)) {
|
||||
objphar[0]->arc.archive = phar;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -704,6 +704,7 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */
|
||||
}
|
||||
}
|
||||
|
||||
phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len);
|
||||
memset(&local, 0, sizeof(local));
|
||||
memset(¢ral, 0, sizeof(central));
|
||||
memset(&perms, 0, sizeof(perms));
|
||||
|
Loading…
Reference in New Issue
Block a user