blob: 51b9b65f580aad1bce9ccacc5229ff3c8d3c2dc7 [file] [log] [blame]
// 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.common.base.Preconditions;
import com.google.devtools.build.lib.clock.Clock;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.vfs.FileStatus;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem.Errno;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem.InodeOrErrno;
import javax.annotation.Nullable;
/**
* 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.
*/
@ThreadSafe
public abstract class InMemoryContentInfo implements FileStatus, InodeOrErrno {
protected final Clock clock;
/**
* Stores the time when the file was last modified. This is atomically updated
* whenever the file changes, so all accesses must be synchronized.
*/
private long lastModifiedTime;
/**
* Stores the time when the file information was changed. This is atomically updated
* whenever the file changes, so all accesses must be synchronized.
*/
private long lastChangeTime;
/**
* Modifications to the isWritable field do not update the lastModifiedTime,
* so we don't need to synchronize; using volatile is enough.
*/
private volatile boolean isWritable = true;
private volatile boolean isExecutable = false;
private volatile boolean isReadable = true;
protected InMemoryContentInfo(Clock clock) {
this.clock = clock;
// When we create the file, it is modified.
markModificationTime();
}
/**
* Returns true if the current object is a directory.
*/
@Override
public abstract boolean isDirectory();
/**
* Returns true if the current object is a symbolic link.
*/
@Override
public abstract boolean isSymbolicLink();
/**
* 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.
*/
@Override
public abstract long getSize();
/**
* Returns the time when the entity denoted by the current object was last
* modified.
*/
@Override
public synchronized long getLastModifiedTime() {
return lastModifiedTime;
}
/**
* Returns the time when the entity denoted by the current object was last
* changed.
*/
@Override
public synchronized long getLastChangeTime() {
return lastChangeTime;
}
/**
* Returns the file node id for the given instance, emulated by the
* identity hash code.
*/
@Override
public long getNodeId() {
return System.identityHashCode(this);
}
@Override
public final int getPermissions() {
int permissions = 0;
// Emulate the default umask of 022.
if (isReadable) {
permissions |= 0444;
}
if (isWritable) {
permissions |= 0200;
}
if (isExecutable) {
permissions |= 0111;
}
return permissions;
}
@Override
public final InMemoryContentInfo inode() {
return this;
}
@Nullable
@Override
public final Errno error() {
return null;
}
@Override
public final boolean isError() {
return false;
}
@Override
public final InMemoryContentInfo inodeOrThrow(PathFragment path) {
return this;
}
/**
* Sets the time that denotes when the entity denoted by this object was last
* modified.
*/
synchronized void setLastModifiedTime(long newTime) {
lastModifiedTime = newTime;
markChangeTime();
}
/** Sets the last modification and change times to the current time. */
synchronized void markModificationTime() {
Preconditions.checkState(clock != null);
lastModifiedTime = clock.currentTimeMillis();
lastChangeTime = lastModifiedTime;
}
/** Sets the last change time to the current time. */
private synchronized void markChangeTime() {
Preconditions.checkState(clock != null);
lastChangeTime = clock.currentTimeMillis();
}
/**
* Sets whether the current file is readable.
*/
boolean isReadable() {
return isReadable;
}
/**
* Returns whether the current file is readable.
*/
void setReadable(boolean readable) {
isReadable = readable;
}
/**
* Sets whether the current file is writable.
*/
void setWritable(boolean writable) {
isWritable = writable;
markChangeTime();
}
/**
* Returns whether the current file is writable.
*/
boolean isWritable() {
return isWritable;
}
/**
* Sets whether the current file is executable.
*/
void setExecutable(boolean executable) {
isExecutable = executable;
markChangeTime();
}
/**
* Returns whether the current file is executable.
*/
boolean isExecutable() {
return isExecutable;
}
InMemoryDirectoryInfo asDirectory() {
throw new IllegalStateException("Not a directory: " + this);
}
}