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 "/" */
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;
}
}
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 {
/* 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\"");
}
return EOF;
}
+ zend_hash_clean(&phar->virtual_dirs);
+
if (phar->is_zip) {
return phar_zip_flush(phar, user_stub, len, convert, error TSRMLS_CC);
}
}
/* 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 */
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)) {
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) {
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;
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);
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;
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;
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);
}
}
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)
}
}
+ phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len);
memset((char *) &header, 0, sizeof(header));
if (entry->filename_len > 100) {
--- /dev/null
+--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
\ No newline at end of file
--- /dev/null
+--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
\ No newline at end of file
--- /dev/null
+<?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');
+?>
--- /dev/null
+<?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');
+?>
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;
phar_archive_data *phar;
HashTable newmanifest;
char *fname;
+ phar_archive_object **objphar;
phar = (phar_archive_data *) emalloc(sizeof(phar_archive_data));
*phar = **pphar;
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;
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;
+ }
+ }
}
/* }}} */
}
}
+ 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));