aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Nathan Harmata <nharmata@google.com>2015-10-20 21:54:34 +0000
committerGravatar Han-Wen Nienhuys <hanwen@google.com>2015-10-21 14:39:08 +0000
commitd8b6ff2dad1de2d98a407ecf67a34fe12e67d494 (patch)
tree030c5ebb89ca5835e76854ed51c0d2a75b04e4e3 /src
parent11ef8fc44821fb593a512eb5a3423013abbe39aa (diff)
Introduce Path#isSpecialFile, FileSystem#isSpecialFile, and FileStatus#isSpecialFile to help disambiguate between a regular file and a special file, since the file size of a special file cannot be trusted.
-- MOS_MIGRATED_REVID=105903622
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/cache/InjectedStat.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java82
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/FileValue.java15
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java8
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/PerBuildSyscallCache.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/FileStatus.java19
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/FilesystemUtils.java11
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java26
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/Dirent.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/FileStatus.java9
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/FileStatusWithDigestAdapter.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java15
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java46
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/Path.java21
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java23
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/ZipFileSystem.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/FileInfo.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java8
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryDirectoryInfo.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java9
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryLinkInfo.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/OutOfScopeFileStatus.java1
-rw-r--r--src/main/native/unix_jni.cc17
-rw-r--r--src/test/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystemTest.java1
-rw-r--r--src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java18
28 files changed, 295 insertions, 85 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/InjectedStat.java b/src/main/java/com/google/devtools/build/lib/actions/cache/InjectedStat.java
index aa32304a90..858edba762 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/InjectedStat.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/InjectedStat.java
@@ -36,6 +36,11 @@ public class InjectedStat implements FileStatus {
}
@Override
+ public boolean isSpecialFile() {
+ return false;
+ }
+
+ @Override
public boolean isDirectory() {
return false;
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java
index 1a75e3b0de..3fffcd9c00 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java
@@ -41,7 +41,7 @@ import javax.annotation.Nullable;
* <li> For a symlink, the symlink target is noted.
* <li> For a directory, the existence is noted.
* <li> For a file, the existence is noted, along with metadata about the file (e.g.
- * file digest). See {@link FileFileStateValue}.
+ * file digest). See {@link RegularFileStateValue}.
* <ul>
*
* <p>This class is an implementation detail of {@link FileValue} and should not be used outside of
@@ -59,7 +59,8 @@ public abstract class FileStateValue implements SkyValue {
new NonexistentFileStateValue();
enum Type {
- FILE,
+ REGULAR_FILE,
+ SPECIAL_FILE,
DIRECTORY,
SYMLINK,
NONEXISTENT,
@@ -86,7 +87,9 @@ public abstract class FileStateValue implements SkyValue {
throws InconsistentFilesystemException, IOException {
Path path = rootedPath.asPath();
if (statNoFollow.isFile()) {
- return FileFileStateValue.fromPath(path, statNoFollow, tsgm);
+ return statNoFollow.isSpecialFile()
+ ? SpecialFileStateValue.fromStat(statNoFollow, tsgm)
+ : RegularFileStateValue.fromPath(path, statNoFollow, tsgm);
} else if (statNoFollow.isDirectory()) {
return DIRECTORY_FILE_STATE_NODE;
} else if (statNoFollow.isSymbolicLink()) {
@@ -125,7 +128,7 @@ public abstract class FileStateValue implements SkyValue {
abstract String prettyPrint();
/**
- * Implementation of {@link FileStateValue} for files that exist.
+ * Implementation of {@link FileStateValue} for regular files that exist.
*
* <p>A union of (digest, mtime). We use digests only if a fast digest lookup is available from
* the filesystem. If not, we fall back to mtime-based digests. This avoids the case where Blaze
@@ -133,7 +136,7 @@ public abstract class FileStateValue implements SkyValue {
* where fast digest lookups are not available.
*/
@ThreadSafe
- public static final class FileFileStateValue extends FileStateValue {
+ public static final class RegularFileStateValue extends FileStateValue {
private final long size;
// Only needed for empty-file equality-checking. Otherwise is always -1.
// TODO(bazel-team): Consider getting rid of this special case for empty files.
@@ -141,7 +144,7 @@ public abstract class FileStateValue implements SkyValue {
@Nullable private final byte[] digest;
@Nullable private final FileContentsProxy contentsProxy;
- private FileFileStateValue(long size, long mtime, byte[] digest,
+ private RegularFileStateValue(long size, long mtime, byte[] digest,
FileContentsProxy contentsProxy) {
Preconditions.checkState((digest == null) != (contentsProxy == null));
this.size = size;
@@ -156,7 +159,7 @@ public abstract class FileStateValue implements SkyValue {
* Create a FileFileStateValue instance corresponding to the given existing file.
* @param stat must be of type "File". (Not a symlink).
*/
- private static FileFileStateValue fromPath(Path path, FileStatusWithDigest stat,
+ private static RegularFileStateValue fromPath(Path path, FileStatusWithDigest stat,
@Nullable TimestampGranularityMonitor tsgm)
throws InconsistentFilesystemException {
Preconditions.checkState(stat.isFile(), path);
@@ -172,13 +175,13 @@ public abstract class FileStateValue implements SkyValue {
if (tsgm != null) {
tsgm.notifyDependenceOnFileTime(mtime);
}
- return new FileFileStateValue(stat.getSize(), stat.getLastModifiedTime(), null,
+ return new RegularFileStateValue(stat.getSize(), stat.getLastModifiedTime(), null,
FileContentsProxy.create(mtime, stat.getNodeId()));
} else {
// We are careful here to avoid putting the value ID into FileMetadata if we already have
// a digest. Arbitrary filesystems may do weird things with the value ID; a digest is more
// robust.
- return new FileFileStateValue(stat.getSize(), stat.getLastModifiedTime(), digest, null);
+ return new RegularFileStateValue(stat.getSize(), stat.getLastModifiedTime(), digest, null);
}
} catch (IOException e) {
String errorMessage = e.getMessage() != null
@@ -191,7 +194,7 @@ public abstract class FileStateValue implements SkyValue {
@Override
Type getType() {
- return Type.FILE;
+ return Type.REGULAR_FILE;
}
@Override
@@ -207,8 +210,8 @@ public abstract class FileStateValue implements SkyValue {
@Override
public boolean equals(Object obj) {
- if (obj instanceof FileFileStateValue) {
- FileFileStateValue other = (FileFileStateValue) obj;
+ if (obj instanceof RegularFileStateValue) {
+ RegularFileStateValue other = (RegularFileStateValue) obj;
return size == other.size && mtime == other.mtime && Arrays.equals(digest, other.digest)
&& Objects.equals(contentsProxy, other.contentsProxy);
}
@@ -230,6 +233,61 @@ public abstract class FileStateValue implements SkyValue {
}
}
+ /** Implementation of {@link FileStateValue} for special files that exist. */
+ public static final class SpecialFileStateValue extends FileStateValue {
+ private final FileContentsProxy contentsProxy;
+
+ private SpecialFileStateValue(FileContentsProxy contentsProxy) {
+ this.contentsProxy = contentsProxy;
+ }
+
+ static SpecialFileStateValue fromStat(FileStatusWithDigest stat,
+ @Nullable TimestampGranularityMonitor tsgm) throws IOException {
+ long mtime = stat.getLastModifiedTime();
+ // Note that TimestampGranularityMonitor#notifyDependenceOnFileTime is a thread-safe
+ // method.
+ if (tsgm != null) {
+ tsgm.notifyDependenceOnFileTime(mtime);
+ }
+ return new SpecialFileStateValue(FileContentsProxy.create(mtime, stat.getNodeId()));
+ }
+
+ @Override
+ Type getType() {
+ return Type.SPECIAL_FILE;
+ }
+
+ @Override
+ long getSize() {
+ return 0;
+ }
+
+ @Override
+ @Nullable
+ byte[] getDigest() {
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof SpecialFileStateValue) {
+ SpecialFileStateValue other = (SpecialFileStateValue) obj;
+ return Objects.equals(contentsProxy, other.contentsProxy);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return contentsProxy.hashCode();
+ }
+
+ @Override
+ public String prettyPrint() {
+ return String.format("special file with %s", contentsProxy.prettyPrint());
+ }
+ }
+
/** Implementation of {@link FileStateValue} for directories that exist. */
public static final class DirectoryFileStateValue extends FileStateValue {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileValue.java
index 8d849e09fd..0c45cf1bcd 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileValue.java
@@ -59,11 +59,20 @@ public abstract class FileValue implements SkyValue {
}
/**
- * Returns true if this value corresponds to a file or symlink to an existing file. If so, its
- * parent directory is guaranteed to exist.
+ * Returns true if this value corresponds to a file or symlink to an existing regular or special
+ * file. If so, its parent directory is guaranteed to exist.
*/
public boolean isFile() {
- return realFileStateValue().getType() == Type.FILE;
+ return realFileStateValue().getType() == Type.REGULAR_FILE
+ || realFileStateValue().getType() == Type.SPECIAL_FILE;
+ }
+
+ /**
+ * Returns true if this value corresponds to a file or symlink to an existing special file. If so,
+ * its parent directory is guaranteed to exist.
+ */
+ public boolean isSpecialFile() {
+ return realFileStateValue().getType() == Type.SPECIAL_FILE;
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
index d8e8230f99..aa922913da 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
@@ -845,7 +845,7 @@ public class PackageFunction implements SkyFunction {
*
* <p>May return null if the computation has to be restarted.
*
- * <p>Exactly one of {@code replacementContents} and {@link buildFileValue} will be
+ * <p>Exactly one of {@code replacementContents} and {@code buildFileValue} will be
* non-{@code null}. The former indicates that we have a faux BUILD file with the given contents
* and the latter indicates that we have a legitimate BUILD file and should actually do
* preprocessing.
@@ -875,13 +875,15 @@ public class PackageFunction implements SkyFunction {
}
Preprocessor.Result preprocessingResult;
if (replacementContents == null) {
- long buildFileSize = Preconditions.checkNotNull(buildFileValue, packageId).getSize();
+ Preconditions.checkNotNull(buildFileValue, packageId);
// Even though we only open and read the file on a cache miss, note that the BUILD is
// still parsed two times. Also, the preprocessor may suboptimally open and read it
// again anyway.
ParserInputSource inputSource;
try {
- inputSource = ParserInputSource.create(buildFilePath, buildFileSize);
+ inputSource = buildFileValue.isSpecialFile()
+ ? ParserInputSource.create(buildFilePath)
+ : ParserInputSource.create(buildFilePath, buildFileValue.getSize());
} catch (IOException e) {
env.getListener().handle(Event.error(Location.fromFile(buildFilePath),
e.getMessage()));
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PerBuildSyscallCache.java b/src/main/java/com/google/devtools/build/lib/skyframe/PerBuildSyscallCache.java
index b1265b4c96..51df3671a4 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PerBuildSyscallCache.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PerBuildSyscallCache.java
@@ -102,6 +102,11 @@ public class PerBuildSyscallCache implements UnixGlob.FilesystemCalls {
}
@Override
+ public boolean isSpecialFile() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean isSymbolicLink() {
throw new UnsupportedOperationException();
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index cc809502d8..1b5dfd8468 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -756,7 +756,9 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory {
protected abstract void invalidate(Predicate<SkyKey> pred);
private static boolean compatibleFileTypes(Dirent.Type oldType, FileStateValue.Type newType) {
- return (oldType.equals(Dirent.Type.FILE) && newType.equals(FileStateValue.Type.FILE))
+ return (oldType.equals(Dirent.Type.FILE) && newType.equals(FileStateValue.Type.REGULAR_FILE))
+ || (oldType.equals(Dirent.Type.UNKNOWN)
+ && newType.equals(FileStateValue.Type.SPECIAL_FILE))
|| (oldType.equals(Dirent.Type.DIRECTORY) && newType.equals(FileStateValue.Type.DIRECTORY))
|| (oldType.equals(Dirent.Type.SYMLINK) && newType.equals(FileStateValue.Type.SYMLINK));
}
diff --git a/src/main/java/com/google/devtools/build/lib/unix/FileStatus.java b/src/main/java/com/google/devtools/build/lib/unix/FileStatus.java
index e227f95cb9..9a63feeb55 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/FileStatus.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/FileStatus.java
@@ -259,4 +259,23 @@ public class FileStatus {
return (i & 0x7FFFFFFF) - (long) (i & 0x80000000);
}
+ public static boolean isFile(int rawType) {
+ int type = rawType & S_IFMT;
+ return type == S_IFREG || isSpecialFile(rawType);
+ }
+
+ public static boolean isSpecialFile(int rawType) {
+ int type = rawType & S_IFMT;
+ return type == S_IFSOCK || type == S_IFBLK || type == S_IFCHR || type == S_IFIFO;
+ }
+
+ public static boolean isDirectory(int rawType) {
+ int type = rawType & S_IFMT;
+ return type == S_IFDIR;
+ }
+
+ public static boolean isSymbolicLink(int rawType) {
+ int type = rawType & S_IFMT;
+ return type == S_IFLNK;
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/unix/FilesystemUtils.java b/src/main/java/com/google/devtools/build/lib/unix/FilesystemUtils.java
index 525942f44e..0cab41ca4f 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/FilesystemUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/FilesystemUtils.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.unix;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.hash.HashCode;
import com.google.devtools.build.lib.UnixJniLoader;
@@ -373,6 +374,16 @@ public final class FilesystemUtils {
*/
public static native boolean remove(String path) throws IOException;
+ /**
+ * Native wrapper around POSIX mkfifo(3) C library call.
+ *
+ * @param path the name of the pipe to create.
+ * @param mode the mode with which to create the pipe.
+ * @throws IOException if the mkfifo failed.
+ */
+ @VisibleForTesting
+ public static native void mkfifo(String path, int mode) throws IOException;
+
/********************************************************************
* *
* Linux extended file attributes *
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java
index de67af85cf..1d0b4dc070 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java
@@ -36,7 +36,7 @@ abstract class AbstractFileSystem extends FileSystem {
protected static final Profiler profiler = Profiler.instance();
@Override
- protected InputStream getInputStream(Path path) throws FileNotFoundException {
+ protected InputStream getInputStream(Path path) throws IOException {
// This loop is a workaround for an apparent bug in FileInputStrean.open, which delegates
// ultimately to JVM_Open in the Hotspot JVM. This call is not EINTR-safe, so we must do the
// retry here.
@@ -144,4 +144,28 @@ abstract class AbstractFileSystem extends FileSystem {
}
}
}
+
+ @Override
+ protected boolean isFile(Path path, boolean followSymlinks) {
+ FileStatus stat = statNullable(path, followSymlinks);
+ return stat != null ? stat.isFile() : false;
+ }
+
+ @Override
+ protected boolean isSpecialFile(Path path, boolean followSymlinks) {
+ FileStatus stat = statNullable(path, followSymlinks);
+ return stat != null ? stat.isSpecialFile() : false;
+ }
+
+ @Override
+ protected boolean isSymbolicLink(Path path) {
+ FileStatus stat = statNullable(path, false);
+ return stat != null ? stat.isSymbolicLink() : false;
+ }
+
+ @Override
+ protected boolean isDirectory(Path path, boolean followSymlinks) {
+ FileStatus stat = statNullable(path, followSymlinks);
+ return stat != null ? stat.isDirectory() : false;
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/Dirent.java b/src/main/java/com/google/devtools/build/lib/vfs/Dirent.java
index 49d41a119b..2b81c831c8 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/Dirent.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/Dirent.java
@@ -25,9 +25,13 @@ public class Dirent implements Serializable {
/** Type of the directory entry */
public enum Type {
+ // A regular file.
FILE,
+ // A directory.
DIRECTORY,
+ // A symlink.
SYMLINK,
+ // Not one of the above. For example, a special file.
UNKNOWN;
}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileStatus.java b/src/main/java/com/google/devtools/build/lib/vfs/FileStatus.java
index 4c7c206c6e..712f96da14 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileStatus.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileStatus.java
@@ -37,8 +37,7 @@ import java.io.IOException;
public interface FileStatus {
/**
- * Returns true iff this file is a regular or special file (e.g. socket,
- * fifo or device).
+ * Returns true iff this file is a regular file or {@code isSpecial()}.
*/
boolean isFile();
@@ -53,6 +52,12 @@ public interface FileStatus {
boolean isSymbolicLink();
/**
+ * Returns true iff this file is a special file (e.g. socket, fifo or device). {@link #getSize()}
+ * can't be trusted for such files.
+ */
+ boolean isSpecialFile();
+
+ /**
* Returns the total size, in bytes, of this file.
*/
long getSize() throws IOException;
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileStatusWithDigestAdapter.java b/src/main/java/com/google/devtools/build/lib/vfs/FileStatusWithDigestAdapter.java
index ab034b063b..5daeaa668c 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileStatusWithDigestAdapter.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileStatusWithDigestAdapter.java
@@ -45,6 +45,11 @@ public class FileStatusWithDigestAdapter implements FileStatusWithDigest {
}
@Override
+ public boolean isSpecialFile() {
+ return stat.isSpecialFile();
+ }
+
+ @Override
public boolean isDirectory() {
return stat.isDirectory();
}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
index 82597e3603..d396139cfe 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
@@ -362,6 +362,7 @@ public abstract class FileSystem {
volatile Boolean isFile;
volatile Boolean isDirectory;
volatile Boolean isSymbolicLink;
+ volatile Boolean isSpecial;
volatile long size = -1;
volatile long mtime = -1;
@@ -386,6 +387,12 @@ public abstract class FileSystem {
}
@Override
+ public boolean isSpecialFile() {
+ if (isSpecial == null) { isSpecial = FileSystem.this.isSpecialFile(path, followSymlinks); }
+ return isSpecial;
+ }
+
+ @Override
public long getSize() throws IOException {
if (size == -1) { size = getFileSize(path, followSymlinks); }
return size;
@@ -453,6 +460,12 @@ public abstract class FileSystem {
protected abstract boolean isFile(Path path, boolean followSymlinks);
/**
+ * Returns true iff {@code path} denotes a special file.
+ * See {@link Path#isSpecialFile(Symlinks)} for specification.
+ */
+ protected abstract boolean isSpecialFile(Path path, boolean followSymlinks);
+
+ /**
* Creates a symbolic link. See {@link Path#createSymbolicLink(Path)} for
* specification.
*
@@ -505,6 +518,8 @@ public abstract class FileSystem {
protected static Dirent.Type direntFromStat(FileStatus stat) {
if (stat == null) {
return Type.UNKNOWN;
+ } else if (stat.isSpecialFile()) {
+ return Type.UNKNOWN;
} else if (stat.isFile()) {
return Type.FILE;
} else if (stat.isDirectory()) {
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java
index 3ae4e96749..9d9f16e5ae 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java
@@ -94,34 +94,6 @@ public class JavaIoFileSystem extends AbstractFileSystem {
}
@Override
- protected boolean isDirectory(Path path, boolean followSymlinks) {
- File file = getIoFile(path);
- long startTime = Profiler.nanoTimeMaybe();
- try {
- if (!followSymlinks && fileIsSymbolicLink(file)) {
- return false;
- }
- return file.isDirectory();
- } finally {
- profiler.logSimpleTask(startTime, ProfilerTask.VFS_STAT, path.toString());
- }
- }
-
- @Override
- protected boolean isFile(Path path, boolean followSymlinks) {
- File file = getIoFile(path);
- long startTime = Profiler.nanoTimeMaybe();
- try {
- if (!followSymlinks && fileIsSymbolicLink(file)) {
- return false;
- }
- return file.isFile();
- } finally {
- profiler.logSimpleTask(startTime, ProfilerTask.VFS_STAT, path.toString());
- }
- }
-
- @Override
protected boolean isReadable(Path path) throws IOException {
File file = getIoFile(path);
long startTime = Profiler.nanoTimeMaybe();
@@ -365,17 +337,6 @@ public class JavaIoFileSystem extends AbstractFileSystem {
}
}
- @Override
- protected boolean isSymbolicLink(Path path) {
- File file = getIoFile(path);
- long startTime = Profiler.nanoTimeMaybe();
- try {
- return fileIsSymbolicLink(file);
- } finally {
- profiler.logSimpleTask(startTime, ProfilerTask.VFS_STAT, file.getPath());
- }
- }
-
private boolean fileIsSymbolicLink(File file) {
return Files.isSymbolicLink(file.toPath());
}
@@ -428,7 +389,12 @@ public class JavaIoFileSystem extends AbstractFileSystem {
FileStatus status = new FileStatus() {
@Override
public boolean isFile() {
- return attributes.isRegularFile();
+ return attributes.isRegularFile() || isSpecialFile();
+ }
+
+ @Override
+ public boolean isSpecialFile() {
+ return attributes.isOther();
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/Path.java b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
index aac68ee8b0..c7c54712a3 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/Path.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
@@ -533,6 +533,25 @@ public class Path implements Comparable<Path>, Serializable {
}
/**
+ * Returns true iff this path denotes an existing special file (e.g. fifo).
+ * Follows symbolic links.
+ */
+ public boolean isSpecialFile() {
+ return fileSystem.isSpecialFile(this, true);
+ }
+
+ /**
+ * Returns true iff this path denotes an existing special file (e.g. fifo).
+ *
+ * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a
+ * symbolic link, the link is dereferenced until a path other than a
+ * symbolic link is found.
+ */
+ public boolean isSpecialFile(Symlinks followSymlinks) {
+ return fileSystem.isSpecialFile(this, followSymlinks.toBoolean());
+ }
+
+ /**
* Returns true iff this path denotes an existing symbolic link. Does not
* follow symbolic links.
*/
@@ -814,7 +833,7 @@ public class Path implements Comparable<Path>, Serializable {
* Returns the size in bytes of the file denoted by the current path,
* following symbolic links.
*
- * <p>The size of directory or special file is undefined.
+ * <p>The size of a directory or special file is undefined and should not be used.
*
* @throws FileNotFoundException if the file denoted by the current path does
* not exist
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java
index 821e4704f0..e3cf56fe3a 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java
@@ -33,7 +33,7 @@ import java.io.OutputStream;
* </ul>
* The above calls will always result in an {@link IOException}.
*/
-public abstract class ReadonlyFileSystem extends FileSystem {
+public abstract class ReadonlyFileSystem extends AbstractFileSystem {
protected ReadonlyFileSystem() {
}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java
index b774543575..a1ef71f844 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java
@@ -248,6 +248,12 @@ public class UnionFileSystem extends FileSystem {
}
@Override
+ protected boolean isSpecialFile(Path path, boolean followSymlinks) {
+ FileSystem delegate = getDelegate(path);
+ return delegate.isSpecialFile(adjustPath(path, delegate), followSymlinks);
+ }
+
+ @Override
protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException {
checkModifiable();
if (!supportsSymbolicLinks()) {
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 d730cab2de..29a8636b06 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
@@ -63,6 +63,9 @@ public class UnixFileSystem extends AbstractFileSystem {
public boolean isSymbolicLink() { return status.isSymbolicLink(); }
@Override
+ public boolean isSpecialFile() { return isFile() && !status.isRegularFile(); }
+
+ @Override
public long getSize() { return status.getSize(); }
@Override
@@ -225,20 +228,6 @@ public class UnixFileSystem extends AbstractFileSystem {
}
@Override
- protected boolean isDirectory(Path path, boolean followSymlinks) {
- FileStatus stat = statNullable(path, followSymlinks);
- return stat != null && stat.isDirectory();
- }
-
- @Override
- protected boolean isFile(Path path, boolean followSymlinks) {
- // Note, FileStatus.isFile means *regular* file whereas Path.isFile may
- // mean special file too, so we don't return FileStatus.isFile here.
- FileStatus status = statNullable(path, followSymlinks);
- return status != null && !(status.isSymbolicLink() || status.isDirectory());
- }
-
- @Override
protected boolean isReadable(Path path) throws IOException {
return (statInternal(path, true).getPermissions() & 0400) != 0;
}
@@ -374,12 +363,6 @@ public class UnixFileSystem extends AbstractFileSystem {
}
@Override
- protected boolean isSymbolicLink(Path path) {
- FileStatus stat = statNullable(path, false);
- return stat != null && stat.isSymbolicLink();
- }
-
- @Override
protected void setLastModifiedTime(Path path, long newTime) throws IOException {
synchronized (path) {
if (newTime == -1L) { // "now"
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/ZipFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/ZipFileSystem.java
index 78961c8f30..393110d5f8 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/ZipFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/ZipFileSystem.java
@@ -198,6 +198,11 @@ public class ZipFileSystem extends ReadonlyFileSystem {
}
@Override
+ protected boolean isSpecialFile(Path path, boolean followSymlinks) {
+ return false;
+ }
+
+ @Override
protected boolean isReadable(Path path) throws IOException {
zipEntryNonNull(path);
return true;
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/FileInfo.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/FileInfo.java
index 2c578d1214..3c5d55c9fe 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/FileInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/FileInfo.java
@@ -43,6 +43,11 @@ public abstract class FileInfo extends InMemoryContentInfo {
return true;
}
+ @Override
+ public boolean isSpecialFile() {
+ return false;
+ }
+
protected abstract byte[] readContent() throws IOException;
protected abstract OutputStream getOutputStream(boolean append) throws IOException;
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 6a721f1f94..d0044ccd28 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
@@ -80,12 +80,18 @@ public abstract class InMemoryContentInfo implements ScopeEscapableStatus {
public abstract boolean isSymbolicLink();
/**
- * Returns true if the current object is a regular file.
+ * Returns true if the current object is a regular or special file.
*/
@Override
public abstract boolean isFile();
/**
+ * Returns true if the current object is a special file.
+ */
+ @Override
+ public abstract boolean isSpecialFile();
+
+ /**
* Returns the size of the entity denoted by the current object. For files,
* this is the length in bytes, for directories the number of children. The
* size of links is unspecified.
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryDirectoryInfo.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryDirectoryInfo.java
index d7a6cf5407..66d7523b16 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryDirectoryInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryDirectoryInfo.java
@@ -96,6 +96,11 @@ class InMemoryDirectoryInfo extends InMemoryContentInfo {
return false;
}
+ @Override
+ public boolean isSpecialFile() {
+ return false;
+ }
+
/**
* In the InMemory hierarchy, the getSize on a directory always returns the
* number of children in the directory.
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 2138af1ba7..fe3731ef90 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
@@ -537,6 +537,15 @@ public class InMemoryFileSystem extends ScopeEscapableFileSystem {
}
@Override
+ protected boolean isSpecialFile(Path path, boolean followSymlinks) {
+ try {
+ return stat(path, followSymlinks).isSpecialFile();
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ @Override
protected boolean isSymbolicLink(Path path) {
try {
return stat(path, false).isSymbolicLink();
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryLinkInfo.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryLinkInfo.java
index 5d55fb563c..56204926e2 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryLinkInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryLinkInfo.java
@@ -50,6 +50,11 @@ class InMemoryLinkInfo extends InMemoryContentInfo {
}
@Override
+ public boolean isSpecialFile() {
+ return false;
+ }
+
+ @Override
public long getSize() {
return linkContent.toString().length();
}
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
index 6a71c5b203..287d97d5fe 100644
--- 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
@@ -51,6 +51,7 @@ final class OutOfScopeFileStatus extends InMemoryContentInfo {
@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 void markModificationTime() { throw failure(); }
@Override public synchronized long getLastModifiedTime() { throw failure(); }
diff --git a/src/main/native/unix_jni.cc b/src/main/native/unix_jni.cc
index bc6a14e3c8..eed3092646 100644
--- a/src/main/native/unix_jni.cc
+++ b/src/main/native/unix_jni.cc
@@ -726,6 +726,23 @@ Java_com_google_devtools_build_lib_unix_FilesystemUtils_remove(JNIEnv *env,
return ::delete_common(env, path, ::remove, ::remove_err);
}
+/*
+ * Class: com.google.devtools.build.lib.unix.FilesystemUtils
+ * Method: mkfifo
+ * Signature: (Ljava/lang/String;I)V
+ * Throws: java.io.IOException
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_devtools_build_lib_unix_FilesystemUtils_mkfifo(JNIEnv *env,
+ jclass clazz,
+ jstring path,
+ jint mode) {
+ const char *path_chars = GetStringLatin1Chars(env, path);
+ if (mkfifo(path_chars, mode) == -1) {
+ ::PostFileException(env, errno, path_chars);
+ }
+ ReleaseStringLatin1Chars(path_chars);
+}
////////////////////////////////////////////////////////////////////////
// Linux extended file attributes
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
index 719ffde41c..87c4cce17a 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/ScopeEscapableFileSystemTest.java
@@ -85,6 +85,7 @@ public abstract class ScopeEscapableFileSystemTest extends SymlinkAwareFileSyste
@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(); }
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java
index cb5685ed6b..40ace2b6f6 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java
@@ -15,8 +15,10 @@ package com.google.devtools.build.lib.vfs;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.google.devtools.build.lib.unix.FilesystemUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -60,4 +62,20 @@ public class UnixFileSystemTest extends SymlinkAwareFileSystemTest {
// Expected.
}
}
+
+ @Test
+ public void testIsSpecialFile() throws Exception {
+ Path regular = absolutize("regular");
+ Path fifo = absolutize("fifo");
+ FileSystemUtils.createEmptyFile(regular);
+ FilesystemUtils.mkfifo(fifo.toString(), 0777);
+ assertTrue(regular.isFile());
+ assertFalse(regular.isSpecialFile());
+ assertTrue(regular.stat().isFile());
+ assertFalse(regular.stat().isSpecialFile());
+ assertTrue(fifo.isFile());
+ assertTrue(fifo.isSpecialFile());
+ assertTrue(fifo.stat().isFile());
+ assertTrue(fifo.stat().isSpecialFile());
+ }
}