[php-src] Issue #12661: Inconsistency in ZipArchive::addGlob 'remove_path' Option Behavior

From: Date: Mon, 13 Nov 2023 14:49:37 +0000
Subject: [php-src] Issue #12661: Inconsistency in ZipArchive::addGlob 'remove_path' Option Behavior
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-245795@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/12661 Author: MoonlitSyntax ### Description ## Steps to Reproduce I've encountered an issue with the remove_path option in the ZipArchive::addGlob method where its behavior doesn't align with the documentation. The documentation states that remove_path is used to "remove prefix from matching file paths before adding to the archive." However, it appears that remove_path matches and removes parts of the file path that are not just prefixes. ### Create a directory structure as shown below: ```php /tmp ├── Dire │ └── This_Is_A_File └── test.php ``` ### Run the following PHP script ```php <?php $zip = new ZipArchive(); $filename = "./HereIsZip.zip"; if ($zip->open($filename, ZipArchive::CREATE)!==TRUE) { exit("Wrong!!!\n"); } $dir = '/tmp/Dire'; $FirstDir = '/Dire/'; $SecondDir = 'This_Is'; $ThirdDir = '/D'; $zip->addGlob('/tmp/Dire/This_Is_A_File', 0, ['add_path' => '/', 'remove_path' => $FirstDir]); $zip->addGlob('/tmp/Dire/This_Is_A_File', 0, ['add_path' => '/', 'remove_path' => $SecondDir]); $zip->addGlob('/tmp/Dire/This_Is_A_File', 0, ['add_path' => '/', 'remove_path' => $ThirdDir]); $zip->addGlob('/tmp/Dire/This_Is_A_File', 0, ['add_path' => '/', 'remove_path' => $dir]); $zip->close(); if ($zip->open($filename) === true) { $extractPath = '/tmp'; $zip->extractTo($extractPath); $zip->close(); } else { echo "Wrong\n"; } ``` ### Observe the output directory structure: ```php /tmp ├── Dire │ └── This_Is_A_File ├── HereIsZip.zip ├── ire │ └── This_Is_A_File ├── mp │ └── Dire │ └── This_Is_A_File ├── re │ └── This_Is_A_File ├── test.php └── This_Is_A_File ``` ### Expected Result: Files are added to the archive with the specified remove_path prefix removed. ### Actual Result: The remove_path option removes parts of the file path that are not prefixes, leading to unexpected directory structures in the output. ```php $dir = '/tmp/Dire' // =>This_Is_A_File $FirstDir = '/Dire/'; // =>ire/This_Is_A_File $SecondDir = 'This_Is'; // =>re/This_Is_A_File $ThirdDir = '/D'; // =>mp/Dire/This_Is_A_File ``` ## Additional Information: This behavior is inconsistent with the documented purpose of remove_path and affects the usability of the ZipArchive::addGlob method when handling file paths. The core of the issue seems to be in the way remove_path is checked within the addGlob function. Specifically, the code zval_file = zend_hash_index_find(Z_ARRVAL_P(return_value), i)) != NULL appears to detect the presence of remove_path anywhere in the file path, not just at the beginning ```php if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(return_value), i)) != NULL) { if (opts.remove_all_path) { basename = php_basename(Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), NULL, 0); file_stripped = ZSTR_VAL(basename); file_stripped_len = ZSTR_LEN(basename); } else if (opts.remove_path && strstr(Z_STRVAL_P(zval_file), opts.remove_path) != NULL) { if (IS_SLASH(Z_STRVAL_P(zval_file)[opts.remove_path_len])) { file_stripped = Z_STRVAL_P(zval_file) + opts.remove_path_len + 1; file_stripped_len = Z_STRLEN_P(zval_file) - opts.remove_path_len - 1; } else { file_stripped = Z_STRVAL_P(zval_file) + opts.remove_path_len; file_stripped_len = Z_STRLEN_P(zval_file) - opts.remove_path_len; } } else { file_stripped = Z_STRVAL_P(zval_file); file_stripped_len = Z_STRLEN_P(zval_file); } ``` ### PHP Version PHP 8.2.12 ### Operating System Arch Linux

« previous php.bugs (#245795) next »