diff --git a/src/Composer/Downloader/PathDownloader.php b/src/Composer/Downloader/PathDownloader.php
index 1156ff62d..afb3e5de3 100644
--- a/src/Composer/Downloader/PathDownloader.php
+++ b/src/Composer/Downloader/PathDownloader.php
@@ -101,4 +101,25 @@ class PathDownloader extends FileDownloader
$this->io->writeError('');
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public function remove(PackageInterface $package, $path)
+ {
+ /**
+ * 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.
+ */
+ if (Platform::isWindows() && $this->filesystem->isJunction($path)) {
+ $this->io->writeError(" - Removing junction for " . $package->getName() . " (" . $package->getFullPrettyVersion() . ")");
+ if (!$this->filesystem->removeJunction($path)) {
+ $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());
+ }
+ } else {
+ parent::remove($package, $path);
+ }
+ }
}
diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php
index 94044756e..463fac1e7 100644
--- a/src/Composer/Util/Filesystem.php
+++ b/src/Composer/Util/Filesystem.php
@@ -618,10 +618,17 @@ class Filesystem
if (!is_dir($junction) || is_link($junction)) {
return false;
}
- // Junctions have no link stat but are otherwise indistinguishable from real directories
+ /**
+ * According to MSDN at https://msdn.microsoft.com/en-us/library/14h5k7ff.aspx we can detect a junction now
+ * using the 'mode' value from stat: "The _S_IFDIR bit is set if path specifies a directory; the _S_IFREG bit
+ * is set if path specifies an ordinary file or a device." We have just tested for a directory above, so if
+ * we have a directory that isn't one according to lstat(...) we must have a junction.
+ *
+ * #define _S_IFDIR 0x4000
+ * #define _S_IFREG 0x8000
+ */
$stat = lstat($junction);
-
- return ($stat['mode'] === 0);
+ return !($stat['mode'] & 0xC000);
}
/**