]> granicus.if.org Git - php/commitdiff
add tests for copy-on-write support
authorGreg Beaver <cellog@php.net>
Sun, 12 Oct 2008 19:40:11 +0000 (19:40 +0000)
committerGreg Beaver <cellog@php.net>
Sun, 12 Oct 2008 19:40:11 +0000 (19:40 +0000)
 - 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

13 files changed:
ext/phar/dirstream.c
ext/phar/phar.c
ext/phar/phar_internal.h
ext/phar/phar_object.c
ext/phar/tar.c
ext/phar/tests/cache_list/copyonwrite2.phar.phpt [new file with mode: 0644]
ext/phar/tests/cache_list/copyonwrite3.phar.phpt [new file with mode: 0644]
ext/phar/tests/cache_list/files/write2.phar [new file with mode: 0644]
ext/phar/tests/cache_list/files/write2.phar.inc [new file with mode: 0644]
ext/phar/tests/cache_list/files/write3.phar [new file with mode: 0644]
ext/phar/tests/cache_list/files/write3.phar.inc [new file with mode: 0644]
ext/phar/util.c
ext/phar/zip.c

index fa22f4681b95b670a8040f2b15edfea10d01ad27..c0e4b18e092c4afb443e94d365bba25ca93ca403 100644 (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;
                }
        }
 
index ec608a84d3873d076f241503f5815f3d30abdd38..183fec44876ce44af4fa3e82b6ddbf58c0114d54 100644 (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) {
index 7e0a59c43be1639682b5dffe044b2ab468ce5ecc..efe28671fa0e35dfb100619455f4f5650adab1e8 100755 (executable)
@@ -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);
index 043f2b42809cfb8fb4355b19248cd4344c7c55bd..c85f39d79e998b3e81c5834410d2fcc8a003c9d2 100755 (executable)
@@ -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)
index 01dca9364cd82984a47d54e7c3b30ea8cfe51832..bcd75c325afd5b716901d3d210a705c3d9af14f3 100644 (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) {
diff --git a/ext/phar/tests/cache_list/copyonwrite2.phar.phpt b/ext/phar/tests/cache_list/copyonwrite2.phar.phpt
new file mode 100644 (file)
index 0000000..8d21c81
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/ext/phar/tests/cache_list/copyonwrite3.phar.phpt b/ext/phar/tests/cache_list/copyonwrite3.phar.phpt
new file mode 100644 (file)
index 0000000..7e2c94c
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/ext/phar/tests/cache_list/files/write2.phar b/ext/phar/tests/cache_list/files/write2.phar
new file mode 100644 (file)
index 0000000..0941025
Binary files /dev/null and b/ext/phar/tests/cache_list/files/write2.phar differ
diff --git a/ext/phar/tests/cache_list/files/write2.phar.inc b/ext/phar/tests/cache_list/files/write2.phar.inc
new file mode 100644 (file)
index 0000000..7a4e2e3
--- /dev/null
@@ -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');
+?>
diff --git a/ext/phar/tests/cache_list/files/write3.phar b/ext/phar/tests/cache_list/files/write3.phar
new file mode 100644 (file)
index 0000000..c5be7da
Binary files /dev/null and b/ext/phar/tests/cache_list/files/write3.phar differ
diff --git a/ext/phar/tests/cache_list/files/write3.phar.inc b/ext/phar/tests/cache_list/files/write3.phar.inc
new file mode 100644 (file)
index 0000000..686c065
--- /dev/null
@@ -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');
+?>
index 224b7e778b230b9be593b856d888ab9ffc1c23a7..c6d734981c8e44ffc41c107667cfebe00307c88c 100644 (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;
+               }
+       }
 }
 /* }}} */
 
index 027fcdb66540b36b613a6ec70014c072eb08f824..891280eb6e228df0bc8f13e13d9065b0020456eb 100644 (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));