]> granicus.if.org Git - php/commitdiff
fix alias overloading handling for archives that are not in use
authorGreg Beaver <cellog@php.net>
Tue, 6 May 2008 21:14:53 +0000 (21:14 +0000)
committerGreg Beaver <cellog@php.net>
Tue, 6 May 2008 21:14:53 +0000 (21:14 +0000)
17 files changed:
ext/phar/phar.c
ext/phar/phar_internal.h
ext/phar/phar_object.c
ext/phar/tar.c
ext/phar/tests/028.phpt
ext/phar/tests/029.phpt
ext/phar/tests/files/phar_test.inc
ext/phar/tests/phar_bz2.phpt
ext/phar/tests/phar_gzip.phpt
ext/phar/tests/phar_setalias2.phpt
ext/phar/tests/tar/exists_as_phar.phpt
ext/phar/tests/tar/phar_setalias2.phpt
ext/phar/tests/test_alias_unset.phpt [new file with mode: 0644]
ext/phar/tests/zip/exists_as_phar.phpt
ext/phar/tests/zip/phar_setalias2.phpt
ext/phar/util.c
ext/phar/zip.c

index 9b55bfc5ff8b5ce3d1e079ded457eb2059b559eb..29cb6d31d713cb2f0ba056dcf1e6eb7a02138dfe 100644 (file)
@@ -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) {
index 2c3d5f1e4b337637859b763e34bb92867c368cad..f8b62fb8c931f10bc67a354328389810ce2a3f7a 100755 (executable)
@@ -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);
 
index 32236a77d50c6ceb332b49ca362f7e148c77cd94..d7a08cbb1d38f0581bc77350f41c436f0be81149 100755 (executable)
@@ -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;
index 3afdc9126b0832f2fac440b8d21c74b956adf763..eb0c9a058fd64aca6c845aa16a1855ec521c5068 100644 (file)
@@ -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);
index a42e17470eec39ea8c4f44f204406e36d0f9ecc0..087f822777fd9d624658af37bd72d34a0251afa9 100755 (executable)
@@ -9,6 +9,7 @@ phar.require_hash=0
 $fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';
 $pname = 'phar://hio';
 $file = '<?php include "' . $pname . '/a.php"; __HALT_COMPILER(); ?>';
+$alias = '';
 
 $files = array();
 $files['a.php']   = '<?php echo "This is a\n"; include "'.$pname.'/b.php"; ?>';      
index 5302fdb804426c3617de09a87ee1ce59303f080e..41fcec03e7fd66a1789cb3f6f5bb2466e320867c 100755 (executable)
@@ -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 = '<?php include "' . $pname . '/a.php"; __HALT_COMPILER(); ?>';
 
@@ -21,10 +22,11 @@ $files['e.php']   = '<?php echo "This is e\n"; ?>';
 
 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'));
index 7c4e9707b9e9264bf76d5e4a38658abce83789cb..a5e9d3fdc8ac8859624885c6df19c694f5dbf187 100644 (file)
@@ -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;
index b2781c684618396d4fe7f5e32525663a96fe0e47..71b10f909241faf214034ad3a7c7783cf58404a2 100644 (file)
@@ -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);
index 1a61a2ef3b86c6d959f96cd1ec98c15457995958..f472949c0552c0c9a75ff1dad19763a7eb99aa8e 100644 (file)
@@ -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);
index 44c8e5b465daaffb9125cabd3eea71a6334b6d93..df8f42282e3489fc34de73c83a718152fd848f1d 100644 (file)
@@ -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');
index 6d03983c8dcc60d32ec333e344729fc86fbe8120..9a8cfc81f0079dcb4061d0c68582dfd346d30f19 100644 (file)
@@ -18,6 +18,7 @@ $phar->setAlias('hio');
 $phar->addEmptyDir('test');
 $phar->stopBuffering();
 copy($fname, $tname);
+$phar->setAlias('hio2');
 
 try {
        $p = new Phar($tname);
index d48c041b76110b4549bdfc600a4ffd0299b163a8..a44cc397c21dd2770d18997d7e1db0aae82a9730 100644 (file)
@@ -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 (file)
index 0000000..0127d8b
--- /dev/null
@@ -0,0 +1,45 @@
+--TEST--
+Phar: test for the odd case where we intend to remove an archive from memory
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--INI--
+phar.readonly=0
+--FILE--
+<?php
+$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';
+$pname = 'phar://' . $fname;
+$fname2 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.2.phar.php';
+$pname2 = 'phar://' . $fname2;
+
+$phar = new Phar($fname);
+$phar->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--
+<?php unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php'); ?>
+<?php unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.2.phar.php'); ?>
+--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
index d7e739c1f1678b38c4db2be38aacd5bb8757aa3f..ccb37e8187a7ea0d0f04c5c8bf554719808b9183 100644 (file)
@@ -18,6 +18,7 @@ $phar->setAlias('hio');
 $phar->addEmptyDir('test');
 $phar->stopBuffering();
 copy($fname, $tname);
+$phar->setAlias('hio2');
 
 try {
        $p = new Phar($tname);
index e26f4ae747b4c203208af75f8412e02922c89f76..c89f4c777a78440745a1713f3ee7ea57a96ed849 100644 (file)
@@ -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');
index 828975281e5113fc14959d29131df0545ff3a56c..9b0dc279a5174b2086df400db7fa977e0b728d23 100644 (file)
@@ -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;
index 633f59c0cc91eff5fda9681e82f390a1fcabe86b..e8b46e4ce56efd2abff3df189ead0c3957051ec7 100644 (file)
@@ -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);