diff --git a/src/Model/ZipEntry.php b/src/Model/ZipEntry.php index 5194fc0..dd3015e 100644 --- a/src/Model/ZipEntry.php +++ b/src/Model/ZipEntry.php @@ -1447,7 +1447,7 @@ public function getUnixMode() return $mode; } - return $this->isDirectory ? 040755 : 0100644; + return $this->isDirectory && !$this->isUnixSymlink() ? 040755 : 0100644; } /** diff --git a/src/Util/FilesUtil.php b/src/Util/FilesUtil.php index 3e64614..cf7a4d4 100644 --- a/src/Util/FilesUtil.php +++ b/src/Util/FilesUtil.php @@ -45,7 +45,7 @@ public static function removeDir($dir) /** @var \SplFileInfo $fileInfo */ foreach ($files as $fileInfo) { - $function = ($fileInfo->isDir() ? 'rmdir' : 'unlink'); + $function = ($fileInfo->isDir() && !$fileInfo->isLink() ? 'rmdir' : 'unlink'); $function($fileInfo->getPathname()); } @rmdir($dir); diff --git a/src/ZipFile.php b/src/ZipFile.php index 384c03f..e3ebab0 100644 --- a/src/ZipFile.php +++ b/src/ZipFile.php @@ -688,7 +688,7 @@ public function addSplFile(\SplFileInfo $file, $entryName = null, array $options } $entryName = $this->normalizeEntryName($entryName); - $entryName = $file->isDir() ? rtrim($entryName, '/\\') . '/' : $entryName; + $entryName = $file->isDir() && !$file->isLink() ? rtrim($entryName, '/\\') . '/' : $entryName; $zipEntry = new ZipEntry($entryName); $zipEntry->setCreatedOS(ZipPlatform::OS_UNIX); @@ -706,6 +706,7 @@ public function addSplFile(\SplFileInfo $file, $entryName = null, array $options $zipEntry->setCompressedSize($lengthLinkTarget); $zipEntry->setCrc(crc32($linkTarget)); $filePerms |= UnixStat::UNX_IFLNK; + $filePerms &= ~UnixStat::UNX_IFDIR; $zipData = new ZipNewData($zipEntry, $linkTarget); } elseif ($file->isFile()) { diff --git a/tests/SymlinkTest.php b/tests/SymlinkTest.php index f7d3b7a..bf4e461 100644 --- a/tests/SymlinkTest.php +++ b/tests/SymlinkTest.php @@ -14,6 +14,8 @@ */ final class SymlinkTest extends ZipTestCase { + + /** * @dataProvider provideAllowSymlink * @@ -67,6 +69,66 @@ public function testSymlink($allowSymlink) } } + /** + * @dataProvider provideAllowSymlink + * + * @param bool $allowSymlink + * + * @throws \Exception + */ + public function testSymlinkedDirectory($allowSymlink) + { + if (self::skipTestForWindows()) { + return; + } + + if (!is_dir($this->outputDirname)) { + self::assertTrue(mkdir($this->outputDirname, 0755, true)); + } + + $dirToBeLinked = $this->outputDirname . '/dir-to-be-linked'; + self::assertTrue(mkdir($dirToBeLinked, 0755, true)); + + $contentsFile = random_bytes(100); + $filePath = $dirToBeLinked . '/file.bin'; + self::assertNotFalse(file_put_contents($filePath, $contentsFile)); + $symlinkPath = $this->outputDirname . '/symlink.dir'; + $symlinkTarget = basename($dirToBeLinked); + self::assertTrue(symlink($symlinkTarget, $symlinkPath)); + + $finder = (new Finder())->in($this->outputDirname); + $zipFile = new ZipFile(); + $zipFile->addFromFinder($finder); + $zipFile->saveAsFile($this->outputFilename); + $zipFile->close(); + + self::assertCorrectZipArchive($this->outputFilename); + + FilesUtil::removeDir($this->outputDirname); + self::assertFalse(is_dir($this->outputDirname)); + self::assertTrue(mkdir($this->outputDirname, 0755, true)); + + $zipFile->openFile($this->outputFilename); + $zipFile->extractTo($this->outputDirname, null, [ + ZipOptions::EXTRACT_SYMLINKS => $allowSymlink, + ]); + $zipFile->close(); + + $splFileInfo = new \SplFileInfo($symlinkPath); + + if ($allowSymlink) { + self::assertTrue($splFileInfo->isLink()); + self::assertSame($splFileInfo->getLinkTarget(), $symlinkTarget); + $linkedFilename = $symlinkPath."/".basename($filePath); + self::assertFileExists($linkedFilename); + $linkedFileContents = file_get_contents($linkedFilename); + self::assertEquals($contentsFile, $linkedFileContents); + } else { + self::assertFalse($splFileInfo->isLink()); + self::assertStringEqualsFile($symlinkPath, $symlinkTarget); + } + } + /** * @return \Generator */