diff options
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java | 10 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java | 218 |
2 files changed, 5 insertions, 223 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java index eb3e6a67a1..dc4920d1dc 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java @@ -16,9 +16,7 @@ package com.google.devtools.build.lib.runtime; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.eventbus.SubscriberExceptionContext; import com.google.common.eventbus.SubscriberExceptionHandler; @@ -905,14 +903,6 @@ public final class BlazeRuntime { workspaceDirectoryPath = fs.getPath(workspaceDirectory); } - if (fs instanceof UnixFileSystem) { - ((UnixFileSystem) fs).setRootsWithAllowedHardlinks( - // Some tests pass nulls for these paths, so remove these from the list - Iterables.filter( - Arrays.asList(installBasePath, outputBasePath, workspaceDirectoryPath), - Predicates.notNull())); - } - ServerDirectories serverDirectories = new ServerDirectories(installBasePath, outputBasePath, startupOptions.installMD5); Clock clock = BlazeClock.instance(); diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java index 790818ecc9..ce7ee93bab 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java @@ -14,8 +14,6 @@ package com.google.devtools.build.lib.vfs; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; import com.google.devtools.build.lib.profiler.Profiler; @@ -25,101 +23,17 @@ import com.google.devtools.build.lib.unix.NativePosixFiles; import com.google.devtools.build.lib.unix.NativePosixFiles.Dirents; import com.google.devtools.build.lib.unix.NativePosixFiles.ReadTypes; import com.google.devtools.build.lib.util.Preconditions; - import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Random; /** - * This class implements the FileSystem interface using direct calls to the - * UNIX filesystem. + * This class implements the FileSystem interface using direct calls to the UNIX filesystem. */ -// Not final only for testing. @ThreadSafe public class UnixFileSystem extends AbstractFileSystemWithCustomStat { - /** - * What to do with requests to create symbolic links. - * - * Currently supports one value: SYMLINK, which simply calls symlink() . It obviously does not - * work on Windows. - */ - public enum SymlinkStrategy { - /** - * Use symlink(). Does not work on Windows, obviously. - */ - SYMLINK, - - /** - * Write a log message for symlinks that won't be compatible with how we are planning to pretend - * that they exist on Windows. - * - * <p>The current plan for emulating symlinks on Windows is that in order to create a "symlink", - * the target needs to exist, that is, we don't do dangling symlinks. Then: - * </p> - * - * <ul> - * <li>If the target is a directory, we create a junction. This is good because we don't need - * write access to the target and it Just Works. The link and its target can be on different - * file systems, which is important, because contrary to the popular belief, *can* do a - * mount() on Windows - * </li> - * <li>If the target is a file in the source tree or under the output base, we use a hard - * link. Hard links only work within the same file system and you need write access to the - * target. We assume that the source tree is writable, and we know that the output base is. - * </li> - * <li>If the target is a file not in one of these locations, we raise an error. The only - * places where we need to do this is in the implementation of local repository rules, - * which will be special-cased.</li> - * </ul> - * - * <p>What does <b>not</b> work is using symbolic links: they need local administrator rights, - * which would make Bazel only usable as local admin. - * </p> - */ - WINDOWS_COMPATIBLE, - } - - private final SymlinkStrategy symlinkStrategy; - private final String symlinkLogFile; - - /** - * Directories where Bazel tries to hardlink files from instead of copying them. - * - * <p>These must be writable to the user. - */ - private ImmutableList<Path> rootsWithAllowedHardlinks; - public UnixFileSystem() { - SymlinkStrategy symlinkStrategy = SymlinkStrategy.SYMLINK; - String strategyString = System.getProperty("io.bazel.SymlinkStrategy"); - symlinkLogFile = System.getProperty("io.bazel.SymlinkLogFile"); - if (strategyString != null) { - try { - symlinkStrategy = SymlinkStrategy.valueOf(strategyString.toUpperCase()); - } catch (IllegalArgumentException e) { - // We just go with the default, this is just an experimental option so it's fine. - } - - if (symlinkLogFile != null) { - writeLogMessage("Logging started"); - } - } - - this.symlinkStrategy = symlinkStrategy; - rootsWithAllowedHardlinks = ImmutableList.of(); - } - - // This method is a little ugly, but it's only for testing for now. - public void setRootsWithAllowedHardlinks(Iterable<Path> roots) { - this.rootsWithAllowedHardlinks = ImmutableList.copyOf(roots); } /** @@ -204,7 +118,7 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } /** - * Converts from {@link NativePosixFiles.Dirents.Type} to + * Converts from {@link com.google.devtools.build.lib.unix.NativePosixFiles.Dirents.Type} to * {@link com.google.devtools.build.lib.vfs.Dirent.Type}. */ private static Dirent.Type convertToDirentType(Dirents.Type type) { @@ -371,7 +285,7 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { @Override public boolean supportsSymbolicLinksNatively() { - return symlinkStrategy != SymlinkStrategy.WINDOWS_COMPATIBLE; + return true; } @Override @@ -400,133 +314,11 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { @Override protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException { - SymlinkImplementation strategy = computeSymlinkImplementation(linkPath, targetFragment); - switch (strategy) { - case HARDLINK: - NativePosixFiles.link(targetFragment.toString(), linkPath.toString()); - break; - - case JUNCTION: // Junctions are emulated on Linux with symlinks, fall through - case SYMLINK: - synchronized (linkPath) { - NativePosixFiles.symlink(targetFragment.toString(), linkPath.toString()); - } - break; - - case FAIL: - if (symlinkLogFile == null) { - // Otherwise, it was logged in computeSymlinkImplementation(). - throw new IOException(String.format("Symlink emulation failed for symlink: %s -> %s", - linkPath, targetFragment)); - } - } - } - - private boolean isHardLinkAllowed(Path path) { - for (Path root : rootsWithAllowedHardlinks) { - if (path.startsWith(root)) { - return true; - } - } - - return false; - } - - private static final int JVM_ID = new Random().nextInt(10000); - - private void writeLogMessage(String message) { - String logLine = String.format("[%04d] %s\n", JVM_ID, message); - // FileLock does not work for synchronization between threads in the same JVM as per its Javadoc - synchronized (symlinkLogFile) { - - try (FileChannel channel = new RandomAccessFile(symlinkLogFile, "rwd").getChannel()) { - try (FileLock lock = channel.lock()) { - channel.position(channel.size()); - ByteBuffer data = Charset.forName("UTF-8").newEncoder().encode(CharBuffer.wrap(logLine)); - channel.write(data); - } - } catch (IOException e) { - // Not much intelligent we can do here - } + synchronized (linkPath) { + NativePosixFiles.symlink(targetFragment.toString(), linkPath.toString()); } } - /** - * How to create a particular symbolic link. - * - * <p>Necessary because Windows doesn't support symlinks properly, so we have to work around it. - * No, even though they say <i>"Microsoft has implemented its symbolic links to function just like - * UNIX links"</i>, it's a lie. - */ - private enum SymlinkImplementation { - /** - * We can't emulate this link. Fail. - */ - FAIL, - - /** - * Create a hard link. This only works if we have write access to the ultimate destination on - * the link. - */ - HARDLINK, - - /** - * Create a junction. This only works if the ultimate target of the "symlink" is a directory. - */ - JUNCTION, - - /** - * Use a symlink. Always works, but only on Unix-based operating systems. - */ - SYMLINK, - } - - private SymlinkImplementation emitSymlinkCompatibilityMessage( - String reason, Path linkPath, PathFragment targetFragment) { - if (symlinkLogFile == null) { - return SymlinkImplementation.FAIL; - } - - Exception e = new Exception(); - e.fillInStackTrace(); - String msg = String.format("ILLEGAL (%s): %s -> %s\nStack:\n%s", - reason, linkPath.getPathString(), targetFragment.getPathString(), - Throwables.getStackTraceAsString(e)); - writeLogMessage(msg); - return SymlinkImplementation.SYMLINK; // We are in logging mode, pretend everything is A-OK - } - - private SymlinkImplementation computeSymlinkImplementation( - Path linkPath, PathFragment targetFragment) throws IOException { - if (symlinkStrategy != SymlinkStrategy.WINDOWS_COMPATIBLE) { - return SymlinkImplementation.SYMLINK; - } - - Path targetPath = linkPath.getRelative(targetFragment); - if (!targetPath.exists(Symlinks.FOLLOW)) { - return emitSymlinkCompatibilityMessage( - "Target does not exist", linkPath, targetFragment); - } - - targetPath = targetPath.resolveSymbolicLinks(); - if (targetPath.isDirectory(Symlinks.FOLLOW)) { - // We can create junctions to any directory. - return SymlinkImplementation.JUNCTION; - } - - if (isHardLinkAllowed(targetPath)) { - // We have write access to the destination and it's a file, so we can do this - return SymlinkImplementation.HARDLINK; - } - - return emitSymlinkCompatibilityMessage( - "Target is a non-writable file", linkPath, targetFragment); - } - - public SymlinkStrategy getSymlinkStrategy() { - return symlinkStrategy; - } - @Override protected PathFragment readSymbolicLink(Path path) throws IOException { // Note that the default implementation of readSymbolicLinkUnchecked calls this method and thus |