From: Greg Beaver Date: Tue, 6 May 2008 21:14:53 +0000 (+0000) Subject: fix alias overloading handling for archives that are not in use X-Git-Tag: RELEASE_2_0_0b1~80 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5ba8ca70a44647839326c988a0f73f6b3378e589;p=php fix alias overloading handling for archives that are not in use --- diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 9b55bfc5ff..29cb6d31d7 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -254,6 +254,14 @@ int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */ php_stream_close(phar->fp); phar->fp = NULL; } + if (!zend_hash_num_elements(&phar->manifest)) { + /* this is a new phar that has perhaps had an alias/metadata set, but has never + been flushed */ + if (zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) { + phar_destroy_phar_data(phar TSRMLS_CC); + } + return 1; + } } return 0; } @@ -381,7 +389,9 @@ void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */ /* }}} */ #define MAPPHAR_ALLOC_FAIL(msg) \ - php_stream_close(fp);\ + if (fp) {\ + php_stream_close(fp);\ + }\ if (error) {\ spprintf(error, 0, msg, fname);\ }\ @@ -857,7 +867,6 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int if (alias && alias_len && (alias_len != (int)tmp_len || strncmp(alias, buffer, tmp_len))) { buffer[tmp_len] = '\0'; - efree(savebuf); php_stream_close(fp); if (signature) { efree(signature); @@ -865,6 +874,7 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int if (error) { spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, buffer, alias); } + efree(savebuf); return FAILURE; } alias_len = tmp_len; @@ -998,13 +1008,27 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int mydata->sig_len = sig_len; mydata->signature = signature; phar_request_initialize(TSRMLS_C); - zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); if (register_alias) { + phar_archive_data **fd_ptr; + mydata->is_temporary_alias = temp_alias; + if (!phar_validate_alias(mydata->alias, mydata->alias_len)) { + signature = NULL; + fp = NULL; + MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias"); + } + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len)) { + signature = NULL; + fp = NULL; + MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive"); + } + } zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); } else { mydata->is_temporary_alias = 1; } + zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); efree(savebuf); if (pphar) { diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 2c3d5f1e4b..f8b62fb8c9 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -391,6 +391,7 @@ int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, i int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, 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 is_data, 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_free_alias(phar_archive_data *phar, char *alias, int alias_len 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 is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC); diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 32236a77d5..d7a08cbb1d 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -2345,6 +2345,10 @@ PHP_METHOD(Phar, setAlias) } if (alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void**)&fd_ptr)) { spprintf(&error, 0, "alias \"%s\" is already used for archive \"%s\" and cannot be used for other archives", alias, (*fd_ptr)->fname); + if (SUCCESS == phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { + efree(error); + goto valid_alias; + } zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); RETURN_FALSE; @@ -2354,6 +2358,7 @@ PHP_METHOD(Phar, setAlias) "Invalid alias \"%s\" specified for phar \"%s\"", alias, phar_obj->arc.archive->fname); RETURN_FALSE; } +valid_alias: if (phar_obj->arc.archive->alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), phar_obj->arc.archive->alias, phar_obj->arc.archive->alias_len, (void**)&fd_ptr)) { zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), phar_obj->arc.archive->alias, phar_obj->arc.archive->alias_len); readd = 1; diff --git a/ext/phar/tar.c b/ext/phar/tar.c index 3afdc9126b..eb0c9a058f 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -387,7 +387,18 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i } myphar = *actual; if (actual_alias) { + phar_archive_data **fd_ptr; + myphar->is_temporary_alias = 0; + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname); + } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len); + return FAILURE; + } + } zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); } else { myphar->alias = estrndup(myphar->fname, fname_len); diff --git a/ext/phar/tests/028.phpt b/ext/phar/tests/028.phpt index a42e17470e..087f822777 100755 --- a/ext/phar/tests/028.phpt +++ b/ext/phar/tests/028.phpt @@ -9,6 +9,7 @@ phar.require_hash=0 $fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php'; $pname = 'phar://hio'; $file = ''; +$alias = ''; $files = array(); $files['a.php'] = ''; diff --git a/ext/phar/tests/029.phpt b/ext/phar/tests/029.phpt index 5302fdb804..41fcec03e7 100755 --- a/ext/phar/tests/029.phpt +++ b/ext/phar/tests/029.phpt @@ -9,6 +9,7 @@ phar.require_hash=0 $fname1 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.1.phar.php'; $fname2 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.2.phar.php'; $fname = $fname1; +$alias = ''; $pname = 'phar://hio'; $file = ''; @@ -21,10 +22,11 @@ $files['e.php'] = ''; include 'files/phar_test.inc'; -file_put_contents($fname2, $file); +copy($fname1, $fname2); var_dump(Phar::loadPhar($fname1, 'hio')); var_dump(Phar::loadPhar($fname1, 'copy')); +$a = new Phar($fname1); try { var_dump(Phar::loadPhar($fname2, 'copy')); diff --git a/ext/phar/tests/files/phar_test.inc b/ext/phar/tests/files/phar_test.inc index 7c4e9707b9..a5e9d3fdc8 100644 --- a/ext/phar/tests/files/phar_test.inc +++ b/ext/phar/tests/files/phar_test.inc @@ -48,7 +48,7 @@ foreach($files as $name => $cont) $files[$name] = $comp; } -$alias = 'hio'; +if (!isset($alias)) $alias = 'hio'; if (isset($pmeta)) $pmeta = serialize($pmeta); else $pmeta = ''; $manifest = pack('VnVV', count($files), isset($hasdir) ? 0x1110 : 0x1000, $gflags, strlen($alias)) . $alias . pack('V', strlen($pmeta)) . $pmeta . $manifest; diff --git a/ext/phar/tests/phar_bz2.phpt b/ext/phar/tests/phar_bz2.phpt index b2781c6846..71b10f9092 100644 --- a/ext/phar/tests/phar_bz2.phpt +++ b/ext/phar/tests/phar_bz2.phpt @@ -34,6 +34,7 @@ include $fname; $a = new Phar($fname); $a['test'] = 'hi'; copy($fname, $fname2); +$a->setAlias('another'); $b = new Phar($fname2); var_dump($b->isFileFormat(Phar::PHAR)); var_dump($b->isCompressed() == Phar::BZ2); diff --git a/ext/phar/tests/phar_gzip.phpt b/ext/phar/tests/phar_gzip.phpt index 1a61a2ef3b..f472949c05 100644 --- a/ext/phar/tests/phar_gzip.phpt +++ b/ext/phar/tests/phar_gzip.phpt @@ -35,6 +35,7 @@ include $fname; $a = new Phar($fname); $a['test'] = 'hi'; copy($fname, $fname2); +$a->setAlias('another'); $b = new Phar($fname2); var_dump($b->isFileFormat(Phar::PHAR)); var_dump($b->isCompressed() == Phar::GZ); diff --git a/ext/phar/tests/phar_setalias2.phpt b/ext/phar/tests/phar_setalias2.phpt index 44c8e5b465..df8f42282e 100644 --- a/ext/phar/tests/phar_setalias2.phpt +++ b/ext/phar/tests/phar_setalias2.phpt @@ -22,6 +22,7 @@ $phar = new Phar($fname); echo $phar->getAlias() . "\n"; $phar->setAlias('test'); echo $phar->getAlias() . "\n"; +$b = $phar; $phar = new Phar(dirname(__FILE__) . '/notphar.phar'); try { $phar->setAlias('test'); diff --git a/ext/phar/tests/tar/exists_as_phar.phpt b/ext/phar/tests/tar/exists_as_phar.phpt index 6d03983c8d..9a8cfc81f0 100644 --- a/ext/phar/tests/tar/exists_as_phar.phpt +++ b/ext/phar/tests/tar/exists_as_phar.phpt @@ -18,6 +18,7 @@ $phar->setAlias('hio'); $phar->addEmptyDir('test'); $phar->stopBuffering(); copy($fname, $tname); +$phar->setAlias('hio2'); try { $p = new Phar($tname); diff --git a/ext/phar/tests/tar/phar_setalias2.phpt b/ext/phar/tests/tar/phar_setalias2.phpt index d48c041b76..a44cc397c2 100644 --- a/ext/phar/tests/tar/phar_setalias2.phpt +++ b/ext/phar/tests/tar/phar_setalias2.phpt @@ -29,7 +29,7 @@ $phar->stopBuffering(); echo $phar->getAlias() . "\n"; $phar->setAlias('test'); echo $phar->getAlias() . "\n"; - +$b = $phar; $phar = new Phar(dirname(__FILE__) . '/notphar.phar'); try { diff --git a/ext/phar/tests/test_alias_unset.phpt b/ext/phar/tests/test_alias_unset.phpt new file mode 100644 index 0000000000..0127d8bba2 --- /dev/null +++ b/ext/phar/tests/test_alias_unset.phpt @@ -0,0 +1,45 @@ +--TEST-- +Phar: test for the odd case where we intend to remove an archive from memory +--SKIPIF-- + +--INI-- +phar.readonly=0 +--FILE-- +setAlias('first'); +$phar['file1.txt'] = 'hi'; +unset($phar); + +$phar2 = new Phar($fname2); +$phar2->setAlias('first'); // this works because there are no references to $fname open +$phar2['file1.txt'] = 'hi'; +unset($phar2); + +$a = fopen($pname . '/file1.txt', 'r'); // this works because there are no references to $fname2 open +try { +$phar2 = new Phar($fname2); // fails because references open to $fname +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +fclose($a); +$phar2 = new Phar($fname2); // succeeds because all refs are closed +var_dump($phar2->getAlias()); + +$a = file_get_contents($pname . '/file1.txt'); // this fails because $fname2 ref exists +?> +===DONE=== +--CLEAN-- + + +--EXPECTF-- +Cannot open archive "%stest_alias_unset.2.phar.php", alias is already in use by existing archive +string(5) "first" + +Warning: file_get_contents(phar://%sfile1.txt): failed to open stream: Cannot open archive "%stest_alias_unset.phar.php", alias is already in use by existing archive in %stest_alias_unset.php on line %d +===DONE=== \ No newline at end of file diff --git a/ext/phar/tests/zip/exists_as_phar.phpt b/ext/phar/tests/zip/exists_as_phar.phpt index d7e739c1f1..ccb37e8187 100644 --- a/ext/phar/tests/zip/exists_as_phar.phpt +++ b/ext/phar/tests/zip/exists_as_phar.phpt @@ -18,6 +18,7 @@ $phar->setAlias('hio'); $phar->addEmptyDir('test'); $phar->stopBuffering(); copy($fname, $tname); +$phar->setAlias('hio2'); try { $p = new Phar($tname); diff --git a/ext/phar/tests/zip/phar_setalias2.phpt b/ext/phar/tests/zip/phar_setalias2.phpt index e26f4ae747..c89f4c777a 100644 --- a/ext/phar/tests/zip/phar_setalias2.phpt +++ b/ext/phar/tests/zip/phar_setalias2.phpt @@ -28,7 +28,7 @@ $phar->stopBuffering(); echo $phar->getAlias() . "\n"; $phar->setAlias('test'); echo $phar->getAlias() . "\n"; - +$b = $phar; $phar = new Phar(dirname(__FILE__) . '/notphar.phar'); try { $phar->setAlias('test'); diff --git a/ext/phar/util.c b/ext/phar/util.c index 828975281e..9b0dc279a5 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -983,6 +983,19 @@ phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, return entry; } +int phar_free_alias(phar_archive_data *phar, char *alias, int alias_len TSRMLS_DC) /* {{{ */ +{ + if (phar->refcount) { + return FAILURE; + } + /* this archive has no open references, so emit an E_STRICT and remove it */ + if (zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) { + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + /** * Looks up a phar archive in the filename map, connecting it to the alias * (if any) or returns null @@ -1005,6 +1018,10 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch if (error) { spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname); } + if (SUCCESS == phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { + efree(*error); + *error = NULL; + } return FAILURE; } *archive = *fd_ptr; @@ -1019,6 +1036,9 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch *archive = *fd_ptr; fd = *fd_ptr; if (alias && alias_len) { + if (fd->alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len, (void**)&fd_ptr)) { + zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len); + } zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd, sizeof(phar_archive_data*), NULL); } return SUCCESS; diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 633f59c0cc..e8b46e4ce5 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -386,6 +386,7 @@ foundit: 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; + phar_archive_data **fd_ptr; /* archive alias found, seek to file contents, do not validate local header. Potentially risky, but not very. */ @@ -432,6 +433,11 @@ foundit: efree(entry.filename); PHAR_ZIP_FAIL("invalid alias"); } + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len)) { + PHAR_ZIP_FAIL("alias is already in use by existing archive"); + } + } 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);