]> granicus.if.org Git - php/commitdiff
do not attempt to create a new phar if a file exists and is corrupted or is not a...
authorGreg Beaver <cellog@php.net>
Sat, 5 Jan 2008 22:46:54 +0000 (22:46 +0000)
committerGreg Beaver <cellog@php.net>
Sat, 5 Jan 2008 22:46:54 +0000 (22:46 +0000)
i.e. require a clean slate - a non-existent file or a valid phar archive - to muck around with phar contents
add the first tar-based phar test

ext/phar/phar.c
ext/phar/tests/tar/make_invalid_tar.php.inc [new file with mode: 0644]
ext/phar/tests/tar/tar_001.phpt [new file with mode: 0644]
ext/phar/tests/tar/tarmaker.php.inc [new file with mode: 0644]

index 5c9e95be20b8177ab129edfce0e6fb96c0956239..ea04444113259837051bfd9c2213675f65aee10f 100644 (file)
@@ -1782,15 +1782,24 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a
                return FAILURE;
        }
 
-       fp = php_stream_open_wrapper(fname, PHAR_G(readonly)?"rb":"r+b", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
+       /* first open readonly so it won't be created if not present */
+       fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
 
-       if (fp && phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, 0 TSRMLS_CC) == SUCCESS) {
-               if (!PHAR_G(readonly)) {
-                       (*pphar)->is_writeable = 1;
+       if (fp) {
+               if (phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC) == SUCCESS) {
+                       if (!PHAR_G(readonly)) {
+                               (*pphar)->is_writeable = 1;
+                       }
+                       return SUCCESS;
+               } else {
+                       /* file exists, but is either corrupt or not a phar archive */
+                       php_stream_close(fp);
+                       return FAILURE;
                }
-               return SUCCESS;
        }
 
+       php_stream_close(fp);
+
        if (PHAR_G(readonly)) {
                if (options & REPORT_ERRORS) {
                        if (error) {
@@ -1799,6 +1808,8 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a
                }
                return FAILURE;
        }
+       /* re-open for writing */
+       fp = php_stream_open_wrapper(fname, "r+b", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
 
        /* set up our manifest */
        mydata = ecalloc(sizeof(phar_archive_data), 1);
diff --git a/ext/phar/tests/tar/make_invalid_tar.php.inc b/ext/phar/tests/tar/make_invalid_tar.php.inc
new file mode 100644 (file)
index 0000000..c18bd19
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+include dirname(__FILE__) . '/tarmaker.php.inc';
+class corrupter extends tarmaker {
+function close()
+{
+       fwrite($this->tmp, 'oopsie');
+       fclose($this->tmp);
+}
+}
\ No newline at end of file
diff --git a/ext/phar/tests/tar/tar_001.phpt b/ext/phar/tests/tar/tar_001.phpt
new file mode 100644 (file)
index 0000000..4e888c0
--- /dev/null
@@ -0,0 +1,33 @@
+--TEST--
+Phar: tar-based phar corrupted
+--SKIPIF--
+<?php if (!extension_loaded('phar')) die('skip'); ?>
+<?php if (!extension_loaded("spl")) die("skip SPL not available"); ?>
+--INI--
+phar.readonly=0
+phar.require_hash=0
+--FILE--
+<?php
+include dirname(__FILE__) . '/make_invalid_tar.php.inc';
+$a = new corrupter(dirname(__FILE__) . '/tar_001.phar', 'none');
+$a->init();
+$a->addFile('tar_001.phpt', __FILE__);
+$a->close();
+
+$a = fopen('phar://tar_001.phar/tar_001.phpt', 'rb');
+try {
+$a = new Phar('tar_001.phar');
+echo "should not execute\n";
+} catch (Exception $e) {
+echo $e->getMessage() . "\n";
+}
+?>
+===DONE===
+--CLEAN--
+<?php
+@unlink(dirname(__FILE__) . '/tar_001.phar');
+?>
+--EXPECTF--
+Warning: fopen(phar://tar_001.phar/tar_001.phpt): failed to open stream: phar error: "tar_001.phar" is a corrupted tar file in %s/tar_001.php on line %d
+Cannot open phar file 'tar_001.phar' with alias '(null)': phar error: "tar_001.phar" is a corrupted tar file
+===DONE===
\ No newline at end of file
diff --git a/ext/phar/tests/tar/tarmaker.php.inc b/ext/phar/tests/tar/tarmaker.php.inc
new file mode 100644 (file)
index 0000000..9b75d9b
--- /dev/null
@@ -0,0 +1,161 @@
+<?php
+// stolen from PEAR2_Pyrus_Developer_Creator_Tar by Greg Beaver, the original author, for use in unit tests
+class tarmaker
+{
+    /**
+     * Path to archive file
+     *
+     * @var string
+     */
+    protected $archive;
+    /**
+     * Temporary stream used for creating the archive
+     *
+     * @var stream
+     */
+    protected $tmp;
+    protected $path;
+    protected $compress;
+    function __construct($path, $compress = 'zlib')
+    {
+        $this->compress = $compress;
+        if ($compress === 'bz2' && !function_exists('bzopen')) {
+            throw new PEAR2_Pyrus_Developer_Creator_Exception(
+                'bzip2 extension not available');
+        }
+        if ($compress === 'zlib' && !function_exists('gzopen')) {
+            throw new PEAR2_Pyrus_Developer_Creator_Exception(
+                'zlib extension not available');
+        }
+        $this->path = $path;
+    }
+
+    /**
+     * save a file inside this package
+     * 
+     * This code is modified from Vincent Lascaux's File_Archive
+     * package, which is licensed under the LGPL license.
+     * @param string relative path within the package
+     * @param string|resource file contents or open file handle
+     */
+    function addFile($path, $fileOrStream)
+    {
+        clearstatcache();
+        if (is_resource($fileOrStream)) {
+            $stat = fstat($fileOrStream);
+        } else {
+            $stat = array(
+                'mode' => 0x8000 + 0644,
+                'uid' => 0,
+                'gid' => 0,
+                'size' => strlen($fileOrStream),
+                'mtime' => time(),
+            );
+        }
+
+        $link = null;
+        if ($stat['mode'] & 0x4000) {
+            $type = 5;        // Directory
+        } else if ($stat['mode'] & 0x8000) {
+            $type = 0;        // Regular
+        } else if ($stat['mode'] & 0xA000) {
+            $type = 1;        // Link
+            $link = @readlink($current);
+        } else {
+            $type = 9;        // Unknown
+        }
+
+        $filePrefix = '';
+        if (strlen($path) > 255) {
+            throw new Exception(
+                "$path is too long, must be 255 characters or less"
+            );
+        } else if (strlen($path) > 100) {
+            $filePrefix = substr($path, 0, strlen($path)-100);
+            $path = substr($path, -100);
+        }
+
+        $block = pack('a100a8a8a8a12A12',
+                $path,
+                decoct($stat['mode']),
+                sprintf('%6s ',decoct($stat['uid'])),
+                sprintf('%6s ',decoct($stat['gid'])),
+                sprintf('%11s ',decoct($stat['size'])),
+                sprintf('%11s ',decoct($stat['mtime']))
+            );
+
+        $blockend = pack('a1a100a6a2a32a32a8a8a155a12',
+            $type,
+            $link,
+            'ustar',
+            '00',
+            'Pyrus',
+            'Pyrus',
+            '',
+            '',
+            $filePrefix,
+            '');
+
+        $checkheader = array_merge(str_split($block), str_split($blockend));
+        if (!function_exists('_pear2tarchecksum')) {
+            function _pear2tarchecksum($a, $b) {return $a + ord($b);}
+        }
+        $checksum = 256; // 8 * ord(' ');
+        $checksum += array_reduce($checkheader, '_pear2tarchecksum');
+
+        $checksum = pack('a8', sprintf('%6s ', decoct($checksum)));
+
+        fwrite($this->tmp, $block . $checksum . $blockend, 512);
+        if (is_resource($fileOrStream)) {
+            stream_copy_to_stream($fileOrStream, $this->tmp);
+            if ($stat['size'] % 512) {
+                fwrite($this->tmp, str_repeat("\0", 512 - $stat['size'] % 512));
+            }
+        } else {
+            fwrite($this->tmp, $fileOrStream);
+            if (strlen($fileOrStream) % 512) {
+                fwrite($this->tmp, str_repeat("\0", 512 - strlen($fileOrStream) % 512));
+            }
+        }
+    }
+
+    /**
+     * Initialize the package creator
+     */
+    function init()
+    {
+        switch ($this->compress) {
+            case 'zlib' :
+                $this->tmp = gzopen($this->path, 'wb');
+                break;
+            case 'bz2' :
+                $this->tmp = bzopen($this->path, 'wb');
+                break;
+            case 'none' :
+                $this->tmp = fopen($this->path, 'wb');
+                break;
+            default :
+                throw new Exception(
+                    'unknown compression type ' . $this->compress);
+        }
+    }
+
+    /**
+     * Create an internal directory, creating parent directories as needed
+     * 
+     * This is a no-op for the tar creator
+     * @param string $dir
+     */
+    function mkdir($dir)
+    {
+    }
+
+    /**
+     * Finish saving the package
+     */
+    function close()
+    {
+        fwrite($this->tmp, pack('a1024', ''));
+        fclose($this->tmp);
+    }
+}
\ No newline at end of file