MDL-70864 files: Fix zip_packer extracting files with trailing dots

File and folder names cannot end with dots on Windows. So replace the
trailing dots with underscore consistently with how some other zip tools
(such as 7-zip) handle this case.
This commit is contained in:
David Mudrák 2021-04-19 21:17:56 +02:00
parent 0a986fdf13
commit 260c2bdb00
2 changed files with 34 additions and 4 deletions

View File

@ -260,6 +260,8 @@ class core_files_zip_packer_testcase extends advanced_testcase implements file_p
* @link https://bugs.php.net/bug.php?id=77214
*/
public function test_zip_entry_path_having_folder_ending_with_dot() {
global $CFG;
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
@ -276,6 +278,28 @@ class core_files_zip_packer_testcase extends advanced_testcase implements file_p
'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt' => [''],
];
if ($CFG->ostype === 'WINDOWS') {
// File names cannot end with dots on Windows and trailing dots are replaced with underscore.
$filenamemap = [
'HOW.TO' => 'HOW.TO',
'README.' => 'README_',
'./Current time' => 'Current time',
'Data/sub1./sub2/1221' => 'Data/sub1_/sub2/1221',
'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt' =>
'Data/sub1_/sub2_/Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt',
];
} else {
$filenamemap = [
'HOW.TO' => 'HOW.TO',
'README.' => 'README.',
'./Current time' => 'Current time',
'Data/sub1./sub2/1221' => 'Data/sub1./sub2/1221',
'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt' =>
'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt',
];
}
// Check that the archive can be created.
$result = $packer->archive_to_pathname($zipcontents, $zippath, false);
$this->assertTrue($result);
@ -298,8 +322,8 @@ class core_files_zip_packer_testcase extends advanced_testcase implements file_p
foreach ($zipcontents as $filename => $filecontents) {
$filecontents = reset($filecontents);
$this->assertTrue(is_readable($targetpath . '/' . $filename));
$this->assertEquals($filecontents, file_get_contents($targetpath . '/' . $filename));
$this->assertTrue(is_readable($targetpath . '/' . $filenamemap[$filename]));
$this->assertEquals($filecontents, file_get_contents($targetpath . '/' . $filenamemap[$filename]));
}
}

View File

@ -297,11 +297,17 @@ class zip_packer extends file_packer {
$size = $info->size;
$name = $info->pathname;
$origname = $name;
// File names cannot end with dots on Windows and trailing dots are replaced with underscore.
if ($CFG->ostype === 'WINDOWS') {
$name = preg_replace('~([^/]+)\.(/|$)~', '\1_\2', $name);
}
if ($name === '' or array_key_exists($name, $processed)) {
// Probably filename collisions caused by filename cleaning/conversion.
continue;
} else if (is_array($onlyfiles) && !in_array($name, $onlyfiles)) {
} else if (is_array($onlyfiles) && !in_array($origname, $onlyfiles)) {
// Skipping files which are not in the list.
continue;
}
@ -342,7 +348,7 @@ class zip_packer extends file_packer {
$newfile = "$newdir/$filename";
if (strpos($newfile, './') > 1) {
if (strpos($newfile, './') > 1 || $name !== $origname) {
// The path to the entry contains a directory ending with dot. We cannot use extract_to() due to
// upstream PHP bugs #69477, #74619 and #77214. Extract the file from its stream which is slower but
// should work even in this case.