From f486da45787a03f3f92208b47b7ea7d540b1ab65 Mon Sep 17 00:00:00 2001 From: Jason Stephens Date: Fri, 30 Oct 2020 17:10:24 -0500 Subject: [PATCH] Remove junctions first in PathDownloader When removing a package from a Path repository, ensure Windows junctions are handled first. This avoids a potential problem using realpath() when comparing dist and install paths. See https://bugs.php.net/bug.php?id=77639 --- src/Composer/Downloader/PathDownloader.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Composer/Downloader/PathDownloader.php b/src/Composer/Downloader/PathDownloader.php index 94c6fb71b..21d0bcb45 100644 --- a/src/Composer/Downloader/PathDownloader.php +++ b/src/Composer/Downloader/PathDownloader.php @@ -178,17 +178,11 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter */ public function remove(PackageInterface $package, $path, $output = true) { - $realUrl = realpath($package->getDistUrl()); - - if (realpath($path) === $realUrl) { - if ($output) { - $this->io->writeError(" - " . UninstallOperation::format($package).", source is still present in $path"); - } - - return; - } - /** + * realpath() may resolve Windows junctions to the source path, so we'll check for a junction first + * to prevent a false positive when checking if the dist and install paths are the same. + * See https://bugs.php.net/bug.php?id=77639 + * * For junctions don't blindly rely on Filesystem::removeDirectory as it may be overzealous. If a process * inadvertently locks the file the removal will fail, but it would fall back to recursive delete which * is disastrous within a junction. So in that case we have no other real choice but to fail hard. @@ -201,6 +195,10 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter $this->io->writeError(" Could not remove junction at " . $path . " - is another process locking it?"); throw new \RuntimeException('Could not reliably remove junction for package ' . $package->getName()); } + } elseif (realpath($path) === realpath($package->getDistUrl())) { + if ($output) { + $this->io->writeError(" - " . UninstallOperation::format($package).", source is still present in $path"); + } } else { parent::remove($package, $path, $output); }