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:
Greg Beaver 2008-10-12 19:40:11 +00:00
parent eaf5d4c8b3
commit e16636f069
13 changed files with 160 additions and 48 deletions

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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)

View File

@ -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) {

View 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

View 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

Binary file not shown.

View 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');
?>

Binary file not shown.

View 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');
?>

View File

@ -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;
}
}
}
/* }}} */

View File

@ -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(&central, 0, sizeof(central));
memset(&perms, 0, sizeof(perms));