|
@@ -14,6 +14,7 @@ namespace Composer\Util;
|
|
|
|
|
|
use RecursiveDirectoryIterator;
|
|
|
use RecursiveIteratorIterator;
|
|
|
+use Symfony\Component\Filesystem\Exception\IOException;
|
|
|
use Symfony\Component\Finder\Finder;
|
|
|
|
|
|
/**
|
|
@@ -98,6 +99,10 @@ class Filesystem
|
|
|
return $this->unlinkSymlinkedDirectory($directory);
|
|
|
}
|
|
|
|
|
|
+ if ($this->isJunction($directory)) {
|
|
|
+ return $this->removeJunction($directory);
|
|
|
+ }
|
|
|
+
|
|
|
if (!file_exists($directory) || !is_dir($directory)) {
|
|
|
return true;
|
|
|
}
|
|
@@ -576,4 +581,64 @@ class Filesystem
|
|
|
|
|
|
return $resolved;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creates an NTFS junction.
|
|
|
+ *
|
|
|
+ * @param string $target
|
|
|
+ * @param string $junction
|
|
|
+ */
|
|
|
+ public function junction($target, $junction)
|
|
|
+ {
|
|
|
+ if (!Platform::isWindows()) {
|
|
|
+ throw new \LogicException(sprintf('Function %s is not available on non-Windows platform', __CLASS__));
|
|
|
+ }
|
|
|
+ if (!is_dir($target)) {
|
|
|
+ throw new IOException(sprintf('Cannot junction to "%s" as it is not a directory.', $target), 0, null, $target);
|
|
|
+ }
|
|
|
+ $cmd = sprintf('mklink /J %s %s',
|
|
|
+ ProcessExecutor::escape(str_replace('/', DIRECTORY_SEPARATOR, $junction)),
|
|
|
+ ProcessExecutor::escape(realpath($target)));
|
|
|
+ if ($this->getProcess()->execute($cmd, $output) !== 0) {
|
|
|
+ throw new IOException(sprintf('Failed to create junction to "%s" at "%s".', $target, $junction), 0, null, $target);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns whether the target directory is a Windows NTFS Junction.
|
|
|
+ *
|
|
|
+ * @param string $junction Path to check.
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function isJunction($junction)
|
|
|
+ {
|
|
|
+ if (!Platform::isWindows()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!is_dir($junction) || is_link($junction)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // Junctions have no link stat but are otherwise indistinguishable from real directories
|
|
|
+ $stat = lstat($junction);
|
|
|
+ return ($stat['mode'] === 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Removes a Windows NTFS junction.
|
|
|
+ *
|
|
|
+ * @param string $junction
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function removeJunction($junction)
|
|
|
+ {
|
|
|
+ if (!Platform::isWindows()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $junction = rtrim(str_replace('/', DIRECTORY_SEPARATOR, $junction), DIRECTORY_SEPARATOR);
|
|
|
+ if (!$this->isJunction($junction)) {
|
|
|
+ throw new IOException(sprintf('%s is not a junction and thus cannot be removed as one', $junction));
|
|
|
+ }
|
|
|
+ $cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape($junction));
|
|
|
+ return ($this->getProcess()->execute($cmd, $output) === 0);
|
|
|
+ }
|
|
|
}
|