aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/native
diff options
context:
space:
mode:
authorGravatar tomlu <tomlu@google.com>2017-12-21 08:59:51 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2017-12-21 09:01:56 -0800
commitdecca2bd97f6a7eb6b2ca0412ddc792d57f0f424 (patch)
tree759006fa2a07d44d7a30f25ff3e9fc89370a229c /src/main/native
parent8fb14d5871088e32078812f21ef24189729e38cc (diff)
Add FileSystem#createDirectoryAndParents.
A native implementation of this (instead of using FileSystemUtils, which can only use public interfaces) should be more efficient and more easy to make correct. In particular, it should allow removing FileSystemUtils#createDirectoriesAndParents, which has poor thread safety characteristics. The latter method has a lot of logic that forces certain unnatural atomicity guarantees on createDirectory, and it also has logic that is conditional on sub-string content of exception messages. PiperOrigin-RevId: 179819623
Diffstat (limited to 'src/main/native')
-rw-r--r--src/main/native/unix_jni.cc120
1 files changed, 101 insertions, 19 deletions
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) {