]> granicus.if.org Git - php/commitdiff
Fixed bug #54289
authorBishop Bettini <bishop.bettini@gmail.com>
Sat, 6 Jan 2018 07:21:30 +0000 (02:21 -0500)
committerNikita Popov <nikita.ppv@gmail.com>
Sun, 28 Jan 2018 20:51:25 +0000 (21:51 +0100)
If a directory is passed to Phar::extractTo(), loop over all
entries and extract all files with the given prefix.

NEWS
ext/phar/phar_object.c
ext/phar/tests/bug54289.phpt [new file with mode: 0644]
ext/phar/tests/bug54289/in/dirA/fileA [new file with mode: 0644]
ext/phar/tests/bug54289/in/dirA/fileB [new file with mode: 0644]
ext/phar/tests/bug54289/in/dirAB/file1 [new file with mode: 0644]
ext/phar/tests/bug54289/in/dirAB/file2 [new file with mode: 0644]
ext/phar/tests/bug54289/in/dirAB/file3 [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 3a32a183397288956a33f698c4188372a8bedc83..28ed594748f82d2ec647074bd2d735943bf6bec5 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,7 +6,11 @@ PHP                                                                        NEWS
   . Fixed bug #75864 ("stream_isatty" returns wrong value on s390x). (Sam Ding)
 
 - PGSQL:
-  . Fixed #75838 (Memory leak in pg_escape_bytea()). (ard_1 at mail dot ru)
+  . Fixed bug #75838 (Memory leak in pg_escape_bytea()). (ard_1 at mail dot ru)
+
+- Phar:
+  . Fixed bug #54289 (Phar::extractTo() does not accept specific directories to
+    be extracted). (bishop)
 
 - ODBC:
   . Fixed bug #73725 (Unable to retrieve value of varchar(max) type). (Anatol)
index 59707d3638fd722bec27225a11cc4ce00bdb8acf..5a85bf7ad98b71fdfd2d4d075db0a014d1cf3f54 100644 (file)
@@ -4346,21 +4346,51 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
 }
 /* }}} */
 
+static int extract_helper(phar_archive_data *archive, zend_string *search, char *pathto, size_t pathto_len, zend_bool overwrite, char **error) { /* {{{ */
+       int extracted = 0;
+       phar_entry_info *entry;
+
+       if (!search) {
+               /* nothing to match -- extract all files */
+               ZEND_HASH_FOREACH_PTR(&archive->manifest, entry) {
+                       if (FAILURE == phar_extract_file(overwrite, entry, pathto, (int)pathto_len, error)) return -1;
+                       extracted++;
+               } ZEND_HASH_FOREACH_END();
+       } else if ('/' == ZSTR_VAL(search)[ZSTR_LEN(search) - 1]) {
+               /* ends in "/" -- extract all entries having that prefix */
+               ZEND_HASH_FOREACH_PTR(&archive->manifest, entry) {
+                       if (0 != strncmp(ZSTR_VAL(search), entry->filename, ZSTR_LEN(search))) continue;
+                       if (FAILURE == phar_extract_file(overwrite, entry, pathto, (int)pathto_len, error)) return -1;
+                       extracted++;
+               } ZEND_HASH_FOREACH_END();
+       } else {
+               /* otherwise, looking for an exact match */
+               entry = zend_hash_find_ptr(&archive->manifest, search);
+               if (NULL == entry) return 0;
+               if (FAILURE == phar_extract_file(overwrite, entry, pathto, (int)pathto_len, error)) return -1;
+               return 1;
+       }
+
+       return extracted;
+}
+/* }}} */
+
 /* {{{ proto bool Phar::extractTo(string pathto[[, mixed files], bool overwrite])
  * Extract one or more file from a phar archive, optionally overwriting existing files
  */
 PHP_METHOD(Phar, extractTo)
 {
-       char *error = NULL;
        php_stream *fp;
        php_stream_statbuf ssb;
-       phar_entry_info *entry;
-       char *pathto, *filename;
-       size_t pathto_len, filename_len;
+       char *pathto;
+       zend_string *filename;
+       size_t pathto_len;
        int ret, i;
        int nelems;
+       zval *zval_file;
        zval *zval_files = NULL;
        zend_bool overwrite = 0;
+       char *error = NULL;
 
        PHAR_ARCHIVE_OBJECT();
 
@@ -4408,10 +4438,10 @@ PHP_METHOD(Phar, extractTo)
        if (zval_files) {
                switch (Z_TYPE_P(zval_files)) {
                        case IS_NULL:
-                               goto all_files;
+                               filename = NULL;
+                               break;
                        case IS_STRING:
-                               filename = Z_STRVAL_P(zval_files);
-                               filename_len = Z_STRLEN_P(zval_files);
+                               filename = Z_STR_P(zval_files);
                                break;
                        case IS_ARRAY:
                                nelems = zend_hash_num_elements(Z_ARRVAL_P(zval_files));
@@ -4419,26 +4449,24 @@ PHP_METHOD(Phar, extractTo)
                                        RETURN_FALSE;
                                }
                                for (i = 0; i < nelems; i++) {
-                                       zval *zval_file;
                                        if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(zval_files), i)) != NULL) {
-                                               switch (Z_TYPE_P(zval_file)) {
-                                                       case IS_STRING:
-                                                               break;
-                                                       default:
-                                                               zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
-                                                                       "Invalid argument, array of filenames to extract contains non-string value");
-                                                               return;
-                                               }
-                                               if (NULL == (entry = zend_hash_find_ptr(&phar_obj->archive->manifest, Z_STR_P(zval_file)))) {
-                                                       zend_throw_exception_ex(phar_ce_PharException, 0,
-                                                               "Phar Error: attempted to extract non-existent file \"%s\" from phar \"%s\"", Z_STRVAL_P(zval_file), phar_obj->archive->fname);
-                                               }
-                                               if (FAILURE == phar_extract_file(overwrite, entry, pathto, (int)pathto_len, &error)) {
-                                                       zend_throw_exception_ex(phar_ce_PharException, 0,
-                                                               "Extraction from phar \"%s\" failed: %s", phar_obj->archive->fname, error);
-                                                       efree(error);
+                                               if (IS_STRING != Z_TYPE_P(zval_file)) {
+                                                       zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
+                                                               "Invalid argument, array of filenames to extract contains non-string value");
                                                        return;
                                                }
+                                               switch (extract_helper(phar_obj->archive, Z_STR_P(zval_file), pathto, pathto_len, overwrite, &error)) {
+                                                       case -1:
+                                                               zend_throw_exception_ex(phar_ce_PharException, 0, "Extraction from phar \"%s\" failed: %s",
+                                                                       phar_obj->archive->fname, error);
+                                                               efree(error);
+                                                               return;
+                                                       case 0:
+                                                               zend_throw_exception_ex(phar_ce_PharException, 0,
+                                                                       "Phar Error: attempted to extract non-existent file or directory \"%s\" from phar \"%s\"",
+                                                                       ZSTR_VAL(Z_STR_P(zval_file)), phar_obj->archive->fname);
+                                                               return;
+                                               }
                                        }
                                }
                                RETURN_TRUE;
@@ -4447,38 +4475,22 @@ PHP_METHOD(Phar, extractTo)
                                        "Invalid argument, expected a filename (string) or array of filenames");
                                return;
                }
-
-               if (NULL == (entry = zend_hash_str_find_ptr(&phar_obj->archive->manifest, filename, filename_len))) {
-                       zend_throw_exception_ex(phar_ce_PharException, 0,
-                               "Phar Error: attempted to extract non-existent file \"%s\" from phar \"%s\"", filename, phar_obj->archive->fname);
-                       return;
-               }
-
-               if (FAILURE == phar_extract_file(overwrite, entry, pathto, (int)pathto_len, &error)) {
-                       zend_throw_exception_ex(phar_ce_PharException, 0,
-                               "Extraction from phar \"%s\" failed: %s", phar_obj->archive->fname, error);
-                       efree(error);
-                       return;
-               }
        } else {
-               phar_archive_data *phar;
-all_files:
-               phar = phar_obj->archive;
-               /* Extract all files */
-               if (!zend_hash_num_elements(&(phar->manifest))) {
-                       RETURN_TRUE;
-               }
+               filename = NULL;
+       }
 
-               ZEND_HASH_FOREACH_PTR(&phar->manifest, entry) {
-                       if (FAILURE == phar_extract_file(overwrite, entry, pathto, (int)pathto_len, &error)) {
-                               zend_throw_exception_ex(phar_ce_PharException, 0,
-                                       "Extraction from phar \"%s\" failed: %s", phar->fname, error);
-                               efree(error);
-                               return;
-                       }
-               } ZEND_HASH_FOREACH_END();
+       ret = extract_helper(phar_obj->archive, filename, pathto, pathto_len, overwrite, &error);
+       if (-1 == ret) {
+               zend_throw_exception_ex(phar_ce_PharException, 0, "Extraction from phar \"%s\" failed: %s",
+                       phar_obj->archive->fname, error);
+               efree(error);
+       } else if (0 == ret && NULL != filename) {
+               zend_throw_exception_ex(phar_ce_PharException, 0,
+                       "Phar Error: attempted to extract non-existent file or directory \"%s\" from phar \"%s\"",
+                       ZSTR_VAL(filename), phar_obj->archive->fname);
+       } else {
+               RETURN_TRUE;
        }
-       RETURN_TRUE;
 }
 /* }}} */
 
diff --git a/ext/phar/tests/bug54289.phpt b/ext/phar/tests/bug54289.phpt
new file mode 100644 (file)
index 0000000..729c4e0
--- /dev/null
@@ -0,0 +1,70 @@
+--TEST--
+Bug #54289 Phar::extractTo() does not accept specific directories to be extracted
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--INI--
+phar.readonly = 0
+--FILE--
+<?php
+// put our test fixtures into a far
+$base  = __DIR__.DIRECTORY_SEPARATOR.'bug54289'.DIRECTORY_SEPARATOR;
+$inDir = $base.'in';
+$phar  = $base.'test.phar';
+$pharA = new Phar($phar);
+$pharA->buildFromDirectory($inDir);
+
+// we should be able to pull out a directory that's there, but none that share
+// the same prefix
+$outDir = $base.'out';
+$pharB = new Phar($phar);
+$pharB->extractTo($outDir, 'dirA/', true);
+var_dump(file_exists($outDir.DIRECTORY_SEPARATOR.'dirA'.DIRECTORY_SEPARATOR.'fileA'));
+var_dump(file_exists($outDir.DIRECTORY_SEPARATOR.'dirA'.DIRECTORY_SEPARATOR.'fileB'));
+var_dump(is_dir($outDir.DIRECTORY_SEPARATOR.'dirAB'));
+
+// should also not be able to pull out non-existent ones
+try {
+  $pharB->extractTo($outDir, 'dirX/', true);
+  echo 'failed to throw expected exception';
+} catch (PharException $ex) {
+}
+
+// should also not be able to pull out /, because paths are not "rooted" that way
+try {
+  $pharB->extractTo($outDir, '/', true);
+  echo 'failed to throw expected exception';
+} catch (PharException $ex) {
+}
+
+// should be able to do by array, too
+$pharB = new Phar($phar);
+$pharB->extractTo($outDir, [ 'dirA/', 'dirAB/' ], true);
+
+// but not an array with a bad member in it
+try {
+  $pharB = new Phar($phar);
+  $pharB->extractTo($outDir, [ 'dirA/', 'dirX/' ], true);
+  echo 'failed to throw expected exception';
+} catch (PharException $ex) {
+}
+
+echo 'done';
+?>
+--CLEAN--
+<?php
+$base   = __DIR__.DIRECTORY_SEPARATOR.'bug54289'.DIRECTORY_SEPARATOR;
+$phar   = $base.'test.phar';
+$outDir = $base.'out';
+unlink($phar);
+$iter = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(
+  $outDir, \FilesystemIterator::SKIP_DOTS|\FilesystemIterator::UNIX_PATHS),
+  \RecursiveIteratorIterator::CHILD_FIRST);
+foreach ($iter as $value) {
+    $value->isFile() ? unlink($value) : rmdir($value);
+}
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(false)
+done
diff --git a/ext/phar/tests/bug54289/in/dirA/fileA b/ext/phar/tests/bug54289/in/dirA/fileA
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ext/phar/tests/bug54289/in/dirA/fileB b/ext/phar/tests/bug54289/in/dirA/fileB
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ext/phar/tests/bug54289/in/dirAB/file1 b/ext/phar/tests/bug54289/in/dirAB/file1
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ext/phar/tests/bug54289/in/dirAB/file2 b/ext/phar/tests/bug54289/in/dirAB/file2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ext/phar/tests/bug54289/in/dirAB/file3 b/ext/phar/tests/bug54289/in/dirAB/file3
new file mode 100644 (file)
index 0000000..e69de29