diff options
author | Nathan Harmata <nharmata@google.com> | 2015-10-20 21:54:34 +0000 |
---|---|---|
committer | Han-Wen Nienhuys <hanwen@google.com> | 2015-10-21 14:39:08 +0000 |
commit | d8b6ff2dad1de2d98a407ecf67a34fe12e67d494 (patch) | |
tree | 030c5ebb89ca5835e76854ed51c0d2a75b04e4e3 /src | |
parent | 11ef8fc44821fb593a512eb5a3423013abbe39aa (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')
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()); + } } |