Skip to content

Commit

Permalink
MDL-70864 files: Fix zip_packer extracting files with trailing dots
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mudrd8mz committed May 3, 2021
1 parent 6f48b4a commit d95c3a0
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 4 deletions.
28 changes: 26 additions & 2 deletions lib/filestorage/tests/zip_packer_test.php
Expand Up @@ -260,6 +260,8 @@ public function test_extract_to_pathname() {
* @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');
Expand All @@ -276,6 +278,28 @@ public function test_zip_entry_path_having_folder_ending_with_dot() {
'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);
Expand All @@ -298,8 +322,8 @@ public function test_zip_entry_path_having_folder_ending_with_dot() {

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]));
}
}

Expand Down
10 changes: 8 additions & 2 deletions lib/filestorage/zip_packer.php
Expand Up @@ -297,11 +297,17 @@ public function extract_to_pathname($archivefile, $pathname,

$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;
}
Expand Down Expand Up @@ -342,7 +348,7 @@ public function extract_to_pathname($archivefile, $pathname,

$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.
Expand Down

0 comments on commit d95c3a0

Please sign in to comment.