aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/NativePosixFiles.java9
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/Path.java13
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java26
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystemWithCustomStat.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java17
-rw-r--r--src/main/native/unix_jni.cc120
10 files changed, 185 insertions, 29 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/unix/NativePosixFiles.java b/src/main/java/com/google/devtools/build/lib/unix/NativePosixFiles.java
index 781467d366..238ef4d2e4 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/NativePosixFiles.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/NativePosixFiles.java
@@ -242,6 +242,15 @@ public final class NativePosixFiles {
throws IOException;
/**
+ * Implements (effectively) mkdir -p.
+ *
+ * @param path the directory to recursively create.
+ * @param mode the mode with which to create the directories.
+ * @throws IOException if the directory creation failed for any reason.
+ */
+ public static native void mkdirs(String path, int mode) throws IOException;
+
+ /**
* Native wrapper around POSIX opendir(2)/readdir(3)/closedir(3) syscall.
*
* @param path the directory to read.
diff --git a/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java b/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java
index 7b82dcc06a..0af17cfb12 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java
@@ -325,6 +325,11 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat {
}
@Override
+ public void createDirectoryAndParents(Path path) throws IOException {
+ NativePosixFiles.mkdirs(path.toString(), 0777);
+ }
+
+ @Override
protected void createSymbolicLink(Path linkPath, PathFragment targetFragment)
throws IOException {
synchronized (linkPath) {
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 fef88b892b..e914663695 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
@@ -257,6 +257,12 @@ public abstract class FileSystem {
public abstract boolean createDirectory(Path path) throws IOException;
/**
+ * Creates all directories up to the path. See {@link Path#createDirectoryAndParents} for
+ * specification.
+ */
+ public abstract void createDirectoryAndParents(Path path) throws IOException;
+
+ /**
* Returns the size in bytes of the file denoted by {@code path}. See {@link
* Path#getFileSize(Symlinks)} for specification.
*
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 dca8b95906..4477275f1f 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
@@ -258,6 +258,12 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat {
}
}
+ @Override
+ public void createDirectoryAndParents(Path path) throws IOException {
+ java.nio.file.Path nioPath = getNioPath(path);
+ Files.createDirectories(nioPath);
+ }
+
private boolean linkExists(File file) {
String shortName = file.getName();
File parentFile = file.getParentFile();
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 1d3947dcdc..06a884b4a9 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
@@ -1026,6 +1026,19 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable {
return fileSystem.createDirectory(this);
}
+ /**
+ * Ensures that the directory with the name of the current path and all its ancestor directories
+ * exist.
+ *
+ * <p>Does not return whether the directory already existed or was created by some other
+ * concurrent call to this method.
+ *
+ * @throws IOException if the directory creation failed for any reason
+ */
+ public void createDirectoryAndParents() throws IOException {
+ fileSystem.createDirectoryAndParents(this);
+ }
+
/** Prefer to use {@link #createSymbolicLink(FileSystem, Path)}. */
@Deprecated
public void createSymbolicLink(Path target) throws IOException {
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 bfcc4f97e0..938f7a7ffb 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
@@ -17,20 +17,21 @@ import java.io.IOException;
import java.io.OutputStream;
/**
- * An abstract partial implementation of FileSystem for read-only
- * implementations.
+ * An abstract partial implementation of FileSystem for read-only implementations.
*
* <p>Any ReadonlyFileSystem does not support the following:
+ *
* <ul>
- * <li>{@link #createDirectory(Path)}</li>
- * <li>{@link #createSymbolicLink(Path, PathFragment)}</li>
- * <li>{@link #delete(Path)}</li>
- * <li>{@link #getOutputStream(Path)}</li>
- * <li>{@link #renameTo(Path, Path)}</li>
- * <li>{@link #setExecutable(Path, boolean)}</li>
- * <li>{@link #setLastModifiedTime(Path, long)}</li>
- * <li>{@link #setWritable(Path, boolean)}</li>
+ * <li>{@link #createDirectory(Path)}
+ * <li>{@link #createSymbolicLink(Path, PathFragment)}
+ * <li>{@link #delete(Path)}
+ * <li>{@link #getOutputStream(Path)}
+ * <li>{@link #renameTo(Path, Path)}
+ * <li>{@link #setExecutable(Path, boolean)}
+ * <li>{@link #setLastModifiedTime(Path, long)}
+ * <li>{@link #setWritable(Path, boolean)}
* </ul>
+ *
* The above calls will always result in an {@link IOException}.
*/
public abstract class ReadonlyFileSystem extends AbstractFileSystem {
@@ -91,6 +92,11 @@ public abstract class ReadonlyFileSystem extends AbstractFileSystem {
}
@Override
+ public void createDirectoryAndParents(Path path) throws IOException {
+ throw modificationException();
+ }
+
+ @Override
protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException {
throw modificationException();
}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystemWithCustomStat.java b/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystemWithCustomStat.java
index de5daca181..f9ca4ebb87 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystemWithCustomStat.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystemWithCustomStat.java
@@ -76,6 +76,11 @@ public abstract class ReadonlyFileSystemWithCustomStat extends AbstractFileSyste
}
@Override
+ public void createDirectoryAndParents(Path path) throws IOException {
+ throw modificationException();
+ }
+
+ @Override
protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException {
throw modificationException();
}
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 824e71dc69..569357bcb2 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
@@ -206,6 +206,13 @@ public class UnionFileSystem extends FileSystem {
}
@Override
+ public void createDirectoryAndParents(Path path) throws IOException {
+ checkModifiable(path);
+ FileSystem delegate = getDelegate(path);
+ delegate.createDirectoryAndParents(path);
+ }
+
+ @Override
protected long getFileSize(Path path, boolean followSymlinks) throws IOException {
path = followSymlinks ? internalResolveSymlink(path) : path;
FileSystem delegate = getDelegate(path);
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 ff6d88a387..0008423cc9 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
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.vfs.inmemoryfs;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
import com.google.devtools.build.lib.clock.Clock;
import com.google.devtools.build.lib.clock.JavaClock;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
@@ -624,6 +625,22 @@ public class InMemoryFileSystem extends FileSystem {
}
@Override
+ public synchronized void createDirectoryAndParents(Path path) throws IOException {
+ List<Path> subdirs = new ArrayList<>();
+ for (; !path.isRootDirectory(); path = path.getParentDirectory()) {
+ if (path.isDirectory()) {
+ break;
+ } else if (path.exists()) {
+ throw new IOException("Not a directory: " + path);
+ }
+ subdirs.add(path);
+ }
+ for (Path subdir : Lists.reverse(subdirs)) {
+ subdir.createDirectory();
+ }
+ }
+
+ @Override
protected void createSymbolicLink(Path path, PathFragment targetFragment)
throws IOException {
if (path.equals(getRootDirectory())) {
diff --git a/src/main/native/unix_jni.cc b/src/main/native/unix_jni.cc
index 395dcc16b7..571e22b72e 100644
--- a/src/main/native/unix_jni.cc
+++ b/src/main/native/unix_jni.cc
@@ -81,28 +81,28 @@ static jstring NewStringLatin1(JNIEnv *env, const char *str) {
* are replaced by '?'. Must be followed by a call to
* ReleaseStringLatin1Chars.
*/
-static const char *GetStringLatin1Chars(JNIEnv *env, jstring jstr) {
- jint len = env->GetStringLength(jstr);
- const jchar *str = env->GetStringCritical(jstr, NULL);
- if (str == NULL) {
- return NULL;
- }
+static char *GetStringLatin1Chars(JNIEnv *env, jstring jstr) {
+ jint len = env->GetStringLength(jstr);
+ const jchar *str = env->GetStringCritical(jstr, NULL);
+ if (str == NULL) {
+ return NULL;
+ }
- char *result = reinterpret_cast<char *>(malloc(len + 1));
- if (result == NULL) {
- env->ReleaseStringCritical(jstr, str);
- ::PostException(env, ENOMEM, "Out of memory in GetStringLatin1Chars");
- return NULL;
- }
+ char *result = reinterpret_cast<char *>(malloc(len + 1));
+ if (result == NULL) {
+ env->ReleaseStringCritical(jstr, str);
+ ::PostException(env, ENOMEM, "Out of memory in GetStringLatin1Chars");
+ return NULL;
+ }
- for (int i = 0; i < len; i++) {
- jchar unicode = str[i]; // (unsigned)
- result[i] = unicode <= 0x00ff ? unicode : '?';
- }
+ for (int i = 0; i < len; i++) {
+ jchar unicode = str[i]; // (unsigned)
+ result[i] = unicode <= 0x00ff ? unicode : '?';
+ }
- result[len] = 0;
- env->ReleaseStringCritical(jstr, str);
- return result;
+ result[len] = 0;
+ env->ReleaseStringCritical(jstr, str);
+ return result;
}
/**
@@ -536,6 +536,88 @@ Java_com_google_devtools_build_lib_unix_NativePosixFiles_mkdir(JNIEnv *env,
return result;
}
+/*
+ * Class: com.google.devtools.build.lib.unix.NativePosixFiles
+ * Method: mkdirs
+ * Signature: (Ljava/lang/String;I)V
+ * Throws: java.io.IOException
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_devtools_build_lib_unix_NativePosixFiles_mkdirs(JNIEnv *env,
+ jclass clazz,
+ jstring path,
+ int mode) {
+ char *path_chars = GetStringLatin1Chars(env, path);
+ portable_stat_struct statbuf;
+ int len;
+ char *p;
+
+ // First, check if the directory already exists and early-out.
+ if (portable_stat(path_chars, &statbuf) == 0) {
+ if (!S_ISDIR(statbuf.st_mode)) {
+ // Exists but is not a directory.
+ ::PostFileException(env, ENOTDIR, path_chars);
+ }
+ goto cleanup;
+ } else if (errno != ENOENT) {
+ ::PostFileException(env, errno, path_chars);
+ goto cleanup;
+ }
+
+ // Find the first directory that already exists and leave a pointer just past
+ // it.
+ len = strlen(path_chars);
+ p = path_chars + len - 1;
+ for (; p > path_chars; --p) {
+ if (*p == '/') {
+ *p = 0;
+ int res = portable_stat(path_chars, &statbuf);
+ *p = '/';
+ if (res == 0) {
+ // Exists and must be a directory, or the initial stat would have failed
+ // with ENOTDIR.
+ break;
+ } else if (errno != ENOENT) {
+ ::PostFileException(env, errno, path_chars);
+ goto cleanup;
+ }
+ }
+ }
+ // p now points at the '/' after the last directory that exists.
+ // Successively create each directory
+ for (const char *end = path_chars + len; p < end; ++p) {
+ if (*p == '/') {
+ *p = 0;
+ int res = ::mkdir(path_chars, mode);
+ *p = '/';
+ // EEXIST is fine, just means we're racing to create the directory.
+ // Note that somebody could have raced to create a file here, but that
+ // will get handled by a ENOTDIR by a subsequent mkdir call.
+ if (res != 0 && errno != EEXIST) {
+ ::PostFileException(env, errno, path_chars);
+ goto cleanup;
+ }
+ }
+ }
+ if (::mkdir(path_chars, mode) != 0) {
+ if (errno != EEXIST) {
+ ::PostFileException(env, errno, path_chars);
+ goto cleanup;
+ }
+ if (portable_stat(path_chars, &statbuf) != 0) {
+ ::PostFileException(env, errno, path_chars);
+ goto cleanup;
+ }
+ if (!S_ISDIR(statbuf.st_mode)) {
+ // Exists but is not a directory.
+ ::PostFileException(env, ENOTDIR, path_chars);
+ goto cleanup;
+ }
+ }
+cleanup:
+ ReleaseStringLatin1Chars(path_chars);
+}
+
static jobject NewDirents(JNIEnv *env,
jobjectArray names,
jbyteArray types) {