From ed507dec9fa898cef3dfa80b779acc03eedfdecd Mon Sep 17 00:00:00 2001 From: hakre Date: Thu, 24 Jul 2014 14:17:42 +0200 Subject: [PATCH 1/2] added test unlinking directory #3157 --- tests/Composer/Test/Util/FilesystemTest.php | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/Composer/Test/Util/FilesystemTest.php b/tests/Composer/Test/Util/FilesystemTest.php index c74e84a5f..bffea7434 100644 --- a/tests/Composer/Test/Util/FilesystemTest.php +++ b/tests/Composer/Test/Util/FilesystemTest.php @@ -176,4 +176,31 @@ class FilesystemTest extends TestCase array('phar://c:../Foo', 'phar://c:../Foo'), ); } + + /** + * @link https://github.com/composer/composer/issues/3157 + */ + public function testUnlinkSymlinkedDirectory() + { + $tmp = sys_get_temp_dir(); + $basepath = $tmp . "/composer_testdir"; + $symlinked = $basepath . "/linked"; + @mkdir($basepath . "/real", 0777, true); + touch($basepath . "/real/FILE"); + + $result = @symlink($basepath . "/real", $symlinked); + + if (!$result) { + $this->markTestSkipped('Symbolic links for directories not supported on this platform'); + } + + if (!is_dir($symlinked)) { + $this->fail('Precondition assertion failed (is_dir is false on symbolic link to directory).'); + } + + $fs = new Filesystem(); + $result = $fs->unlink($symlinked); + $this->assertTrue($result); + $this->assertFalse(file_exists($symlinked)); + } } From 0ad2449fe8d7c6a0295626c285d55466847ecedb Mon Sep 17 00:00:00 2001 From: hakre Date: Mon, 28 Jul 2014 23:42:53 +0200 Subject: [PATCH 2/2] rmdir needs to be used on windows to remove symbolic links to directories --- src/Composer/Util/Filesystem.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 182004205..7cc463525 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -181,9 +181,9 @@ class Filesystem */ public function unlink($path) { - if (!@unlink($path)) { + if (!@$this->unlinkImplementation($path)) { // retry after a bit on windows since it tends to be touchy with mass removals - if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@unlink($path))) { + if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@$this->unlinkImplementation($path))) { $error = error_get_last(); $message = 'Could not delete '.$path.': ' . @$error['message']; if (defined('PHP_WINDOWS_VERSION_BUILD')) { @@ -473,4 +473,22 @@ class Filesystem { return new ProcessExecutor; } + + /** + * delete symbolic link implementation (commonly known as "unlink()") + * + * symbolic links on windows which link to directories need rmdir instead of unlink + * + * @param string $path + * + * @return bool + */ + private function unlinkImplementation($path) + { + if (defined('PHP_WINDOWS_VERSION_BUILD') && is_dir($path) && is_link($path)) { + return rmdir($path); + } + + return unlink($path); + } }