aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar tomlu <tomlu@google.com>2017-10-20 17:27:11 +0200
committerGravatar Dmitry Lomov <dslomov@google.com>2017-10-23 17:15:55 +0200
commit0b2f94d38f2d6cf27a47ce2e66e2423ccef770ce (patch)
treee49dfca6af8a51d6da8c824770546b886f1732d0 /src
parent1a4f4264492a0b37a4132d629342aa961fa1c6b0 (diff)
Remove scope escaping capability from InMemoryFileSystem.
Nobody sets a delegate anymore, meaning it should be fine to crash hard if we try to resolve a file outside scope. The CL was created by inlining ScopeEscapableFileSystem into InMemoryFileSystem, making it impossible to return an outOfScope file status (instead throwing an exception), then trivially fixing the code. PiperOrigin-RevId: 172886769
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystem.java143
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java25
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java516
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/OutOfScopeDirectoryStatus.java123
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/OutOfScopeFileStatus.java110
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/ScopeEscapableStatus.java46
-rw-r--r--src/test/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystemTest.java818
-rw-r--r--src/test/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystemTest.java12
8 files changed, 188 insertions, 1605 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystem.java
deleted file mode 100644
index c3634d7bf6..0000000000
--- a/src/main/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystem.java
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.devtools.build.lib.vfs;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadHostile;
-import com.google.devtools.build.lib.util.Preconditions;
-
-import java.io.IOException;
-
-/**
- * A file system that's capable of identifying paths residing outside its scope
- * and using a delegator (such as {@link UnionFileSystem}) to re-route them
- * to appropriate alternative file systems.
- *
- * <p>This is most useful for symlinks, which may ostensibly fall beneath some
- * file system but resolve to paths outside that file system.
- *
- * <p>Note that we don't protect against cross-filesystem circular references.
- * Therefore, care should be taken not to mix two scopable file systems that
- * can reference each other. This theoretical safety cost is balanced by
- * decreased code complexity requirements in implementations.
- */
-public abstract class ScopeEscapableFileSystem extends FileSystem {
-
- private FileSystem delegator;
- protected final PathFragment scopeRoot;
- private boolean enableScopeChecking = true; // Used for testing.
-
- /**
- * Instantiates a new ScopeEscapableFileSystem.
- *
- * @param scopeRoot the root path for the file system's scope. Any path
- * that isn't beneath this one is considered out of scope according
- * to {@link #inScope}. If null, scope checking is disabled. Note
- * this is not the same thing as {@link FileSystem#rootPath}, which
- * generally resolves to "/".
- */
- protected ScopeEscapableFileSystem(PathFragment scopeRoot) {
- this.scopeRoot = scopeRoot;
- }
-
- @VisibleForTesting
- void enableScopeChecking(boolean enable) {
- this.enableScopeChecking = enable;
- }
-
- /**
- * Sets the delegator used to resolve paths that fall outside this file
- * system's scope.
- *
- * <p>This method is not thread safe. It's intended to be called during
- * instance initialization, not during active usage. The only reason this
- * isn't set as immutable state within the constructor is that the delegator
- * may need a reference to this instance for its own constructor.
- */
- @ThreadHostile
- public void setDelegator(FileSystem delegator) {
- this.delegator = delegator;
- }
-
- /**
- * Uses the delegator to convert a path fragment to a path that's bound
- * to the file system that manages that path.
- */
- protected Path getDelegatedPath(PathFragment path) {
- Preconditions.checkState(delegator != null);
- return delegator.getPath(path);
- }
-
- /**
- * Proxy for {@link FileSystem#resolveOneLink} that sends the input path
- * through the delegator.
- */
- protected PathFragment resolveOneLinkWithDelegator(final PathFragment path) throws IOException {
- Preconditions.checkState(delegator != null);
- return delegator.resolveOneLink(getDelegatedPath(path));
- }
-
- /**
- * Proxy for {@link FileSystem#stat} that sends the input path through
- * the delegator.
- */
- protected FileStatus statWithDelegator(final PathFragment path, final boolean followSymlinks)
- throws IOException {
- Preconditions.checkState(delegator != null);
- return delegator.stat(getDelegatedPath(path), followSymlinks);
- }
-
- /**
- * Returns true if the given path is within this file system's scope, false
- * otherwise.
- *
- * @param parentDepth the number of segments in the path's parent directory
- * (only meaningful for paths that begin with ".."). The parent directory
- * itself is assumed to be in scope.
- * @param normalizedPath input path, expected to be normalized such that all
- * ".." and "." segments are removed (with the exception of a possible
- * prefix sequence of contiguous ".." segments)
- */
- protected boolean inScope(int parentDepth, PathFragment normalizedPath) {
- if (scopeRoot == null || !enableScopeChecking) {
- return true;
- } else if (normalizedPath.isAbsolute()) {
- return normalizedPath.startsWith(scopeRoot);
- } else {
- // Efficiency note: we're not accounting for "/scope/root/../root" paths here, i.e. paths
- // that appear to go out of scope but ultimately stay within scope. This may result in
- // unnecessary re-delegation back into the same FS. we're choosing to forgo that
- // optimization under the assumption that such scenarios are rare and unimportant to
- // overall performance. We can always enhance this if needed.
- return parentDepth - leadingParentReferences(normalizedPath) >= scopeRoot.segmentCount();
- }
- }
-
- /**
- * Given a path that's normalized (no ".." or "." segments), except for a possible
- * prefix sequence of contiguous ".." segments, returns the size of that prefix
- * sequence.
- *
- * <p>Example allowed inputs: "/absolute/path", "relative/path", "../../relative/path".
- * Example disallowed inputs: "/absolute/path/../path2", "relative/../path", "../relative/../p".
- */
- protected int leadingParentReferences(PathFragment normalizedPath) {
- int leadingParentReferences = 0;
- for (int i = 0; i < normalizedPath.segmentCount() &&
- normalizedPath.getSegment(i).equals(".."); i++) {
- leadingParentReferences++;
- }
- return leadingParentReferences;
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java
index 16c0f89380..b9a06ac213 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java
@@ -16,21 +16,20 @@ package com.google.devtools.build.lib.vfs.inmemoryfs;
import com.google.devtools.build.lib.clock.Clock;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.vfs.FileStatus;
import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
/**
- * This interface defines the function directly supported by the "files" stored
- * in a InMemoryFileSystem. This corresponds to a file or inode in UNIX: it
- * doesn't have a path (it could have many paths due to hard links, or none if
- * it's unlinked, i.e. garbage).
+ * This interface defines the function directly supported by the "files" stored in a
+ * InMemoryFileSystem. This corresponds to a file or inode in UNIX: it doesn't have a path (it could
+ * have many paths due to hard links, or none if it's unlinked, i.e. garbage).
*
- * <p>This class is thread-safe: instances may be accessed and modified from
- * concurrent threads. Subclasses must preserve this property.
+ * <p>This class is thread-safe: instances may be accessed and modified from concurrent threads.
+ * Subclasses must preserve this property.
*/
@ThreadSafe
-public abstract class InMemoryContentInfo implements ScopeEscapableStatus {
+public abstract class InMemoryContentInfo implements FileStatus {
private final Clock clock;
@@ -196,16 +195,6 @@ public abstract class InMemoryContentInfo implements ScopeEscapableStatus {
return isExecutable;
}
- @Override
- public boolean outOfScope() {
- return false;
- }
-
- @Override
- public PathFragment getEscapingPath() {
- return null;
- }
-
/**
* Called just before this inode is moved.
*
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java
index b3e59d18b0..d2db9967a1 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java
@@ -20,10 +20,9 @@ import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.FileAccessException;
import com.google.devtools.build.lib.vfs.FileStatus;
+import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
-import com.google.devtools.build.lib.vfs.ScopeEscapableFileSystem;
-import com.google.devtools.build.lib.vfs.Symlinks;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -38,26 +37,18 @@ import javax.annotation.Nullable;
/**
* This class provides a complete in-memory file system.
*
- * <p>Naming convention: we use "path" for all {@link Path} variables, since these
- * represent *names* and we use "node" or "inode" for InMemoryContentInfo
- * variables, since these correspond to inodes in the UNIX file system.
+ * <p>Naming convention: we use "path" for all {@link Path} variables, since these represent *names*
+ * and we use "node" or "inode" for InMemoryContentInfo variables, since these correspond to inodes
+ * in the UNIX file system.
*
- * <p>The code is structured to be as similar to the implementation of UNIX "namei"
- * as is reasonably possibly. This provides a firm reference point for many
- * concepts and makes compatibility easier to achieve.
- *
- * <p>As a scope-escapable file system, this class supports re-delegation of symbolic links
- * that escape its root. This is done through the use of {@link OutOfScopeFileStatus}
- * and {@link OutOfScopeDirectoryStatus} objects, which may be returned by
- * getDirectory, pathWalk, and scopeLimitedStat. Any code that calls one of these
- * methods (either directly or indirectly) is obligated to check the possibility
- * that its info represents an out-of-scope path. Lack of such a check will result
- * in unchecked runtime exceptions upon any request for status data (as well as
- * possible logical errors).
+ * <p>The code is structured to be as similar to the implementation of UNIX "namei" as is reasonably
+ * possibly. This provides a firm reference point for many concepts and makes compatibility easier
+ * to achieve.
*/
@ThreadSafe
-public class InMemoryFileSystem extends ScopeEscapableFileSystem {
+public class InMemoryFileSystem extends FileSystem {
+ private final PathFragment scopeRoot;
private final Clock clock;
// The root inode (a directory).
@@ -83,12 +74,11 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
}
/**
- * Creates a new InMemoryFileSystem with scope checking bound to
- * scopeRoot, i.e. any path that's not below scopeRoot is considered
- * to be out of scope.
+ * Creates a new InMemoryFileSystem with scope checking bound to scopeRoot, i.e. any path that's
+ * not below scopeRoot is considered to be out of scope.
*/
- protected InMemoryFileSystem(Clock clock, PathFragment scopeRoot) {
- super(scopeRoot);
+ public InMemoryFileSystem(Clock clock, PathFragment scopeRoot) {
+ this.scopeRoot = scopeRoot;
this.clock = clock;
this.rootInode = new InMemoryDirectoryInfo(clock);
rootInode.addChild(".", rootInode);
@@ -96,6 +86,46 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
}
/**
+ * Returns true if the given path is within this file system's scope, false otherwise.
+ *
+ * @param parentDepth the number of segments in the path's parent directory (only meaningful for
+ * paths that begin with ".."). The parent directory itself is assumed to be in scope.
+ * @param normalizedPath input path, expected to be normalized such that all ".." and "." segments
+ * are removed (with the exception of a possible prefix sequence of contiguous ".." segments)
+ */
+ private boolean inScope(int parentDepth, PathFragment normalizedPath) {
+ if (scopeRoot == null) {
+ return true;
+ } else if (normalizedPath.isAbsolute()) {
+ return normalizedPath.startsWith(scopeRoot);
+ } else {
+ // Efficiency note: we're not accounting for "/scope/root/../root" paths here, i.e. paths
+ // that appear to go out of scope but ultimately stay within scope. This may result in
+ // unnecessary re-delegation back into the same FS. we're choosing to forgo that
+ // optimization under the assumption that such scenarios are rare and unimportant to
+ // overall performance. We can always enhance this if needed.
+ return parentDepth - leadingParentReferences(normalizedPath) >= scopeRoot.segmentCount();
+ }
+ }
+
+ /**
+ * Given a path that's normalized (no ".." or "." segments), except for a possible prefix sequence
+ * of contiguous ".." segments, returns the size of that prefix sequence.
+ *
+ * <p>Example allowed inputs: "/absolute/path", "relative/path", "../../relative/path". Example
+ * disallowed inputs: "/absolute/path/../path2", "relative/../path", "../relative/../p".
+ */
+ private int leadingParentReferences(PathFragment normalizedPath) {
+ int leadingParentReferences = 0;
+ for (int i = 0;
+ i < normalizedPath.segmentCount() && normalizedPath.getSegment(i).equals("..");
+ i++) {
+ leadingParentReferences++;
+ }
+ return leadingParentReferences;
+ }
+
+ /**
* The errors that {@link InMemoryFileSystem} might issue for different sorts of IO failures.
*/
public enum Error {
@@ -110,7 +140,7 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
private final String message;
- private Error(String message) {
+ Error(String message) {
this.message = message;
}
@@ -120,7 +150,7 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
}
/** Implemented by exceptions that contain the extra info of which Error caused them. */
- private static interface WithError {
+ private interface WithError {
Error getError();
}
@@ -271,7 +301,7 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
if (!create) {
throw Error.ENOENT.exception(path);
} else {
- child = makeFileInfo(clock, path.asFragment());
+ child = new InMemoryFileInfo(clock);
insert(imdi, name, child, path);
}
}
@@ -279,21 +309,18 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
}
/**
- * Low-level path-to-inode lookup routine. Analogous to path_walk() in many
- * UNIX kernels. Given 'path', walks the directory tree from the root,
- * resolving all symbolic links, and returns the designated inode.
+ * Low-level path-to-inode lookup routine. Analogous to path_walk() in many UNIX kernels. Given
+ * 'path', walks the directory tree from the root, resolving all symbolic links, and returns the
+ * designated inode.
*
- * <p>If 'create' is false, the inode must exist; otherwise, it will be created
- * and added to its parent directory, which must exist.
+ * <p>If 'create' is false, the inode must exist; otherwise, it will be created and added to its
+ * parent directory, which must exist.
*
- * <p>Iff the given path escapes this file system's scope, the returned value
- * is an {@link OutOfScopeFileStatus} instance. Any code that calls this method
- * needs to check for that possibility (via {@link ScopeEscapableStatus#outOfScope}).
+ * <p>Iff the given path escapes this file system's scope, a Error.ENOENT exception is thrown.
*
* <p>May fail with ENOTDIR, ENOENT, EACCES, ELOOP.
*/
- private synchronized InMemoryContentInfo pathWalk(Path path, boolean create)
- throws IOException {
+ private synchronized InMemoryContentInfo pathWalk(Path path, boolean create) throws IOException {
// Implementation note: This is where we check for out-of-scope symlinks and
// trigger re-delegation to another file system accordingly. This code handles
// both absolute and relative symlinks. Some assumptions we make: First, only
@@ -325,7 +352,7 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
if (child.isSymbolicLink()) {
PathFragment linkTarget = ((InMemoryLinkInfo) child).getNormalizedLinkContent();
if (!inScope(parentDepth, linkTarget)) {
- return outOfScopeStatus(linkTarget, parentDepth, stack);
+ throw Error.ENOENT.exception(path);
}
if (linkTarget.isAbsolute()) {
inode = rootInode;
@@ -345,62 +372,14 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
}
/**
- * Helper routine for pathWalk: given a symlink target known to escape this file system's
- * scope (and that has the form [".."]*[standard segment]+), the number of segments
- * in the directory containing the symlink, and the remaining path segments following
- * the symlink in the original input to pathWalk, returns an OutofScopeFileStatus
- * initialized with an appropriate out-of-scope reformulation of pathWalk's original
- * input.
- */
- private OutOfScopeFileStatus outOfScopeStatus(PathFragment linkTarget, int parentDepth,
- Stack<String> descendantSegments) {
-
- PathFragment escapingPath;
- if (linkTarget.isAbsolute()) {
- escapingPath = linkTarget;
- } else {
- // Relative out-of-scope paths must look like "../../../a/b/c". Find the target's
- // parent path depth by subtracting one from parentDepth for each ".." reference.
- // Then use that to retrieve a prefix of the scope root, which is the target's
- // canonicalized parent path.
- int leadingParentRefs = leadingParentReferences(linkTarget);
- int baseDepth = parentDepth - leadingParentRefs;
- Preconditions.checkState(baseDepth < scopeRoot.segmentCount());
- escapingPath = baseDepth > 0
- ? scopeRoot.subFragment(0, baseDepth)
- : scopeRoot.subFragment(0, 0);
- // Now add in everything that comes after the ".." sequence.
- for (int i = leadingParentRefs; i < linkTarget.segmentCount(); i++) {
- escapingPath = escapingPath.getRelative(linkTarget.getSegment(i));
- }
- }
-
- // We've now converted the symlink to its target in canonicalized absolute path
- // form. Since the symlink wasn't necessarily the final segment in the original
- // input sent to pathWalk, now add in every segment that came after.
- while (!descendantSegments.empty()) {
- escapingPath = escapingPath.getRelative(descendantSegments.pop());
- }
-
- return new OutOfScopeFileStatus(escapingPath);
- }
-
- /**
* Given 'path', returns the existing directory inode it designates,
* following symbolic links.
*
* <p>May fail with ENOTDIR, or any exception from pathWalk.
- *
- * <p>Iff the given path escapes this file system's scope, this method skips
- * ENOTDIR checking and returns an OutOfScopeDirectoryStatus instance. Any
- * code that calls this method needs to check for that possibility
- * (via {@link ScopeEscapableStatus#outOfScope}).
*/
private InMemoryDirectoryInfo getDirectory(Path path) throws IOException {
InMemoryContentInfo dirInfo = pathWalk(path, false);
- if (dirInfo.outOfScope()) {
- return new OutOfScopeDirectoryStatus(dirInfo.getEscapingPath());
- } else if (!dirInfo.isDirectory()) {
+ if (!dirInfo.isDirectory()) {
throw Error.ENOTDIR.exception(path);
} else {
return (InMemoryDirectoryInfo) dirInfo;
@@ -416,9 +395,7 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
private synchronized InMemoryContentInfo getNoFollowStatOrOutOfScopeParent(Path path)
throws IOException {
InMemoryDirectoryInfo dirInfo = getDirectory(path.getParentDirectory());
- return dirInfo.outOfScope()
- ? dirInfo
- : directoryLookup(dirInfo, path.getBaseName(), /*create=*/false, path);
+ return directoryLookup(dirInfo, path.getBaseName(), /*create=*/ false, path);
}
/**
@@ -429,21 +406,12 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
@Override
public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
if (followSymlinks) {
- InMemoryContentInfo status = scopeLimitedStat(path, true);
- return status.outOfScope()
- ? statWithDelegator(status.getEscapingPath(), true)
- : status;
+ return scopeLimitedStat(path, true);
} else {
if (path.equals(rootPath)) {
return rootInode;
} else {
- InMemoryContentInfo status = getNoFollowStatOrOutOfScopeParent(path);
- // If out of scope, status references the path's parent directory. Else it references the
- // path itself.
- return status.outOfScope()
- ? getDelegatedPath(status.getEscapingPath().getRelative(
- path.getBaseName())).stat(Symlinks.NOFOLLOW)
- : status;
+ return getNoFollowStatOrOutOfScopeParent(path);
}
}
}
@@ -465,13 +433,10 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
}
/**
- * Version of stat that returns an inode if the input path stays entirely within
- * this file system's scope, otherwise an {@link OutOfScopeFileStatus}.
- *
- * <p>Any code that calls this method needs to check for either possibility via
- * {@link ScopeEscapableStatus#outOfScope}.
+ * Version of stat that returns an inode if the input path stays entirely within this file
+ * system's scope, otherwise throws.
*/
- protected InMemoryContentInfo scopeLimitedStat(Path path, boolean followSymlinks)
+ private InMemoryContentInfo scopeLimitedStat(Path path, boolean followSymlinks)
throws IOException {
if (followSymlinks) {
return pathWalk(path, false);
@@ -479,12 +444,7 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
if (path.equals(rootPath)) {
return rootInode;
} else {
- InMemoryContentInfo status = getNoFollowStatOrOutOfScopeParent(path);
- // If out of scope, status references the path's parent directory. Else it references the
- // path itself.
- return status.outOfScope()
- ? new OutOfScopeFileStatus(status.getEscapingPath().getRelative(path.getBaseName()))
- : status;
+ return getNoFollowStatOrOutOfScopeParent(path);
}
}
}
@@ -507,13 +467,7 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
// Beware, this seemingly simple code belies the complex specification of
// FileSystem.resolveOneLink().
InMemoryContentInfo status = scopeLimitedStat(path, false);
- if (status.outOfScope()) {
- return resolveOneLinkWithDelegator(status.getEscapingPath());
- } else {
- return status.isSymbolicLink()
- ? ((InMemoryLinkInfo) status).getLinkContent()
- : null;
- }
+ return status.isSymbolicLink() ? ((InMemoryLinkInfo) status).getLinkContent() : null;
}
@Override
@@ -562,47 +516,24 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
}
}
- /**
- * Like {@link #exists}, but checks for existence within this filesystem's scope.
- */
- protected boolean scopeLimitedExists(Path path, boolean followSymlinks) {
- try {
- // Path#asFragment() always returns an absolute path, so inScope() is called with
- // parentDepth = 0.
- return inScope(0, path.asFragment()) && !scopeLimitedStat(path, followSymlinks).outOfScope();
- } catch (IOException e) {
- return false;
- }
- }
-
@Override
protected boolean isReadable(Path path) throws IOException {
InMemoryContentInfo status = scopeLimitedStat(path, true);
- return status.outOfScope()
- ? getDelegatedPath(status.getEscapingPath()).isReadable()
- : status.isReadable();
+ return status.isReadable();
}
@Override
protected void setReadable(Path path, boolean readable) throws IOException {
- InMemoryContentInfo status;
synchronized (this) {
- status = scopeLimitedStat(path, true);
- if (!status.outOfScope()) {
- status.setReadable(readable);
- return;
- }
+ InMemoryContentInfo status = scopeLimitedStat(path, true);
+ status.setReadable(readable);
}
- // If we get here, we're out of scope.
- getDelegatedPath(status.getEscapingPath()).setReadable(readable);
}
@Override
protected boolean isWritable(Path path) throws IOException {
InMemoryContentInfo status = scopeLimitedStat(path, true);
- return status.outOfScope()
- ? getDelegatedPath(status.getEscapingPath()).isWritable()
- : status.isWritable();
+ return status.isWritable();
}
@Override
@@ -610,36 +541,23 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
InMemoryContentInfo status;
synchronized (this) {
status = scopeLimitedStat(path, true);
- if (!status.outOfScope()) {
- status.setWritable(writable);
- return;
- }
+ status.setWritable(writable);
}
- // If we get here, we're out of scope.
- getDelegatedPath(status.getEscapingPath()).setWritable(writable);
}
@Override
protected boolean isExecutable(Path path) throws IOException {
InMemoryContentInfo status = scopeLimitedStat(path, true);
- return status.outOfScope()
- ? getDelegatedPath(status.getEscapingPath()).isExecutable()
- : status.isExecutable();
+ return status.isExecutable();
}
@Override
protected void setExecutable(Path path, boolean executable)
throws IOException {
- InMemoryContentInfo status;
synchronized (this) {
- status = scopeLimitedStat(path, true);
- if (!status.outOfScope()) {
- status.setExecutable(executable);
- return;
- }
+ InMemoryContentInfo status = scopeLimitedStat(path, true);
+ status.setExecutable(executable);
}
- // If we get here, we're out of scope.
- getDelegatedPath(status.getEscapingPath()).setExecutable(executable);
}
@Override
@@ -662,25 +580,6 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
return OS.getCurrent() != OS.WINDOWS;
}
- /**
- * Constructs a new inode. Provided so that subclasses of InMemoryFileSystem
- * can inject subclasses of FileInfo properly.
- */
- protected FileInfo makeFileInfo(Clock clock, PathFragment frag) {
- return new InMemoryFileInfo(clock);
- }
-
- /**
- * Returns a new path constructed by appending the child's base name to the
- * escaped parent path. For example, assume our file system root is /foo
- * and /foo/link1 -> /bar. This method can be used on child = /foo/link1/link2/name
- * and parent = /bar/link2 to return /bar/link2/name, which is a semi-resolved
- * path bound to a different file system.
- */
- private Path getDelegatedPath(PathFragment escapedParent, Path child) {
- return getDelegatedPath(escapedParent.getRelative(child.getBaseName()));
- }
-
@Override
protected boolean createDirectory(Path path) throws IOException {
if (path.equals(rootPath)) { throw Error.EACCES.exception(path); }
@@ -688,27 +587,22 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
InMemoryDirectoryInfo parent;
synchronized (this) {
parent = getDirectory(path.getParentDirectory());
- if (!parent.outOfScope()) {
- InMemoryContentInfo child = parent.getChild(path.getBaseName());
- if (child != null) { // already exists
- if (child.isDirectory()) {
- return false;
- } else {
- throw Error.EEXIST.exception(path);
- }
+ InMemoryContentInfo child = parent.getChild(path.getBaseName());
+ if (child != null) { // already exists
+ if (child.isDirectory()) {
+ return false;
+ } else {
+ throw Error.EEXIST.exception(path);
}
+ }
- InMemoryDirectoryInfo newDir = new InMemoryDirectoryInfo(clock);
- newDir.addChild(".", newDir);
- newDir.addChild("..", parent);
- insert(parent, path.getBaseName(), newDir, path);
+ InMemoryDirectoryInfo newDir = new InMemoryDirectoryInfo(clock);
+ newDir.addChild(".", newDir);
+ newDir.addChild("..", parent);
+ insert(parent, path.getBaseName(), newDir, path);
- return true;
- }
+ return true;
}
-
- // If we get here, we're out of scope.
- return getDelegatedPath(parent.getEscapingPath(), path).createDirectory();
}
@Override
@@ -716,26 +610,19 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
throws IOException {
if (path.equals(rootPath)) { throw Error.EACCES.exception(path); }
- InMemoryDirectoryInfo parent;
synchronized (this) {
- parent = getDirectory(path.getParentDirectory());
- if (!parent.outOfScope()) {
- if (parent.getChild(path.getBaseName()) != null) { throw Error.EEXIST.exception(path); }
- insert(parent, path.getBaseName(), new InMemoryLinkInfo(clock, targetFragment), path);
- return;
+ InMemoryDirectoryInfo parent = getDirectory(path.getParentDirectory());
+ if (parent.getChild(path.getBaseName()) != null) {
+ throw Error.EEXIST.exception(path);
}
+ insert(parent, path.getBaseName(), new InMemoryLinkInfo(clock, targetFragment), path);
}
-
- // If we get here, we're out of scope.
- getDelegatedPath(parent.getEscapingPath(), path).createSymbolicLink(targetFragment);
}
@Override
protected PathFragment readSymbolicLink(Path path) throws IOException {
InMemoryContentInfo status = scopeLimitedStat(path, false);
- if (status.outOfScope()) {
- return getDelegatedPath(status.getEscapingPath()).readSymbolicLink();
- } else if (status.isSymbolicLink()) {
+ if (status.isSymbolicLink()) {
Preconditions.checkState(status instanceof InMemoryLinkInfo);
return ((InMemoryLinkInfo) status).getLinkContent();
} else {
@@ -751,29 +638,23 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
@Override
protected Collection<Path> getDirectoryEntries(Path path) throws IOException {
- InMemoryDirectoryInfo dirInfo;
synchronized (this) {
- dirInfo = getDirectory(path);
- if (!dirInfo.outOfScope()) {
- FileStatus status = stat(path, false);
- Preconditions.checkState(status instanceof InMemoryContentInfo);
- if (!((InMemoryContentInfo) status).isReadable()) {
- throw new IOException("Directory is not readable");
- }
+ InMemoryDirectoryInfo dirInfo = getDirectory(path);
+ FileStatus status = stat(path, false);
+ Preconditions.checkState(status instanceof InMemoryContentInfo);
+ if (!((InMemoryContentInfo) status).isReadable()) {
+ throw new IOException("Directory is not readable");
+ }
- Collection<String> allChildren = dirInfo.getAllChildren();
- List<Path> result = new ArrayList<>(allChildren.size());
- for (String child : allChildren) {
- if (!(child.equals(".") || child.equals(".."))) {
- result.add(path.getChild(child));
- }
+ Collection<String> allChildren = dirInfo.getAllChildren();
+ List<Path> result = new ArrayList<>(allChildren.size());
+ for (String child : allChildren) {
+ if (!(child.equals(".") || child.equals(".."))) {
+ result.add(path.getChild(child));
}
- return result;
}
+ return result;
}
-
- // If we get here, we're out of scope.
- return getDelegatedPath(dirInfo.getEscapingPath()).getDirectoryEntries();
}
@Override
@@ -781,19 +662,15 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
if (path.equals(rootPath)) { throw Error.EBUSY.exception(path); }
if (!exists(path, false)) { return false; }
- InMemoryDirectoryInfo parent;
synchronized (this) {
- parent = getDirectory(path.getParentDirectory());
- if (!parent.outOfScope()) {
- InMemoryContentInfo child = parent.getChild(path.getBaseName());
- if (child.isDirectory() && child.getSize() > 2) { throw Error.ENOTEMPTY.exception(path); }
- unlink(parent, path.getBaseName(), path);
- return true;
+ InMemoryDirectoryInfo parent = getDirectory(path.getParentDirectory());
+ InMemoryContentInfo child = parent.getChild(path.getBaseName());
+ if (child.isDirectory() && child.getSize() > 2) {
+ throw Error.ENOTEMPTY.exception(path);
}
+ unlink(parent, path.getBaseName(), path);
+ return true;
}
-
- // If we get here, we're out of scope.
- return getDelegatedPath(parent.getEscapingPath(), path).delete();
}
@Override
@@ -804,46 +681,29 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
@Override
protected void setLastModifiedTime(Path path, long newTime) throws IOException {
- InMemoryContentInfo status;
synchronized (this) {
- status = scopeLimitedStat(path, true);
- if (!status.outOfScope()) {
- status.setLastModifiedTime(newTime == -1L
- ? clock.currentTimeMillis()
- : newTime);
- return;
- }
+ InMemoryContentInfo status = scopeLimitedStat(path, true);
+ status.setLastModifiedTime(newTime == -1L ? clock.currentTimeMillis() : newTime);
}
-
- // If we get here, we're out of scope.
- getDelegatedPath(status.getEscapingPath()).setLastModifiedTime(newTime);
}
@Override
protected InputStream getInputStream(Path path) throws IOException {
- InMemoryContentInfo status;
synchronized (this) {
- status = scopeLimitedStat(path, true);
- if (!status.outOfScope()) {
- if (status.isDirectory()) { throw Error.EISDIR.exception(path); }
- if (!path.isReadable()) { throw Error.EACCES.exception(path); }
- Preconditions.checkState(status instanceof FileInfo);
- return new ByteArrayInputStream(((FileInfo) status).readContent());
+ InMemoryContentInfo status = scopeLimitedStat(path, true);
+ if (status.isDirectory()) {
+ throw Error.EISDIR.exception(path);
+ }
+ if (!path.isReadable()) {
+ throw Error.EACCES.exception(path);
}
+ Preconditions.checkState(status instanceof FileInfo);
+ return new ByteArrayInputStream(((FileInfo) status).readContent());
}
-
- // If we get here, we're out of scope.
- return getDelegatedPath(status.getEscapingPath()).getInputStream();
}
- /**
- * Creates a new file at the given path and returns its inode. If the path
- * escapes this file system's scope, trivially returns an "out of scope" status.
- * Calling code should check for both possibilities via
- * {@link ScopeEscapableStatus#outOfScope}.
- */
- protected InMemoryContentInfo getOrCreateWritableInode(Path path)
- throws IOException {
+ /** Creates a new file at the given path and returns its inode. */
+ private InMemoryContentInfo getOrCreateWritableInode(Path path) throws IOException {
// open(WR_ONLY) of a dangling link writes through the link. That means
// that the usual path lookup operations have to behave differently when
// resolving a path with the intent to create it: instead of failing with
@@ -851,9 +711,7 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
// kernels do it, which is what we're trying to emulate.
InMemoryContentInfo child = pathWalk(path, /*create=*/true);
Preconditions.checkNotNull(child);
- if (child.outOfScope()) {
- return child;
- } else if (child.isDirectory()) {
+ if (child.isDirectory()) {
throw Error.EISDIR.exception(path);
} else { // existing or newly-created file
if (!child.isWritable()) { throw Error.EACCES.exception(path); }
@@ -864,74 +722,58 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
@Override
protected OutputStream getOutputStream(Path path, boolean append)
throws IOException {
- InMemoryContentInfo status;
synchronized (this) {
- status = getOrCreateWritableInode(path);
- if (!status.outOfScope()) {
- return ((FileInfo) getOrCreateWritableInode(path)).getOutputStream(append);
- }
+ InMemoryContentInfo status = getOrCreateWritableInode(path);
+ return ((FileInfo) status).getOutputStream(append);
}
- // If we get here, we're out of scope.
- return getDelegatedPath(status.getEscapingPath()).getOutputStream(append);
}
@Override
protected void renameTo(Path sourcePath, Path targetPath)
throws IOException {
- if (sourcePath.equals(rootPath)) { throw Error.EACCES.exception(sourcePath); }
- if (targetPath.equals(rootPath)) { throw Error.EACCES.exception(targetPath); }
+ if (sourcePath.equals(rootPath)) {
+ throw Error.EACCES.exception(sourcePath);
+ }
+ if (targetPath.equals(rootPath)) {
+ throw Error.EACCES.exception(targetPath);
+ }
+ synchronized (this) {
+ InMemoryDirectoryInfo sourceParent = getDirectory(sourcePath.getParentDirectory());
+ InMemoryDirectoryInfo targetParent = getDirectory(targetPath.getParentDirectory());
+
+ InMemoryContentInfo sourceInode = sourceParent.getChild(sourcePath.getBaseName());
+ if (sourceInode == null) {
+ throw Error.ENOENT.exception(sourcePath);
+ }
+ InMemoryContentInfo targetInode = targetParent.getChild(targetPath.getBaseName());
- InMemoryDirectoryInfo sourceParent;
- InMemoryDirectoryInfo targetParent;
+ unlink(sourceParent, sourcePath.getBaseName(), sourcePath);
+ try {
+ // TODO(bazel-team): (2009) test with symbolic links.
- synchronized (this) {
- sourceParent = getDirectory(sourcePath.getParentDirectory());
- targetParent = getDirectory(targetPath.getParentDirectory());
-
- // Handle the rename if both paths are within our scope.
- if (!sourceParent.outOfScope() && !targetParent.outOfScope()) {
- InMemoryContentInfo sourceInode = sourceParent.getChild(sourcePath.getBaseName());
- if (sourceInode == null) { throw Error.ENOENT.exception(sourcePath); }
- InMemoryContentInfo targetInode = targetParent.getChild(targetPath.getBaseName());
-
- unlink(sourceParent, sourcePath.getBaseName(), sourcePath);
- try {
- // TODO(bazel-team): (2009) test with symbolic links.
-
- // Precondition checks:
- if (targetInode != null) { // already exists
- if (targetInode.isDirectory()) {
- if (!sourceInode.isDirectory()) {
- throw new IOException(sourcePath + " -> " + targetPath + " (" + Error.EISDIR + ")");
- }
- if (targetInode.getSize() > 2) {
- throw Error.ENOTEMPTY.exception(targetPath);
- }
- } else if (sourceInode.isDirectory()) {
- throw new IOException(sourcePath + " -> " + targetPath + " (" + Error.ENOTDIR + ")");
+ // Precondition checks:
+ if (targetInode != null) { // already exists
+ if (targetInode.isDirectory()) {
+ if (!sourceInode.isDirectory()) {
+ throw new IOException(sourcePath + " -> " + targetPath + " (" + Error.EISDIR + ")");
}
- unlink(targetParent, targetPath.getBaseName(), targetPath);
+ if (targetInode.getSize() > 2) {
+ throw Error.ENOTEMPTY.exception(targetPath);
+ }
+ } else if (sourceInode.isDirectory()) {
+ throw new IOException(sourcePath + " -> " + targetPath + " (" + Error.ENOTDIR + ")");
}
- sourceInode.movedTo(targetPath);
- insert(targetParent, targetPath.getBaseName(), sourceInode, targetPath);
- return;
-
- } catch (IOException e) {
- sourceInode.movedTo(sourcePath);
- insert(sourceParent, sourcePath.getBaseName(), sourceInode, sourcePath); // restore source
- throw e;
+ unlink(targetParent, targetPath.getBaseName(), targetPath);
}
- }
- }
+ sourceInode.movedTo(targetPath);
+ insert(targetParent, targetPath.getBaseName(), sourceInode, targetPath);
+ return;
- // If we get here, either one or both paths is out of scope.
- if (sourceParent.outOfScope() && targetParent.outOfScope()) {
- Path delegatedSource = getDelegatedPath(sourceParent.getEscapingPath(), sourcePath);
- Path delegatedTarget = getDelegatedPath(targetParent.getEscapingPath(), targetPath);
- delegatedSource.renameTo(delegatedTarget);
- } else {
- // We don't support cross-file system renaming.
- throw Error.EACCES.exception(targetPath);
+ } catch (IOException e) {
+ sourceInode.movedTo(sourcePath);
+ insert(sourceParent, sourcePath.getBaseName(), sourceInode, sourcePath); // restore source
+ throw e;
+ }
}
}
@@ -944,23 +786,17 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
throw Error.EACCES.exception(originalPath);
}
- InMemoryDirectoryInfo linkParent;
synchronized (this) {
- linkParent = getDirectory(linkPath.getParentDirectory());
+ InMemoryDirectoryInfo linkParent = getDirectory(linkPath.getParentDirectory());
// Same check used when creating a symbolic link
- if (!linkParent.outOfScope()) {
- if (linkParent.getChild(linkPath.getBaseName()) != null) {
- throw Error.EEXIST.exception(linkPath);
- }
- insert(
- linkParent,
- linkPath.getBaseName(),
- getDirectory(originalPath.getParentDirectory()).getChild(originalPath.getBaseName()),
- linkPath);
- return;
+ if (linkParent.getChild(linkPath.getBaseName()) != null) {
+ throw Error.EEXIST.exception(linkPath);
}
+ insert(
+ linkParent,
+ linkPath.getBaseName(),
+ getDirectory(originalPath.getParentDirectory()).getChild(originalPath.getBaseName()),
+ linkPath);
}
- // If we get here, we're out of scope.
- getDelegatedPath(linkParent.getEscapingPath(), originalPath).createHardLink(linkPath);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/OutOfScopeDirectoryStatus.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/OutOfScopeDirectoryStatus.java
deleted file mode 100644
index 95d6044144..0000000000
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/OutOfScopeDirectoryStatus.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.devtools.build.lib.vfs.inmemoryfs;
-
-import com.google.devtools.build.lib.vfs.PathFragment;
-
-import java.util.Set;
-
-/**
- * A directory status that signifies a path has left this file system's
- * scope. All methods beside {@link #outOfScope} and {@link #getEscapingPath}
- * are disabled.
- */
-final class OutOfScopeDirectoryStatus extends InMemoryDirectoryInfo {
- /**
- * Contains the requested path resolved up to the point where it
- * first escapes the scope. See
- * {@link ScopeEscapableStatus#getEscapingPath} for an example.
- */
- private final PathFragment escapingPath;
-
- public OutOfScopeDirectoryStatus(PathFragment escapingPath) {
- super(null, false);
- this.escapingPath = escapingPath;
- }
-
- @Override
- public boolean outOfScope() {
- return true;
- }
-
- @Override
- public PathFragment getEscapingPath() {
- return escapingPath;
- }
-
- private static UnsupportedOperationException failure() {
- return new UnsupportedOperationException();
- }
-
- @Override public boolean isDirectory() {
- throw failure();
- }
-
- @Override public boolean isSymbolicLink() {
- throw failure();
- }
-
- @Override public boolean isFile() {
- throw failure();
- }
-
- @Override public long getSize() {
- throw failure();
- }
-
- @Override protected synchronized void markModificationTime() {
- throw failure();
- }
-
- @Override public synchronized long getLastModifiedTime() {
- throw failure();
- }
-
- @Override synchronized void setLastModifiedTime(long newTime) {
- throw failure();
- }
-
- @Override public synchronized long getLastChangeTime() {
- throw failure();
- }
-
- @Override boolean isReadable() {
- throw failure();
- }
-
- @Override void setReadable(boolean readable) {
- throw failure();
- }
-
- @Override void setWritable(boolean writable) {
- throw failure();
- }
-
- @Override void setExecutable(boolean executable) {
- throw failure();
- }
-
- @Override boolean isWritable() {
- throw failure();
- }
-
- @Override boolean isExecutable() {
- throw failure();
- }
-
- @Override synchronized void addChild(String name, InMemoryContentInfo inode) {
- throw failure();
- }
-
- @Override synchronized InMemoryContentInfo getChild(String name) {
- throw failure();
- }
-
- @Override synchronized void removeChild(String name) {
- throw failure();
- }
-
- @Override Set<String> getAllChildren() {
- throw failure();
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/OutOfScopeFileStatus.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/OutOfScopeFileStatus.java
deleted file mode 100644
index 0804968091..0000000000
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/OutOfScopeFileStatus.java
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.devtools.build.lib.vfs.inmemoryfs;
-
-import com.google.devtools.build.lib.vfs.PathFragment;
-
-/**
- * A file status that signifies a path has left this file system's
- * scope. All methods beside {@link #outOfScope} and {@link #getEscapingPath}
- * are disabled.
- */
-final class OutOfScopeFileStatus extends InMemoryContentInfo {
-
- /**
- * Contains the requested path resolved up to the point where it
- * first escapes the scope. See
- * {@link ScopeEscapableStatus#getEscapingPath} for an example.
- */
- private final PathFragment escapingPath;
-
- public OutOfScopeFileStatus(PathFragment escapingPath) {
- super(null, false);
- this.escapingPath = escapingPath;
- }
-
- @Override
- public boolean outOfScope() {
- return true;
- }
-
- @Override
- public PathFragment getEscapingPath() {
- return escapingPath;
- }
-
- private static UnsupportedOperationException failure() {
- return new UnsupportedOperationException();
- }
-
- @Override public boolean isDirectory() {
- throw failure();
- }
-
- @Override public boolean isSymbolicLink() {
- throw failure();
- }
-
- @Override public boolean isFile() {
- throw failure();
- }
-
- @Override public boolean isSpecialFile() {
- throw failure();
- }
-
- @Override public long getSize() {
- throw failure();
- }
-
- @Override protected synchronized void markModificationTime() {
- throw failure();
- }
-
- @Override public synchronized long getLastModifiedTime() {
- throw failure();
- }
-
- @Override synchronized void setLastModifiedTime(long newTime) {
- throw failure();
- }
-
- @Override public synchronized long getLastChangeTime() {
- throw failure();
- }
-
- @Override boolean isReadable() {
- throw failure();
- }
-
- @Override void setReadable(boolean readable) {
- throw failure();
- }
-
- @Override void setWritable(boolean writable) {
- throw failure();
- }
-
- @Override boolean isWritable() {
- throw failure();
- }
-
- @Override void setExecutable(boolean executable) {
- throw failure();
- }
-
- @Override boolean isExecutable() {
- throw failure();
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/ScopeEscapableStatus.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/ScopeEscapableStatus.java
deleted file mode 100644
index 371637985d..0000000000
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/ScopeEscapableStatus.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.devtools.build.lib.vfs.inmemoryfs;
-
-import com.google.devtools.build.lib.vfs.FileStatus;
-import com.google.devtools.build.lib.vfs.PathFragment;
-import com.google.devtools.build.lib.vfs.ScopeEscapableFileSystem;
-
-/**
- * Interface definition for a file status that may signify that the
- * referenced path falls outside the scope of the file system (see
- * {@link ScopeEscapableFileSystem}) and can provide the "escaped"
- * version of that path suitable for re-delegation to another file
- * system.
- */
-interface ScopeEscapableStatus extends FileStatus {
-
- /**
- * Returns true if this status corresponds to a path that leaves
- * the file system's scope, false otherwise.
- */
- boolean outOfScope();
-
- /**
- * If this status represents a path that leaves the file system's scope,
- * returns the requested path resolved up to the point where it first
- * escapes the file system. For example: if the file system is mapped to
- * /foo, the requested path is /foo/link1/link2/link3, and link1 -> /bar,
- * this returns /bar/link2/link3.
- *
- * <p>If this status doesn't represent a scope-escaping path, returns
- * null.
- */
- PathFragment getEscapingPath();
-}
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystemTest.java
deleted file mode 100644
index 9fd98b0f6d..0000000000
--- a/src/test/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystemTest.java
+++ /dev/null
@@ -1,818 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.devtools.build.lib.vfs;
-
-import static com.google.common.truth.Truth.assertThat;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.junit.Assert.fail;
-
-import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.util.Preconditions;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Collection;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Generic tests for any file system that implements {@link ScopeEscapableFileSystem},
- * i.e. any file system that supports symlinks that escape its scope.
- *
- * Each suitable file system test should inherit from this class, thereby obtaining
- * all the tests.
- */
-public abstract class ScopeEscapableFileSystemTest extends SymlinkAwareFileSystemTest {
-
- /**
- * Trivial FileSystem implementation that can record the last path passed to each method
- * and read/write to a unified "state" variable (which can then be checked by tests) for
- * each data type this class manipulates.
- *
- * The default implementation of each method throws an exception. Each test case should
- * selectively override the methods it expects to be invoked.
- */
- private static class TestDelegator extends FileSystem {
- protected Path lastPath;
- protected boolean booleanState;
- protected long longState;
- protected Object objectState;
-
- public void setState(boolean state) { booleanState = state; }
- public void setState(long state) { longState = state; }
- public void setState(Object state) { objectState = state; }
-
- public boolean booleanState() { return booleanState; }
- public long longState() { return longState; }
- public Object objectState() { return objectState; }
-
- public PathFragment lastPath() {
- Path ans = lastPath;
- // Clear this out to protect against accidental matches when testing the same path multiple
- // consecutive times.
- lastPath = null;
- return ans != null ? ans.asFragment() : null;
- }
-
- @Override
- public boolean supportsModifications(Path path) {
- return true;
- }
-
- @Override
- public boolean supportsSymbolicLinksNatively(Path path) {
- return true;
- }
-
- @Override
- public boolean supportsHardLinksNatively(Path path) {
- return true;
- }
-
- @Override
- public boolean isFilePathCaseSensitive() {
- return true;
- }
-
- private static RuntimeException re() {
- return new RuntimeException("This method should not be called in this context");
- }
-
- @Override protected boolean isReadable(Path path) { throw re(); }
- @Override protected boolean isWritable(Path path) { throw re(); }
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) { throw re(); }
- @Override protected boolean isFile(Path path, boolean followSymlinks) { throw re(); }
- @Override protected boolean isSpecialFile(Path path, boolean followSymlinks) { throw re(); }
- @Override protected boolean isExecutable(Path path) { throw re(); }
- @Override protected boolean exists(Path path, boolean followSymlinks) {throw re(); }
- @Override protected boolean isSymbolicLink(Path path) { throw re(); }
- @Override protected boolean createDirectory(Path path) { throw re(); }
- @Override protected boolean delete(Path path) { throw re(); }
-
- @Override protected long getFileSize(Path path, boolean followSymlinks) { throw re(); }
- @Override protected long getLastModifiedTime(Path path, boolean followSymlinks) { throw re(); }
-
- @Override protected void setWritable(Path path, boolean writable) { throw re(); }
- @Override protected void setExecutable(Path path, boolean executable) { throw re(); }
- @Override protected void setReadable(Path path, boolean readable) { throw re(); }
- @Override protected void setLastModifiedTime(Path path, long newTime) { throw re(); }
- @Override protected void renameTo(Path sourcePath, Path targetPath) { throw re(); }
- @Override protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) {
- throw re();
- }
- @Override protected void createFSDependentHardLink(Path linkPath, Path originalPath) {
- throw re();
- }
- @Override protected PathFragment readSymbolicLink(Path path) { throw re(); }
- @Override protected InputStream getInputStream(Path path) { throw re(); }
- @Override protected Collection<Path> getDirectoryEntries(Path path) { throw re(); }
- @Override protected OutputStream getOutputStream(Path path, boolean append) { throw re(); }
- @Override
- protected FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
- throw re();
- }
- }
-
- protected static final PathFragment SCOPE_ROOT = PathFragment.create("/fs/root");
-
- private Path fileLink;
- private PathFragment fileLinkTarget;
- private Path dirLink;
- private PathFragment dirLinkTarget;
-
- @Before
- public final void createLinks() throws Exception {
- Preconditions.checkState(
- testFS instanceof ScopeEscapableFileSystem, "Not ScopeEscapable: %s", testFS);
- ((ScopeEscapableFileSystem) testFS).enableScopeChecking(false);
- for (int i = 1; i <= SCOPE_ROOT.segmentCount(); i++) {
- testFS.getPath(SCOPE_ROOT.subFragment(0, i)).createDirectory();
- }
-
- fileLink = testFS.getPath(SCOPE_ROOT.getRelative("link"));
- fileLinkTarget = PathFragment.create("/should/be/delegated/fileLinkTarget");
- testFS.createSymbolicLink(fileLink, fileLinkTarget);
-
- dirLink = testFS.getPath(SCOPE_ROOT.getRelative("dirlink"));
- dirLinkTarget = PathFragment.create("/should/be/delegated/dirLinkTarget");
- testFS.createSymbolicLink(dirLink, dirLinkTarget);
- }
-
- /**
- * Returns the file system supplied by {@link #getFreshFileSystem}, cast to
- * a {@link ScopeEscapableFileSystem}. Also enables scope checking within
- * the file system (which we keep disabled for inherited tests that aren't
- * intended to test scope boundaries).
- */
- private ScopeEscapableFileSystem scopedFS() {
- ScopeEscapableFileSystem fs = (ScopeEscapableFileSystem) testFS;
- fs.enableScopeChecking(true);
- return fs;
- }
-
- // Checks that the semi-resolved path passed to the delegator matches the expected value.
- private void checkPath(TestDelegator delegator, PathFragment expectedDelegatedPath) {
- assertThat(delegator.lastPath()).isEqualTo(expectedDelegatedPath);
- }
-
- // Asserts that the condition is false and checks that the expected path was delegated.
- private void assertFalseWithPathCheck(boolean result, TestDelegator delegator,
- PathFragment expectedDelegatedPath) {
- assertThat(result).isFalse();
- checkPath(delegator, expectedDelegatedPath);
- }
-
- // Asserts that the condition is true and checks that the expected path was delegated.
- private void assertTrueWithPathCheck(boolean result, TestDelegator delegator,
- PathFragment expectedDelegatedPath) {
- assertThat(result).isTrue();
- checkPath(delegator, expectedDelegatedPath);
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // Tests:
- /////////////////////////////////////////////////////////////////////////////
-
- @Test
- public void testIsReadableCallOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected boolean isReadable(Path path) {
- lastPath = path;
- return booleanState();
- }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- assertFalseWithPathCheck(fileLink.isReadable(), delegator, fileLinkTarget);
- assertFalseWithPathCheck(dirLink.getRelative("a").isReadable(), delegator,
- dirLinkTarget.getRelative("a"));
-
- delegator.setState(true);
- assertTrueWithPathCheck(fileLink.isReadable(), delegator, fileLinkTarget);
- assertTrueWithPathCheck(dirLink.getRelative("a").isReadable(), delegator,
- dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testIsWritableCallOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected boolean isWritable(Path path) {
- lastPath = path;
- return booleanState();
- }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- assertFalseWithPathCheck(fileLink.isWritable(), delegator, fileLinkTarget);
- assertFalseWithPathCheck(dirLink.getRelative("a").isWritable(), delegator,
- dirLinkTarget.getRelative("a"));
-
- delegator.setState(true);
- assertTrueWithPathCheck(fileLink.isWritable(), delegator, fileLinkTarget);
- assertTrueWithPathCheck(dirLink.getRelative("a").isWritable(), delegator,
- dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testisExecutableCallOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected boolean isExecutable(Path path) {
- lastPath = path;
- return booleanState();
- }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- assertFalseWithPathCheck(fileLink.isExecutable(), delegator, fileLinkTarget);
- assertFalseWithPathCheck(dirLink.getRelative("a").isExecutable(), delegator,
- dirLinkTarget.getRelative("a"));
-
- delegator.setState(true);
- assertTrueWithPathCheck(fileLink.isExecutable(), delegator, fileLinkTarget);
- assertTrueWithPathCheck(dirLink.getRelative("a").isExecutable(), delegator,
- dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testIsDirectoryCallOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) {
- lastPath = path;
- return booleanState();
- }
- @Override protected boolean exists(Path path, boolean followSymlinks) { return true; }
- @Override protected long getLastModifiedTime(Path path, boolean followSymlinks) { return 0; }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- assertFalseWithPathCheck(fileLink.isDirectory(), delegator, fileLinkTarget);
- assertFalseWithPathCheck(dirLink.getRelative("a").isDirectory(), delegator,
- dirLinkTarget.getRelative("a"));
-
- delegator.setState(true);
- assertTrueWithPathCheck(fileLink.isDirectory(), delegator, fileLinkTarget);
- assertTrueWithPathCheck(dirLink.getRelative("a").isDirectory(), delegator,
- dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testIsFileCallOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected boolean isFile(Path path, boolean followSymlinks) {
- lastPath = path;
- return booleanState();
- }
- @Override protected boolean exists(Path path, boolean followSymlinks) { return true; }
- @Override protected long getLastModifiedTime(Path path, boolean followSymlinks) { return 0; }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- assertFalseWithPathCheck(fileLink.isFile(), delegator, fileLinkTarget);
- assertFalseWithPathCheck(dirLink.getRelative("a").isFile(), delegator,
- dirLinkTarget.getRelative("a"));
-
- delegator.setState(true);
- assertTrueWithPathCheck(fileLink.isFile(), delegator, fileLinkTarget);
- assertTrueWithPathCheck(dirLink.getRelative("a").isFile(), delegator,
- dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testIsSymbolicLinkCallOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected boolean isSymbolicLink(Path path) {
- lastPath = path;
- return booleanState();
- }
- @Override protected boolean exists(Path path, boolean followSymlinks) { return true; }
- @Override protected long getLastModifiedTime(Path path, boolean followSymlinks) { return 0; }
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) { return true; }
- };
- scopedFS().setDelegator(delegator);
-
- // We shouldn't follow final-segment links, so they should never invoke the delegator.
- delegator.setState(false);
- assertThat(fileLink.isSymbolicLink()).isTrue();
- assertThat(delegator.lastPath()).isNull();
-
- assertFalseWithPathCheck(dirLink.getRelative("a").isSymbolicLink(), delegator,
- dirLinkTarget.getRelative("a"));
-
- delegator.setState(true);
- assertTrueWithPathCheck(dirLink.getRelative("a").isSymbolicLink(), delegator,
- dirLinkTarget.getRelative("a"));
- }
-
- /**
- * Returns a test delegator that reflects info passed to Path.exists() calls.
- */
- private TestDelegator newExistsDelegator() {
- return new TestDelegator() {
- @Override protected boolean exists(Path path, boolean followSymlinks) {
- lastPath = path;
- return booleanState();
- }
- @Override protected FileStatus stat(Path path, boolean followSymlinks) throws IOException {
- if (!exists(path, followSymlinks)) {
- throw new IOException("Expected exception on stat of non-existent file");
- }
- return super.stat(path, followSymlinks);
- }
- @Override protected long getLastModifiedTime(Path path, boolean followSymlinks) { return 0; }
- };
- }
-
- @Test
- public void testExistsCallOnEscapingSymlink() throws Exception {
- TestDelegator delegator = newExistsDelegator();
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- assertFalseWithPathCheck(fileLink.exists(), delegator, fileLinkTarget);
- assertFalseWithPathCheck(dirLink.getRelative("a").exists(), delegator,
- dirLinkTarget.getRelative("a"));
-
- delegator.setState(true);
- assertTrueWithPathCheck(fileLink.exists(), delegator, fileLinkTarget);
- assertTrueWithPathCheck(dirLink.getRelative("a").exists(), delegator,
- dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testCreateDirectoryCallOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected boolean createDirectory(Path path) {
- lastPath = path;
- return booleanState();
- }
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) { return true; }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- assertFalseWithPathCheck(dirLink.getRelative("a").createDirectory(), delegator,
- dirLinkTarget.getRelative("a"));
-
- delegator.setState(true);
- assertTrueWithPathCheck(dirLink.getRelative("a").createDirectory(), delegator,
- dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testDeleteCallOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected boolean delete(Path path) {
- lastPath = path;
- return booleanState();
- }
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) { return true; }
- @Override protected long getLastModifiedTime(Path path, boolean followSymlinks) { return 0; }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- assertThat(fileLink.delete()).isTrue();
- assertThat(delegator.lastPath()).isNull(); // Deleting a link shouldn't require delegation.
- assertFalseWithPathCheck(dirLink.getRelative("a").delete(), delegator,
- dirLinkTarget.getRelative("a"));
-
- delegator.setState(true);
- assertTrueWithPathCheck(dirLink.getRelative("a").delete(), delegator,
- dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testCallGetFileSizeOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected long getFileSize(Path path, boolean followSymlinks) {
- lastPath = path;
- return longState();
- }
- @Override protected long getLastModifiedTime(Path path, boolean followSymlinks) { return 0; }
- };
- scopedFS().setDelegator(delegator);
-
- final int state1 = 10;
- delegator.setState(state1);
- assertThat(fileLink.getFileSize()).isEqualTo(state1);
- checkPath(delegator, fileLinkTarget);
- assertThat(dirLink.getRelative("a").getFileSize()).isEqualTo(state1);
- checkPath(delegator, dirLinkTarget.getRelative("a"));
-
- final int state2 = 10;
- delegator.setState(state2);
- assertThat(fileLink.getFileSize()).isEqualTo(state2);
- checkPath(delegator, fileLinkTarget);
- assertThat(dirLink.getRelative("a").getFileSize()).isEqualTo(state2);
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testCallGetLastModifiedTimeOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected long getLastModifiedTime(Path path, boolean followSymlinks) {
- lastPath = path;
- return longState();
- }
- };
- scopedFS().setDelegator(delegator);
-
- final int state1 = 10;
- delegator.setState(state1);
- assertThat(fileLink.getLastModifiedTime()).isEqualTo(state1);
- checkPath(delegator, fileLinkTarget);
- assertThat(dirLink.getRelative("a").getLastModifiedTime()).isEqualTo(state1);
- checkPath(delegator, dirLinkTarget.getRelative("a"));
-
- final int state2 = 10;
- delegator.setState(state2);
- assertThat(fileLink.getLastModifiedTime()).isEqualTo(state2);
- checkPath(delegator, fileLinkTarget);
- assertThat(dirLink.getRelative("a").getLastModifiedTime()).isEqualTo(state2);
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testCallSetReadableOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected void setReadable(Path path, boolean readable) {
- lastPath = path;
- setState(readable);
- }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- fileLink.setReadable(true);
- assertThat(delegator.booleanState()).isTrue();
- checkPath(delegator, fileLinkTarget);
- fileLink.setReadable(false);
- assertThat(delegator.booleanState()).isFalse();
- checkPath(delegator, fileLinkTarget);
-
- delegator.setState(false);
- dirLink.getRelative("a").setReadable(true);
- assertThat(delegator.booleanState()).isTrue();
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- dirLink.getRelative("a").setReadable(false);
- assertThat(delegator.booleanState()).isFalse();
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testCallSetWritableOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected void setWritable(Path path, boolean writable) {
- lastPath = path;
- setState(writable);
- }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- fileLink.setWritable(true);
- assertThat(delegator.booleanState()).isTrue();
- checkPath(delegator, fileLinkTarget);
- fileLink.setWritable(false);
- assertThat(delegator.booleanState()).isFalse();
- checkPath(delegator, fileLinkTarget);
-
- delegator.setState(false);
- dirLink.getRelative("a").setWritable(true);
- assertThat(delegator.booleanState()).isTrue();
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- dirLink.getRelative("a").setWritable(false);
- assertThat(delegator.booleanState()).isFalse();
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testCallSetExecutableOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected void setReadable(Path path, boolean readable) {
- lastPath = path;
- setState(readable);
- }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(false);
- fileLink.setReadable(true);
- assertThat(delegator.booleanState()).isTrue();
- checkPath(delegator, fileLinkTarget);
- fileLink.setReadable(false);
- assertThat(delegator.booleanState()).isFalse();
- checkPath(delegator, fileLinkTarget);
-
- delegator.setState(false);
- dirLink.getRelative("a").setReadable(true);
- assertThat(delegator.booleanState()).isTrue();
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- dirLink.getRelative("a").setReadable(false);
- assertThat(delegator.booleanState()).isFalse();
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testCallSetLastModifiedTimeOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected void setLastModifiedTime(Path path, long newTime) {
- lastPath = path;
- setState(newTime);
- }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(0);
- fileLink.setLastModifiedTime(10);
- assertThat(delegator.longState()).isEqualTo(10);
- checkPath(delegator, fileLinkTarget);
- fileLink.setLastModifiedTime(15);
- assertThat(delegator.longState()).isEqualTo(15);
- checkPath(delegator, fileLinkTarget);
-
- dirLink.getRelative("a").setLastModifiedTime(20);
- assertThat(delegator.longState()).isEqualTo(20);
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- dirLink.getRelative("a").setLastModifiedTime(25);
- assertThat(delegator.longState()).isEqualTo(25);
- checkPath(delegator, dirLinkTarget.getRelative("a"));
- }
-
- @Test
- public void testCallRenameToOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected void renameTo(Path sourcePath, Path targetPath) {
- lastPath = sourcePath;
- setState(targetPath);
- }
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) { return true; }
- };
- scopedFS().setDelegator(delegator);
-
- // Renaming a link should work fine.
- delegator.setState(null);
- fileLink.renameTo(testFS.getPath(SCOPE_ROOT).getRelative("newname"));
- assertThat(delegator.lastPath()).isNull(); // Renaming a link shouldn't require delegation.
- assertThat(delegator.objectState()).isNull();
-
- // Renaming an out-of-scope path to an in-scope path should fail due to filesystem mismatch
- // errors.
- Path newPath = testFS.getPath(SCOPE_ROOT.getRelative("blah"));
- try {
- dirLink.getRelative("a").renameTo(newPath);
- fail("This is an attempt at a cross-filesystem renaming, which should fail");
- } catch (IOException e) {
- // Expected.
- }
-
- // Renaming an out-of-scope path to another out-of-scope path can be valid.
- newPath = dirLink.getRelative("b");
- dirLink.getRelative("a").renameTo(newPath);
- assertThat(delegator.lastPath()).isEqualTo(dirLinkTarget.getRelative("a"));
- assertThat(((Path) delegator.objectState()).asFragment())
- .isEqualTo(dirLinkTarget.getRelative("b"));
- }
-
- @Test
- public void testCallCreateSymbolicLinkOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) {
- lastPath = linkPath;
- setState(targetFragment);
- }
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) { return true; }
- };
- scopedFS().setDelegator(delegator);
-
- PathFragment newLinkTarget = PathFragment.create("/something/else");
- dirLink.getRelative("a").createSymbolicLink(newLinkTarget);
- assertThat(delegator.lastPath()).isEqualTo(dirLinkTarget.getRelative("a"));
- assertThat(delegator.objectState()).isSameAs(newLinkTarget);
- }
-
- @Test
- public void testCallReadSymbolicLinkOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected PathFragment readSymbolicLink(Path path) {
- lastPath = path;
- return (PathFragment) objectState;
- }
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) { return true; }
- };
- scopedFS().setDelegator(delegator);
-
- // Since we're not following the link, this shouldn't invoke delegation.
- delegator.setState(PathFragment.create("whatever"));
- PathFragment p = fileLink.readSymbolicLink();
- assertThat(delegator.lastPath()).isNull();
- assertThat(p).isNotSameAs(delegator.objectState());
-
- // This should.
- p = dirLink.getRelative("a").readSymbolicLink();
- assertThat(delegator.lastPath()).isEqualTo(dirLinkTarget.getRelative("a"));
- assertThat(p).isSameAs(delegator.objectState());
- }
-
- @Test
- public void testCallGetInputStreamOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected InputStream getInputStream(Path path) {
- lastPath = path;
- return (InputStream) objectState;
- }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(new ByteArrayInputStream("blah".getBytes(UTF_8)));
- InputStream is = fileLink.getInputStream();
- assertThat(delegator.lastPath()).isEqualTo(fileLinkTarget);
- assertThat(is).isSameAs(delegator.objectState());
-
- delegator.setState(new ByteArrayInputStream("blah2".getBytes(UTF_8)));
- is = dirLink.getInputStream();
- assertThat(delegator.lastPath()).isEqualTo(dirLinkTarget);
- assertThat(is).isSameAs(delegator.objectState());
- }
-
- @Test
- public void testCallGetOutputStreamOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected OutputStream getOutputStream(Path path, boolean append) {
- lastPath = path;
- return (OutputStream) objectState;
- }
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) { return true; }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(new ByteArrayOutputStream());
- OutputStream os = fileLink.getOutputStream();
- assertThat(delegator.lastPath()).isEqualTo(fileLinkTarget);
- assertThat(os).isSameAs(delegator.objectState());
-
- delegator.setState(new ByteArrayOutputStream());
- os = dirLink.getOutputStream();
- assertThat(delegator.lastPath()).isEqualTo(dirLinkTarget);
- assertThat(os).isSameAs(delegator.objectState());
- }
-
- @Test
- public void testCallGetDirectoryEntriesOnEscapingSymlink() throws Exception {
- TestDelegator delegator = new TestDelegator() {
- @Override protected Collection<Path> getDirectoryEntries(Path path) {
- lastPath = path;
- return ImmutableList.of((Path) objectState);
- }
- @Override protected boolean isDirectory(Path path, boolean followSymlinks) { return true; }
- };
- scopedFS().setDelegator(delegator);
-
- delegator.setState(testFS.getPath("/anything"));
- Collection<Path> entries = dirLink.getDirectoryEntries();
- assertThat(delegator.lastPath()).isEqualTo(dirLinkTarget);
- assertThat(entries).hasSize(1);
- assertThat(entries.iterator().next()).isSameAs(delegator.objectState());
- }
-
- /**
- * Asserts that "link" is an in-scope link that doesn't result in an out-of-FS
- * delegation. If link is relative, its path is relative to SCOPE_ROOT.
- *
- * Note that we don't actually check that the canonicalized target path matches
- * the link's target value. Such testing should be covered by
- * SymlinkAwareFileSystemTest.
- */
- private void assertInScopeLink(String link, String target, TestDelegator d) throws IOException {
- Path l = testFS.getPath(SCOPE_ROOT.getRelative(link));
- testFS.createSymbolicLink(l, PathFragment.create(target));
- l.exists();
- assertThat(d.lastPath()).isNull();
- }
-
- /**
- * Asserts that "link" is an out-of-scope link and that the re-delegated path
- * matches expectedPath. If link is relative, its path is relative to SCOPE_ROOT.
- */
- private void assertOutOfScopeLink(String link, String target, String expectedPath,
- TestDelegator d) throws IOException {
- Path l = testFS.getPath(SCOPE_ROOT.getRelative(link));
- testFS.createSymbolicLink(l, PathFragment.create(target));
- l.exists();
- assertThat(d.lastPath().getPathString()).isEqualTo(expectedPath);
- }
-
- /**
- * Returns the scope root with the final n segments chopped off (or a 0-segment path
- * if n > SCOPE_ROOT.segmentCount()).
- */
- private String chopScopeRoot(int n) {
- return SCOPE_ROOT
- .subFragment(0, n > SCOPE_ROOT.segmentCount() ? 0 : SCOPE_ROOT.segmentCount() - n)
- .getPathString();
- }
-
- /**
- * Tests that absolute symlinks with ".." and "." segments are delegated to
- * the expected paths.
- */
- @Test
- public void testAbsoluteSymlinksWithParentReferences() throws Exception {
- TestDelegator d = newExistsDelegator();
- scopedFS().setDelegator(d);
- testFS.createDirectory(testFS.getPath(SCOPE_ROOT.getRelative("dir")));
- String scopeRoot = SCOPE_ROOT.getPathString();
- String scopeBase = SCOPE_ROOT.getBaseName();
-
- // Symlinks that should never escape our scope.
- assertInScopeLink("ilink1", scopeRoot, d);
- assertInScopeLink("ilink2", scopeRoot + "/target", d);
- assertInScopeLink("ilink3", scopeRoot + "/dir/../target", d);
- assertInScopeLink("ilink4", scopeRoot + "/dir/../dir/dir2/../target", d);
- assertInScopeLink("ilink5", scopeRoot + "/./dir/.././target", d);
- assertInScopeLink("ilink6", scopeRoot + "/../" + scopeBase + "/target", d);
- assertInScopeLink("ilink7", "/some/path/../.." + scopeRoot + "/target", d);
-
- // Symlinks that should escape our scope.
- assertOutOfScopeLink("olink1", scopeRoot + "/../target", chopScopeRoot(1) + "/target", d);
- assertOutOfScopeLink("olink2", "/some/other/path", "/some/other/path", d);
- assertOutOfScopeLink("olink3", scopeRoot + "/../target", chopScopeRoot(1) + "/target", d);
- assertOutOfScopeLink("olink4", chopScopeRoot(1) + "/target", chopScopeRoot(1) + "/target", d);
- assertOutOfScopeLink("olink5", scopeRoot + "/../../../../target", "/target", d);
-
- // In-scope symlink that's not the final segment in a query.
- Path iDirLink = testFS.getPath(SCOPE_ROOT.getRelative("ilinkdir"));
- testFS.createSymbolicLink(iDirLink, SCOPE_ROOT.getRelative("dir"));
- iDirLink.getRelative("file").exists();
- assertThat(d.lastPath()).isNull();
-
- // Out-of-scope symlink that's not the final segment in a query.
- Path oDirLink = testFS.getPath(SCOPE_ROOT.getRelative("olinkdir"));
- testFS.createSymbolicLink(oDirLink, PathFragment.create("/some/other/dir"));
- oDirLink.getRelative("file").exists();
- assertThat(d.lastPath().getPathString()).isEqualTo("/some/other/dir/file");
- }
-
- /**
- * Tests that relative symlinks with ".." and "." segments are delegated to
- * the expected paths.
- */
- @Test
- public void testRelativeSymlinksWithParentReferences() throws Exception {
- TestDelegator d = newExistsDelegator();
- scopedFS().setDelegator(d);
- testFS.createDirectory(testFS.getPath(SCOPE_ROOT.getRelative("dir")));
- testFS.createDirectory(testFS.getPath(SCOPE_ROOT.getRelative("dir/dir2")));
- testFS.createDirectory(testFS.getPath(SCOPE_ROOT.getRelative("dir/dir2/dir3")));
- String scopeRoot = SCOPE_ROOT.getPathString();
- String scopeBase = SCOPE_ROOT.getBaseName();
-
- // Symlinks that should never escape our scope.
- assertInScopeLink("ilink1", "target", d);
- assertInScopeLink("ilink2", "dir/../otherdir/target", d);
- assertInScopeLink("dir/ilink3", "../target", d);
- assertInScopeLink("dir/dir2/ilink4", "../../target", d);
- assertInScopeLink("dir/dir2/ilink5", ".././../dir/./target", d);
- assertInScopeLink("dir/dir2/ilink6", "../dir2/../../dir/dir2/dir3/../../../target", d);
-
- // Symlinks that should escape our scope.
- assertOutOfScopeLink("olink1", "../target", chopScopeRoot(1) + "/target", d);
- assertOutOfScopeLink("dir/olink2", "../../target", chopScopeRoot(1) + "/target", d);
- assertOutOfScopeLink("olink3", "../" + scopeBase + "/target", scopeRoot + "/target", d);
- assertOutOfScopeLink("dir/dir2/olink5", "../../../target", chopScopeRoot(1) + "/target", d);
- assertOutOfScopeLink("dir/dir2/olink6", "../dir2/../../dir/dir2/../../../target",
- chopScopeRoot(1) + "/target", d);
- assertOutOfScopeLink("dir/olink7", "../../../target", chopScopeRoot(2) + "target", d);
- assertOutOfScopeLink("olink8", "../../../../../target", "/target", d);
-
- // In-scope symlink that's not the final segment in a query.
- Path iDirLink = testFS.getPath(SCOPE_ROOT.getRelative("dir/dir2/ilinkdir"));
- testFS.createSymbolicLink(iDirLink, PathFragment.create("../../dir"));
- iDirLink.getRelative("file").exists();
- assertThat(d.lastPath()).isNull();
-
- // Out-of-scope symlink that's not the final segment in a query.
- Path oDirLink = testFS.getPath(SCOPE_ROOT.getRelative("dir/dir2/olinkdir"));
- testFS.createSymbolicLink(oDirLink, PathFragment.create("../../../other/dir"));
- oDirLink.getRelative("file").exists();
- assertThat(d.lastPath().getPathString()).isEqualTo(chopScopeRoot(1) + "/other/dir/file");
- }
-}
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystemTest.java
index 355d96d089..2ebe0a5dd3 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystemTest.java
@@ -21,7 +21,7 @@ import com.google.devtools.build.lib.testutil.TestThread;
import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
-import com.google.devtools.build.lib.vfs.ScopeEscapableFileSystemTest;
+import com.google.devtools.build.lib.vfs.SymlinkAwareFileSystemTest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -34,17 +34,15 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
- * Tests for {@link InMemoryFileSystem}. Note that most tests are inherited
- * from {@link ScopeEscapableFileSystemTest} and ancestors. This specific
- * file focuses only on concurrency tests.
- *
+ * Tests for {@link InMemoryFileSystem}. Note that most tests are inherited from {@link
+ * SymlinkAwareFileSystemTest} and ancestors. This specific file focuses only on concurrency tests.
*/
@RunWith(JUnit4.class)
-public class InMemoryFileSystemTest extends ScopeEscapableFileSystemTest {
+public class InMemoryFileSystemTest extends SymlinkAwareFileSystemTest {
@Override
public FileSystem getFreshFileSystem() {
- return new InMemoryFileSystem(BlazeClock.instance(), SCOPE_ROOT);
+ return new InMemoryFileSystem(BlazeClock.instance());
}
@Override