]> granicus.if.org Git - php/commitdiff
new test for bad checksum, fix tar.c to catch tar archives with a corrupted first...
authorGreg Beaver <cellog@php.net>
Sun, 20 Apr 2008 17:28:54 +0000 (17:28 +0000)
committerGreg Beaver <cellog@php.net>
Sun, 20 Apr 2008 17:28:54 +0000 (17:28 +0000)
ext/phar/phar.c
ext/phar/phar_internal.h
ext/phar/tar.c
ext/phar/tests/tar/badchecksum.phpt [new file with mode: 0644]
ext/phar/tests/tar/files/corrupt_tarmaker.php.inc [new file with mode: 0644]

index 99fe3ddac21639c777c837a334075c8b13728054..b429b3aab7691c6891c016e0169bcdeceab9e5de 100644 (file)
@@ -1373,7 +1373,7 @@ static int phar_open_fp(php_stream* fp, char *fname, int fname_len, char *alias,
                                return phar_open_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC);
                        }
                        if (got > 512) {
-                               if (phar_is_tar(pos)) {
+                               if (phar_is_tar(pos, fname)) {
                                        php_stream_rewind(fp);
                                        return phar_open_tarfile(fp, fname, fname_len, alias, alias_len, options, pphar, compression, error TSRMLS_CC);
                                }
index 547df83167d9c02611086536b08e21456bd9110c..b6f6bf49d6c175476802965c19399bce99c44b1d 100755 (executable)
@@ -413,7 +413,7 @@ int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC);
 int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC);
 
 /* tar functions in tar.c */
-int phar_is_tar(char *buf);
+int phar_is_tar(char *buf, char *fname);
 int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC);
 int phar_open_or_create_tar(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_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC);
index 1aa94037fac728bad1a12364f916c3b27a47fa77..38ba039f53ffe6779b5a6bd80a9eade410d52ae9 100644 (file)
@@ -97,7 +97,7 @@ static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */
 }
 /* }}} */
 
-int phar_is_tar(char *buf)
+int phar_is_tar(char *buf, char *fname)
 {
        tar_header *header = (tar_header *) buf;
        php_uint32 checksum = phar_tar_number(header->checksum, sizeof(header->checksum));
@@ -113,6 +113,10 @@ int phar_is_tar(char *buf)
        memset(header->checksum, ' ', sizeof(header->checksum));
        ret = (checksum == phar_tar_checksum(buf, 512));
        memcpy(header->checksum, save, sizeof(header->checksum));
+       if (!ret && strstr(fname, ".tar")) {
+               /* probably a corrupted tar - so we will pretend it is one */
+               return 1;
+       }
        return ret;
 }
 
@@ -196,18 +200,6 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i
                }
                memset(hdr->checksum, ' ', sizeof(hdr->checksum));
                sum2 = phar_tar_checksum(buf, old?sizeof(old_tar_header):sizeof(tar_header));
-               if (sum1 != sum2) {
-                       if (error) {
-                               spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file", fname);
-                       }
-                       php_stream_close(fp);
-                       zend_hash_destroy(&myphar->manifest);
-                       myphar->manifest.arBuckets = 0;
-                       zend_hash_destroy(&myphar->mounted_dirs);
-                       myphar->mounted_dirs.arBuckets = 0;
-                       efree(myphar);
-                       return FAILURE;
-               }
 
                size = entry.uncompressed_filesize = entry.compressed_filesize =
                        phar_tar_number(hdr->size, sizeof(hdr->size));
@@ -232,6 +224,19 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i
                                entry.filename_len--;
                        }
                }
+               if (sum1 != sum2) {
+                       if (error) {
+                               spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename);
+                       }
+                       efree(entry.filename);
+                       php_stream_close(fp);
+                       zend_hash_destroy(&myphar->manifest);
+                       myphar->manifest.arBuckets = 0;
+                       zend_hash_destroy(&myphar->mounted_dirs);
+                       myphar->mounted_dirs.arBuckets = 0;
+                       efree(myphar);
+                       return FAILURE;
+               }
 
                entry.tar_type = ((old & (hdr->typeflag == 0))?'0':hdr->typeflag);
                entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */
@@ -257,6 +262,7 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i
                                if (error) {
                                        spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - symbolic link to non-existent file", fname);
                                }
+                               efree(entry.filename);
                                php_stream_close(fp);
                                zend_hash_destroy(&myphar->manifest);
                                myphar->manifest.arBuckets = 0;
diff --git a/ext/phar/tests/tar/badchecksum.phpt b/ext/phar/tests/tar/badchecksum.phpt
new file mode 100644 (file)
index 0000000..fae19b1
--- /dev/null
@@ -0,0 +1,30 @@
+--TEST--
+Phar: tar with bad checksum
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--FILE--
+<?php
+$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.tar';
+$pname = 'phar://' . $fname;
+
+include dirname(__FILE__) . '/files/corrupt_tarmaker.php.inc';
+$a = new corrupt_tarmaker($fname, 'none');
+$a->init();
+$a->addFile('hithere', 'contents', null, 'checksum');
+$a->close();
+
+try {
+       $p = new PharData($fname);
+} catch (Exception $e) {
+       echo $e->getMessage() . "\n";
+}
+
+?>
+===DONE===
+--CLEAN--
+<?php
+unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.tar');
+?>
+--EXPECTF--
+phar error: "%sbadchecksum.tar" is a corrupted tar file (checksum mismatch of file "hithere")
+===DONE===
diff --git a/ext/phar/tests/tar/files/corrupt_tarmaker.php.inc b/ext/phar/tests/tar/files/corrupt_tarmaker.php.inc
new file mode 100644 (file)
index 0000000..28fb8a4
--- /dev/null
@@ -0,0 +1,170 @@
+<?php
+// stolen from PEAR2_Pyrus_Developer_Creator_Tar by Greg Beaver, the original author, for use in unit tests
+class corrupt_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, $stat = null, $corrupt = null)
+    {
+        clearstatcache();
+        if ($stat === null) {
+            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 = 'file1.txt';
+        } 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');
+
+       if ($corrupt == 'checksum') $checksum++;
+        $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, 'w');
+                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
+     * 
+     * @param string $dir
+     */
+    function mkdir($dir)
+    {
+        $this->addFile($dir, "", array(
+                    'mode' => 0x4000 + 0644,
+                    'uid' => 0,
+                    'gid' => 0,
+                    'size' => 0,
+                    'mtime' => time(),
+                ));
+    }
+
+    /**
+     * Finish saving the package
+     */
+    function close()
+    {
+        fwrite($this->tmp, pack('a1024', ''));
+        fclose($this->tmp);
+    }
+}
\ No newline at end of file