|  | // 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. | 
|  |  | 
|  | #include "src/main/native/unix_jni.h" | 
|  |  | 
|  | #include <dirent.h> | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <ifaddrs.h> | 
|  | #include <jni.h> | 
|  | #include <limits.h> | 
|  | #include <netdb.h> | 
|  | #include <netinet/in.h> | 
|  | #include <stdint.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <sys/resource.h> | 
|  | #include <sys/socket.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/syscall.h> | 
|  | #include <sys/time.h> | 
|  | #include <sys/types.h> | 
|  | #include <time.h> | 
|  | #include <unistd.h> | 
|  | #include <utime.h> | 
|  |  | 
|  | // Linting disabled for this line because for google code we could use | 
|  | // absl::Mutex but we cannot yet because Bazel doesn't depend on absl. | 
|  | #include <mutex>  // NOLINT | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "src/main/cpp/util/logging.h" | 
|  | #include "src/main/cpp/util/port.h" | 
|  | #include "src/main/native/latin1_jni_path.h" | 
|  | #include "src/main/native/macros.h" | 
|  |  | 
|  | #if defined(O_DIRECTORY) | 
|  | #define PORTABLE_O_DIRECTORY O_DIRECTORY | 
|  | #else | 
|  | #define PORTABLE_O_DIRECTORY 0 | 
|  | #endif | 
|  |  | 
|  | namespace blaze_jni { | 
|  |  | 
|  | struct DIROrError { | 
|  | DIR *dir; | 
|  | int error; | 
|  | }; | 
|  |  | 
|  | static void PostException(JNIEnv *env, const char *exception_classname, | 
|  | const std::string &message) { | 
|  | jclass exception_class = env->FindClass(exception_classname); | 
|  | bool success = false; | 
|  | if (exception_class != nullptr) { | 
|  | success = env->ThrowNew(exception_class, message.c_str()) == 0; | 
|  | } | 
|  | if (!success) { | 
|  | BAZEL_LOG(FATAL) << "Failed to throw Java exception from JNI: " | 
|  | << message.c_str(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // See unix_jni.h. | 
|  | void PostException(JNIEnv *env, int error_number, const std::string& message) { | 
|  | // Keep consistent with package-info.html! | 
|  | // | 
|  | // See /usr/include/asm/errno.h for UNIX error messages. | 
|  | // Select the most appropriate Java exception for a given UNIX error number. | 
|  | // (Consistent with errors generated by java.io package.) | 
|  | const char *exception_classname; | 
|  | switch (error_number) { | 
|  | case EFAULT:  // Illegal pointer (unlikely; perhaps from or via FUSE?) | 
|  | exception_classname = "java/lang/IllegalArgumentException"; | 
|  | break; | 
|  | case ETIMEDOUT:  // Local socket timed out | 
|  | exception_classname = "java/net/SocketTimeoutException"; | 
|  | break; | 
|  | case ENOENT:  // No such file or directory | 
|  | exception_classname = "java/io/FileNotFoundException"; | 
|  | break; | 
|  | case EACCES:  // Permission denied | 
|  | exception_classname = | 
|  | "com/google/devtools/build/lib/vfs/FileAccessException"; | 
|  | break; | 
|  | case EPERM:   // Operation not permitted | 
|  | exception_classname = | 
|  | "com/google/devtools/build/lib/unix/FilePermissionException"; | 
|  | break; | 
|  | case EINTR:   // Interrupted system call | 
|  | exception_classname = "java/io/InterruptedIOException"; | 
|  | break; | 
|  | case ENOMEM:  // Out of memory | 
|  | exception_classname = "java/lang/OutOfMemoryError"; | 
|  | break; | 
|  | case ENOSYS:   // Function not implemented | 
|  | case ENOTSUP:  // Operation not supported on transport endpoint | 
|  | // (aka EOPNOTSUPP) | 
|  | exception_classname = "java/lang/UnsupportedOperationException"; | 
|  | break; | 
|  | case EINVAL:  // Invalid argument | 
|  | exception_classname = | 
|  | "com/google/devtools/build/lib/unix/InvalidArgumentIOException"; | 
|  | break; | 
|  | case ELOOP:  // Too many symbolic links encountered | 
|  | exception_classname = | 
|  | "com/google/devtools/build/lib/vfs/FileSymlinkLoopException"; | 
|  | break; | 
|  | case EBADF:         // Bad file number or descriptor already closed. | 
|  | case ENAMETOOLONG:  // File name too long | 
|  | case ENODATA:    // No data available | 
|  | #if defined(EMULTIHOP) | 
|  | case EMULTIHOP:  // Multihop attempted | 
|  | #endif | 
|  | case ENOLINK:    // Link has been severed | 
|  | case EIO:        // I/O error | 
|  | case EAGAIN:     // Try again | 
|  | case EFBIG:      // File too large | 
|  | case EPIPE:      // Broken pipe | 
|  | case ENOSPC:     // No space left on device | 
|  | case EXDEV:      // Cross-device link | 
|  | case EROFS:      // Read-only file system | 
|  | case EEXIST:     // File exists | 
|  | case EMLINK:     // Too many links | 
|  | case EISDIR:     // Is a directory | 
|  | case ENOTDIR:    // Not a directory | 
|  | case ENOTEMPTY:  // Directory not empty | 
|  | case EBUSY:      // Device or resource busy | 
|  | case ENFILE:     // File table overflow | 
|  | case EMFILE:     // Too many open files | 
|  | default: | 
|  | exception_classname = "java/io/IOException"; | 
|  | } | 
|  | PostException(env, exception_classname, | 
|  | message + " (" + ErrorMessage(error_number) + ")"); | 
|  | } | 
|  |  | 
|  | static void PostAssertionError(JNIEnv *env, const std::string& message) { | 
|  | PostException(env, "java/lang/AssertionError", message); | 
|  | } | 
|  |  | 
|  | // Throws RuntimeExceptions for IO operations which fail unexpectedly. | 
|  | // See package-info.html. | 
|  | // Returns true iff an exception was thrown. | 
|  | static bool PostRuntimeException(JNIEnv *env, int error_number, | 
|  | const char *file_path) { | 
|  | const char *exception_classname; | 
|  | switch (error_number) { | 
|  | case EFAULT:   // Illegal pointer--not likely | 
|  | case EBADF:    // Bad file number | 
|  | exception_classname = "java/lang/IllegalArgumentException"; | 
|  | break; | 
|  | case ENOMEM:   // Out of memory | 
|  | exception_classname = "java/lang/OutOfMemoryError"; | 
|  | break; | 
|  | case ENOTSUP:  // Operation not supported on transport endpoint | 
|  | // (aka EOPNOTSUPP) | 
|  | exception_classname = "java/lang/UnsupportedOperationException"; | 
|  | break; | 
|  | default: | 
|  | exception_classname = nullptr; | 
|  | } | 
|  |  | 
|  | if (exception_classname == nullptr) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | jclass exception_class = env->FindClass(exception_classname); | 
|  | if (exception_class != nullptr) { | 
|  | std::string message(file_path); | 
|  | message += " ("; | 
|  | message += ErrorMessage(error_number); | 
|  | message += ")"; | 
|  | env->ThrowNew(exception_class, message.c_str()); | 
|  | return true; | 
|  | } else { | 
|  | BAZEL_LOG(FATAL) << "Unable to find exception_class: " | 
|  | << exception_classname; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | static JavaVM *GetJavaVM(JNIEnv *env) { | 
|  | static JavaVM *java_vm = nullptr; | 
|  | static std::mutex java_vm_mtx; | 
|  | std::lock_guard<std::mutex> lock(java_vm_mtx); | 
|  | if (env != nullptr) { | 
|  | JavaVM *env_java_vm; | 
|  | jint value = env->GetJavaVM(&env_java_vm); | 
|  | if (value != 0) { | 
|  | return nullptr; | 
|  | } | 
|  | if (java_vm == nullptr) { | 
|  | java_vm = env_java_vm; | 
|  | } else if (java_vm != env_java_vm) { | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  | return java_vm; | 
|  | } | 
|  |  | 
|  | static void PerformIntegerValueCallback(jobject object, const char *callback, | 
|  | int value) { | 
|  | JavaVM *java_vm = GetJavaVM(nullptr); | 
|  | JNIEnv *java_env; | 
|  | int status = java_vm->GetEnv((void **)&java_env, JNI_VERSION_1_8); | 
|  | bool attach_current_thread = false; | 
|  | if (status == JNI_EDETACHED) { | 
|  | attach_current_thread = true; | 
|  | } else { | 
|  | BAZEL_CHECK_EQ(status, JNI_OK); | 
|  | } | 
|  | if (attach_current_thread) { | 
|  | BAZEL_CHECK_EQ(java_vm->AttachCurrentThread((void **)&java_env, nullptr), | 
|  | 0); | 
|  | } | 
|  | jclass clazz = java_env->GetObjectClass(object); | 
|  | BAZEL_CHECK_NE(clazz, nullptr); | 
|  | jmethodID method_id = java_env->GetMethodID(clazz, callback, "(I)V"); | 
|  | BAZEL_CHECK_NE(method_id, nullptr); | 
|  | java_env->CallVoidMethod(object, method_id, value); | 
|  |  | 
|  | if (attach_current_thread) { | 
|  | BAZEL_CHECK_EQ(java_vm->DetachCurrentThread(), JNI_OK); | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO(bazel-team): split out all the FileSystem class's native methods | 
|  | // into a separate source file, fsutils.cc. | 
|  |  | 
|  | extern "C" JNIEXPORT jstring JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_readlink(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | char target[PATH_MAX] = ""; | 
|  | jstring r = nullptr; | 
|  | if (readlink(path_chars, target, arraysize(target)) == -1) { | 
|  | PostException(env, errno, path_chars); | 
|  | } else { | 
|  | r = NewStringLatin1(env, target); | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_chmod(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path, | 
|  | jint mode) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | if (chmod(path_chars, static_cast<int>(mode)) == -1) { | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | } | 
|  |  | 
|  | static void link_common(JNIEnv *env, | 
|  | jstring oldpath, | 
|  | jstring newpath, | 
|  | int (*link_function)(const char *, const char *)) { | 
|  | const char *oldpath_chars = GetStringLatin1Chars(env, oldpath); | 
|  | const char *newpath_chars = GetStringLatin1Chars(env, newpath); | 
|  | if (link_function(oldpath_chars, newpath_chars) == -1) { | 
|  | PostException(env, errno, newpath_chars); | 
|  | } | 
|  | ReleaseStringLatin1Chars(oldpath_chars); | 
|  | ReleaseStringLatin1Chars(newpath_chars); | 
|  | } | 
|  |  | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_link(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring oldpath, | 
|  | jstring newpath) { | 
|  | link_common(env, oldpath, newpath, ::link); | 
|  | } | 
|  |  | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_symlink(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring oldpath, | 
|  | jstring newpath) { | 
|  | link_common(env, oldpath, newpath, ::symlink); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | static jclass file_status_class = nullptr; | 
|  | static jclass errno_file_status_class = nullptr; | 
|  | static jmethodID file_status_class_ctor = nullptr; | 
|  | static jmethodID errno_file_status_class_no_error_ctor = nullptr; | 
|  | static jmethodID errno_file_status_class_errorno_ctor = nullptr; | 
|  | static jclass dirents_class = nullptr; | 
|  | static jmethodID dirents_ctor = nullptr; | 
|  |  | 
|  | static jclass makeStaticClass(JNIEnv *env, const char *name) { | 
|  | jclass lookup_result = env->FindClass(name); | 
|  | BAZEL_CHECK_NE(lookup_result, nullptr); | 
|  | return static_cast<jclass>(env->NewGlobalRef(lookup_result)); | 
|  | } | 
|  |  | 
|  | static jmethodID getConstructorID(JNIEnv *env, jclass clazz, | 
|  | const char *parameters) { | 
|  | jmethodID method = env->GetMethodID(clazz, "<init>", parameters); | 
|  | BAZEL_CHECK_NE(method, nullptr); | 
|  | return method; | 
|  | } | 
|  |  | 
|  | static jobject NewFileStatus(JNIEnv *env, | 
|  | const portable_stat_struct &stat_ref) { | 
|  | return env->NewObject( | 
|  | file_status_class, file_status_class_ctor, | 
|  | static_cast<jint>(stat_ref.st_mode), | 
|  | static_cast<jlong>(StatEpochMilliseconds(stat_ref, STAT_ATIME)), | 
|  | static_cast<jlong>(StatEpochMilliseconds(stat_ref, STAT_MTIME)), | 
|  | static_cast<jlong>(StatEpochMilliseconds(stat_ref, STAT_CTIME)), | 
|  | static_cast<jlong>(stat_ref.st_size), static_cast<jint>(stat_ref.st_dev), | 
|  | static_cast<jlong>(stat_ref.st_ino)); | 
|  | } | 
|  |  | 
|  | static jobject NewErrnoFileStatus(JNIEnv *env, | 
|  | int saved_errno, | 
|  | const portable_stat_struct &stat_ref) { | 
|  | if (saved_errno != 0) { | 
|  | return env->NewObject(errno_file_status_class, | 
|  | errno_file_status_class_errorno_ctor, saved_errno); | 
|  | } | 
|  | return env->NewObject( | 
|  | errno_file_status_class, errno_file_status_class_no_error_ctor, | 
|  | static_cast<jint>(stat_ref.st_mode), | 
|  | static_cast<jlong>(StatEpochMilliseconds(stat_ref, STAT_ATIME)), | 
|  | static_cast<jlong>(StatEpochMilliseconds(stat_ref, STAT_MTIME)), | 
|  | static_cast<jlong>(StatEpochMilliseconds(stat_ref, STAT_CTIME)), | 
|  | static_cast<jlong>(stat_ref.st_size), static_cast<jint>(stat_ref.st_dev), | 
|  | static_cast<jlong>(stat_ref.st_ino)); | 
|  | } | 
|  |  | 
|  | static void SetIntField(JNIEnv *env, | 
|  | const jclass &clazz, | 
|  | const jobject &object, | 
|  | const char *name, | 
|  | int val) { | 
|  | jfieldID fid = env->GetFieldID(clazz, name, "I"); | 
|  | BAZEL_CHECK_NE(fid, nullptr); | 
|  | env->SetIntField(object, fid, val); | 
|  | } | 
|  |  | 
|  | // RAII class for jstring. | 
|  | class JStringLatin1Holder { | 
|  | const char *const chars; | 
|  |  | 
|  | public: | 
|  | JStringLatin1Holder(JNIEnv *env, jstring string) | 
|  | : chars(GetStringLatin1Chars(env, string)) {} | 
|  |  | 
|  | ~JStringLatin1Holder() { ReleaseStringLatin1Chars(chars); } | 
|  |  | 
|  | operator const char *() const { return chars; } | 
|  |  | 
|  | operator std::string() const { return chars; } | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_initJNIClasses( | 
|  | JNIEnv *env, jclass clazz) { | 
|  | file_status_class = | 
|  | makeStaticClass(env, "com/google/devtools/build/lib/unix/FileStatus"); | 
|  | errno_file_status_class = makeStaticClass( | 
|  | env, "com/google/devtools/build/lib/unix/ErrnoFileStatus"); | 
|  | file_status_class_ctor = | 
|  | getConstructorID(env, file_status_class, "(IJJJJIJ)V"); | 
|  | errno_file_status_class_no_error_ctor = | 
|  | getConstructorID(env, errno_file_status_class, "(IJJJJIJ)V"); | 
|  | errno_file_status_class_errorno_ctor = | 
|  | getConstructorID(env, errno_file_status_class, "(I)V"); | 
|  | dirents_class = makeStaticClass( | 
|  | env, "com/google/devtools/build/lib/unix/NativePosixFiles$Dirents"); | 
|  | dirents_ctor = | 
|  | getConstructorID(env, dirents_class, "([Ljava/lang/String;[B)V"); | 
|  | } | 
|  |  | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_ErrnoFileStatus_00024ErrnoConstants_initErrnoConstants(  // NOLINT | 
|  | JNIEnv *env, jobject errno_constants) { | 
|  | jclass clazz = env->GetObjectClass(errno_constants); | 
|  | SetIntField(env, clazz, errno_constants, "errnoENOENT", ENOENT); | 
|  | SetIntField(env, clazz, errno_constants, "errnoEACCES", EACCES); | 
|  | SetIntField(env, clazz, errno_constants, "errnoELOOP", ELOOP); | 
|  | SetIntField(env, clazz, errno_constants, "errnoENOTDIR", ENOTDIR); | 
|  | SetIntField(env, clazz, errno_constants, "errnoENAMETOOLONG", ENAMETOOLONG); | 
|  | SetIntField(env, clazz, errno_constants, "errnoENODATA", ENODATA); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | static jobject StatCommon(JNIEnv *env, jstring path, | 
|  | int (*stat_function)(const char *, | 
|  | portable_stat_struct *), | 
|  | bool should_throw) { | 
|  | portable_stat_struct statbuf; | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | int r; | 
|  | int saved_errno = 0; | 
|  | while ((r = stat_function(path_chars, &statbuf)) == -1 && errno == EINTR) { } | 
|  | if (r == -1) { | 
|  | // Save errno immediately, before we do any other syscalls | 
|  | saved_errno = errno; | 
|  |  | 
|  | // EACCES ENOENT ENOTDIR ELOOP -> IOException | 
|  | // ENAMETOOLONGEFAULT          -> RuntimeException | 
|  | // ENOMEM                      -> OutOfMemoryError | 
|  |  | 
|  | if (PostRuntimeException(env, saved_errno, path_chars)) { | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | return nullptr; | 
|  | } else if (should_throw) { | 
|  | PostException(env, saved_errno, path_chars); | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  |  | 
|  | return should_throw | 
|  | ? NewFileStatus(env, statbuf) | 
|  | : NewErrnoFileStatus(env, saved_errno, statbuf); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    stat | 
|  | * Signature: (Ljava/lang/String;)Lcom/google/devtools/build/lib/unix/FileStatus; | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT jobject JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_stat(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path) { | 
|  | return StatCommon(env, path, portable_stat, true); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    lstat | 
|  | * Signature: (Ljava/lang/String;)Lcom/google/devtools/build/lib/unix/FileStatus; | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT jobject JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_lstat(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path) { | 
|  | return StatCommon(env, path, portable_lstat, true); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    statNullable | 
|  | * Signature: (Ljava/lang/String;)Lcom/google/devtools/build/lib/unix/FileStatus; | 
|  | */ | 
|  | extern "C" JNIEXPORT jobject JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_errnoStat(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path) { | 
|  | return StatCommon(env, path, portable_stat, false); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    lstatNullable | 
|  | * Signature: (Ljava/lang/String;)Lcom/google/devtools/build/lib/unix/FileStatus; | 
|  | */ | 
|  | extern "C" JNIEXPORT jobject JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_errnoLstat(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path) { | 
|  | return StatCommon(env, path, portable_lstat, false); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    utimensat | 
|  | * Signature: (Ljava/lang/String;ZJ)V | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_utimensat( | 
|  | JNIEnv *env, jclass clazz, jstring path, jboolean now, jlong millis) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | int64_t sec = millis / 1000; | 
|  | int32_t nsec = (millis % 1000) * 1000000; | 
|  | struct timespec spec[2] = { | 
|  | // Do not set atime. | 
|  | {0, UTIME_OMIT}, | 
|  | // Set mtime to now if `now` is true, otherwise to the specified time. | 
|  | {sec, now ? UTIME_NOW : nsec}, | 
|  | }; | 
|  | if (::utimensat(AT_FDCWD, path_chars, spec, 0) == -1) { | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    umask | 
|  | * Signature: (I)I | 
|  | */ | 
|  | extern "C" JNIEXPORT jint JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_umask(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jint new_umask) { | 
|  | return ::umask(new_umask); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    mkdir | 
|  | * Signature: (Ljava/lang/String;I)Z | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT jboolean JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_mkdir(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path, | 
|  | jint mode) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | jboolean result = true; | 
|  | if (::mkdir(path_chars, mode) == -1) { | 
|  | // EACCES ENOENT ELOOP | 
|  | // ENOSPC ENOTDIR EPERM EROFS     -> IOException | 
|  | // EFAULT ENAMETOOLONG            -> RuntimeException | 
|  | // ENOMEM                         -> OutOfMemoryError | 
|  | // EEXIST                         -> return false | 
|  | if (errno == EEXIST) { | 
|  | result = false; | 
|  | } else { | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    mkdirWritable | 
|  | * Signature: (Ljava/lang/String;I)Z | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT jboolean JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_mkdirWritable( | 
|  | JNIEnv *env, jclass clazz, jstring path) { | 
|  | JStringLatin1Holder path_chars(env, path); | 
|  |  | 
|  | portable_stat_struct statbuf; | 
|  | int r; | 
|  | do { | 
|  | r = portable_lstat(path_chars, &statbuf); | 
|  | } while (r != 0 && errno == EINTR); | 
|  |  | 
|  | if (r != 0) { | 
|  | if (errno != ENOENT) { | 
|  | PostException(env, errno, path_chars); | 
|  | return false; | 
|  | } | 
|  | // Directory does not exist. | 
|  | // Use 0777 so that the permissions can be overridden by umask(2). | 
|  | if (::mkdir(path_chars, 0777) == -1) { | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | return true; | 
|  | } | 
|  | // Path already exists, but might not be a directory. | 
|  | if (!S_ISDIR(statbuf.st_mode)) { | 
|  | PostException(env, ENOTDIR, path_chars); | 
|  | return false; | 
|  | } | 
|  | // Make sure the permissions are correct. | 
|  | // Avoid touching permissions for group/other, which may have been overridden | 
|  | // by umask(2) when this directory was originally created. | 
|  | if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) { | 
|  | if (::chmod(path_chars, statbuf.st_mode | S_IRWXU) == -1) { | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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. | 
|  | PostException(env, ENOTDIR, path_chars); | 
|  | } | 
|  | goto cleanup; | 
|  | } else if (errno != ENOENT) { | 
|  | PostException(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) { | 
|  | PostException(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) { | 
|  | PostException(env, errno, path_chars); | 
|  | goto cleanup; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (::mkdir(path_chars, mode) != 0) { | 
|  | if (errno != EEXIST) { | 
|  | PostException(env, errno, path_chars); | 
|  | goto cleanup; | 
|  | } | 
|  | if (portable_stat(path_chars, &statbuf) != 0) { | 
|  | PostException(env, errno, path_chars); | 
|  | goto cleanup; | 
|  | } | 
|  | if (!S_ISDIR(statbuf.st_mode)) { | 
|  | // Exists but is not a directory. | 
|  | PostException(env, ENOTDIR, path_chars); | 
|  | goto cleanup; | 
|  | } | 
|  | } | 
|  | cleanup: | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | static jobject NewDirents(JNIEnv *env, | 
|  | jobjectArray names, | 
|  | jbyteArray types) { | 
|  | return env->NewObject(dirents_class, dirents_ctor, names, types); | 
|  | } | 
|  |  | 
|  | static char GetDirentType(struct dirent *entry, | 
|  | int dirfd, | 
|  | bool follow_symlinks) { | 
|  | switch (entry->d_type) { | 
|  | case DT_REG: | 
|  | return 'f'; | 
|  | case DT_DIR: | 
|  | return 'd'; | 
|  | case DT_LNK: | 
|  | if (!follow_symlinks) { | 
|  | return 's'; | 
|  | } | 
|  | FALLTHROUGH_INTENDED; | 
|  | case DT_UNKNOWN: | 
|  | portable_stat_struct statbuf; | 
|  | if (portable_fstatat(dirfd, entry->d_name, &statbuf, 0) == 0) { | 
|  | if (S_ISREG(statbuf.st_mode)) return 'f'; | 
|  | if (S_ISDIR(statbuf.st_mode)) return 'd'; | 
|  | } | 
|  | // stat failed or returned something weird; fall through | 
|  | FALLTHROUGH_INTENDED; | 
|  | default: | 
|  | return '?'; | 
|  | } | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    readdir | 
|  | * Signature: (Ljava/lang/String;Z)Lcom/google/devtools/build/lib/unix/Dirents; | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT jobject JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_readdir(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path, | 
|  | jchar read_types) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | DIR *dirh; | 
|  | while ((dirh = ::opendir(path_chars)) == nullptr && errno == EINTR) { | 
|  | } | 
|  | if (dirh == nullptr) { | 
|  | // EACCES EMFILE ENFILE ENOENT ENOTDIR -> IOException | 
|  | // ENOMEM                              -> OutOfMemoryError | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | if (dirh == nullptr) { | 
|  | return nullptr; | 
|  | } | 
|  | int fd = dirfd(dirh); | 
|  |  | 
|  | std::vector<std::string> entries; | 
|  | std::vector<jbyte> types; | 
|  | for (;;) { | 
|  | // Clear errno beforehand.  Because readdir() is not required to clear it at | 
|  | // EOF, this is the only way to reliably distinguish EOF from error. | 
|  | errno = 0; | 
|  | struct dirent *entry = ::readdir(dirh); | 
|  | if (entry == nullptr) { | 
|  | if (errno == 0) break;  // EOF | 
|  | // It is unclear whether an error can also skip some records. | 
|  | // That does not appear to happen with glibc, at least. | 
|  | if (errno == EINTR) continue;  // interrupted by a signal | 
|  | if (errno == EIO) continue;  // glibc returns this on transient errors | 
|  | // Otherwise, this is a real error we should report. | 
|  | PostException(env, errno, path_chars); | 
|  | ::closedir(dirh); | 
|  | return nullptr; | 
|  | } | 
|  | // Omit . and .. from results. | 
|  | if (entry->d_name[0] == '.') { | 
|  | if (entry->d_name[1] == '\0') continue; | 
|  | if (entry->d_name[1] == '.' && entry->d_name[2] == '\0') continue; | 
|  | } | 
|  | entries.push_back(entry->d_name); | 
|  | if (read_types != 'n') { | 
|  | types.push_back(GetDirentType(entry, fd, read_types == 'f')); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (::closedir(dirh) < 0 && errno != EINTR) { | 
|  | PostException(env, errno, path_chars); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | size_t len = entries.size(); | 
|  | jclass jlStringClass = env->GetObjectClass(path); | 
|  | jobjectArray names_obj = env->NewObjectArray(len, jlStringClass, nullptr); | 
|  | if (names_obj == nullptr && env->ExceptionOccurred()) { | 
|  | return nullptr;  // async exception! | 
|  | } | 
|  |  | 
|  | for (size_t ii = 0; ii < len; ++ii) { | 
|  | jstring s = NewStringLatin1(env, entries[ii].c_str()); | 
|  | if (s == nullptr && env->ExceptionOccurred()) { | 
|  | return nullptr;  // async exception! | 
|  | } | 
|  | env->SetObjectArrayElement(names_obj, ii, s); | 
|  | } | 
|  |  | 
|  | jbyteArray types_obj = nullptr; | 
|  | if (read_types != 'n') { | 
|  | BAZEL_CHECK_EQ(len, types.size()); | 
|  | types_obj = env->NewByteArray(len); | 
|  | BAZEL_CHECK_NE(types_obj, nullptr); | 
|  | if (len > 0) { | 
|  | env->SetByteArrayRegion(types_obj, 0, len, &types[0]); | 
|  | } | 
|  | } | 
|  |  | 
|  | return NewDirents(env, names_obj, types_obj); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    rename | 
|  | * Signature: (Ljava/lang/String;Ljava/lang/String;)V | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_rename(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring oldpath, | 
|  | jstring newpath) { | 
|  | const char *oldpath_chars = GetStringLatin1Chars(env, oldpath); | 
|  | const char *newpath_chars = GetStringLatin1Chars(env, newpath); | 
|  | if (::rename(oldpath_chars, newpath_chars) == -1) { | 
|  | // EISDIR EXDEV ENOTEMPTY EEXIST EBUSY | 
|  | // EINVAL EMLINK ENOTDIR EACCES EPERM | 
|  | // ENOENT EROFS ELOOP ENOSPC           -> IOException | 
|  | // EFAULT ENAMETOOLONG                 -> RuntimeException | 
|  | // ENOMEM                              -> OutOfMemoryError | 
|  | std::string filename(std::string(oldpath_chars) + " -> " + newpath_chars); | 
|  | PostException(env, errno, filename); | 
|  | } | 
|  | ReleaseStringLatin1Chars(oldpath_chars); | 
|  | ReleaseStringLatin1Chars(newpath_chars); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    remove | 
|  | * Signature: (Ljava/lang/String;)V | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT bool JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_remove(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | if (path_chars == nullptr) { | 
|  | return false; | 
|  | } | 
|  | bool ok = remove(path_chars) != -1; | 
|  | if (!ok) { | 
|  | if (errno != ENOENT && errno != ENOTDIR) { | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | return ok; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    mkfifo | 
|  | * Signature: (Ljava/lang/String;I)V | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_mkfifo(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path, | 
|  | jint mode) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | if (mkfifo(path_chars, mode) == -1) { | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | // Posts an exception generated by the DeleteTreesBelow algorithm and its helper | 
|  | // functions. | 
|  | // | 
|  | // This is just a convenience wrapper over PostException to format the | 
|  | // path that caused an error only when necessary, as we keep that path tokenized | 
|  | // throughout the deletion process. | 
|  | // | 
|  | // env is the JNI environment in which to post the exception. error and function | 
|  | // capture the errno value and the name of the system function that triggered | 
|  | // it. The faulty path is specified by all the components of dir_path and the | 
|  | // optional entry subcomponent, which may be NULL. | 
|  | static void PostDeleteTreesBelowException( | 
|  | JNIEnv* env, int error, const char* function, | 
|  | const std::vector<std::string>& dir_path, const char* entry) { | 
|  | std::vector<std::string>::const_iterator iter = dir_path.begin(); | 
|  | std::string path; | 
|  | if (iter != dir_path.end()) { | 
|  | path = *iter; | 
|  | while (++iter != dir_path.end()) { | 
|  | path += "/"; | 
|  | path += *iter; | 
|  | } | 
|  | if (entry != nullptr) { | 
|  | path += "/"; | 
|  | path += entry; | 
|  | } | 
|  | } else { | 
|  | // When scanning the top-level directory given to DeleteTreesBelow, the | 
|  | // dir_path buffer is still empty but we have the full path in entry. | 
|  | path = entry; | 
|  | } | 
|  | BAZEL_CHECK(!env->ExceptionOccurred()); | 
|  | PostException(env, errno, std::string(function) + " (" + path + ")"); | 
|  | } | 
|  |  | 
|  | // Tries to open a directory and, if the first attempt fails, retries after | 
|  | // granting extra permissions to the directory. | 
|  | // | 
|  | // The directory to open is identified by the open descriptor of the parent | 
|  | // directory (dir_fd) and the subpath to resolve within that directory (entry). | 
|  | // dir_path contains the path components that were used when opening dir_fd and | 
|  | // is only used for error reporting purposes. | 
|  | // | 
|  | // Returns a directory handle on success or an errno on error. If the error is | 
|  | // other than ENOENT, posts an exception before returning. | 
|  | static DIROrError ForceOpendir(JNIEnv *env, | 
|  | const std::vector<std::string> &dir_path, | 
|  | const int dir_fd, const char *entry) { | 
|  | static const int flags = O_RDONLY | O_NOFOLLOW | PORTABLE_O_DIRECTORY; | 
|  | int fd = openat(dir_fd, entry, flags); | 
|  | if (fd == -1) { | 
|  | if (errno == ENOENT) { | 
|  | return {nullptr, errno}; | 
|  | } | 
|  | // If dir_fd is a readable but non-executable directory containing entry, we | 
|  | // could have obtained entry by readdir()-ing, but any attempt to open or | 
|  | // stat the entry would fail with EACCESS. In this case, we need to fix the | 
|  | // permissions on dir_fd (which we can do only if it's a "real" file | 
|  | // descriptor, not AT_FDCWD used as the starting point of DeleteTreesBelow | 
|  | // recursion). | 
|  | if (errno == EACCES && dir_fd != AT_FDCWD) { | 
|  | if (fchmod(dir_fd, 0700) == -1) { | 
|  | if (errno != ENOENT) { | 
|  | PostDeleteTreesBelowException(env, errno, "fchmod", dir_path, | 
|  | nullptr); | 
|  | } | 
|  | return {nullptr, errno}; | 
|  | } | 
|  | } | 
|  | if (fchmodat(dir_fd, entry, 0700, 0) == -1) { | 
|  | if (errno != ENOENT) { | 
|  | PostDeleteTreesBelowException(env, errno, "fchmodat", dir_path, entry); | 
|  | } | 
|  | return {nullptr, errno}; | 
|  | } | 
|  | fd = openat(dir_fd, entry, flags); | 
|  | if (fd == -1) { | 
|  | if (errno != ENOENT) { | 
|  | PostDeleteTreesBelowException(env, errno, "opendir", dir_path, entry); | 
|  | } | 
|  | return {nullptr, errno}; | 
|  | } | 
|  | } | 
|  | DIR* dir = fdopendir(fd); | 
|  | if (dir == nullptr) { | 
|  | if (errno != ENOENT) { | 
|  | PostDeleteTreesBelowException(env, errno, "fdopendir", dir_path, entry); | 
|  | } | 
|  | close(fd); | 
|  | return {nullptr, errno}; | 
|  | } | 
|  | return {dir, 0}; | 
|  | } | 
|  |  | 
|  | // Tries to delete a file within a directory and, if the first attempt fails, | 
|  | // retries after granting extra write permissions to the directory. | 
|  | // | 
|  | // The file to delete is identified by the open descriptor of the parent | 
|  | // directory (dir_fd) and the subpath to resolve within that directory (entry). | 
|  | // dir_path contains the path components that were used when opening dir_fd and | 
|  | // is only used for error reporting purposes. | 
|  | // | 
|  | // is_dir indicates whether the entry to delete is a directory or not. | 
|  | // | 
|  | // Returns 0 when the file doesn't exist or is successfully deleted. Otherwise, | 
|  | // returns -1 and posts an exception. | 
|  | static int ForceDelete(JNIEnv* env, const std::vector<std::string>& dir_path, | 
|  | const int dir_fd, const char* entry, | 
|  | const bool is_dir) { | 
|  | const int flags = is_dir ? AT_REMOVEDIR : 0; | 
|  | if (unlinkat(dir_fd, entry, flags) == -1) { | 
|  | if (errno == ENOENT) { | 
|  | return 0; | 
|  | } | 
|  | if (fchmod(dir_fd, 0700) == -1) { | 
|  | if (errno == ENOENT) { | 
|  | return 0; | 
|  | } | 
|  | PostDeleteTreesBelowException(env, errno, "fchmod", dir_path, nullptr); | 
|  | return -1; | 
|  | } | 
|  | if (unlinkat(dir_fd, entry, flags) == -1) { | 
|  | if (errno == ENOENT) { | 
|  | return 0; | 
|  | } | 
|  | PostDeleteTreesBelowException(env, errno, "unlinkat", dir_path, entry); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Returns true if the given directory entry represents a subdirectory of dir. | 
|  | // | 
|  | // The file to check is identified by the open descriptor of the parent | 
|  | // directory (dir_fd) and the directory entry within that directory (de). | 
|  | // dir_path contains the path components that were used when opening dir_fd and | 
|  | // is only used for error reporting purposes. | 
|  | // | 
|  | // This function prefers to extract the type information from the directory | 
|  | // entry itself if available. If not available, issues a stat starting from | 
|  | // dir_fd. | 
|  | // | 
|  | // Returns 0 on success and updates is_dir accordingly. Returns -1 on error and | 
|  | // posts an exception. | 
|  | static int IsSubdir(JNIEnv* env, const std::vector<std::string>& dir_path, | 
|  | const int dir_fd, const struct dirent* de, bool* is_dir) { | 
|  | switch (de->d_type) { | 
|  | case DT_DIR: | 
|  | *is_dir = true; | 
|  | return 0; | 
|  |  | 
|  | case DT_UNKNOWN: { | 
|  | struct stat st; | 
|  | if (fstatat(dir_fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1) { | 
|  | if (errno == ENOENT) { | 
|  | *is_dir = false; | 
|  | return 0; | 
|  | } | 
|  | PostDeleteTreesBelowException(env, errno, "fstatat", dir_path, | 
|  | de->d_name); | 
|  | return -1; | 
|  | } | 
|  | *is_dir = st.st_mode & S_IFDIR; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | default: | 
|  | *is_dir = false; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Recursively deletes all trees under the given path. | 
|  | // | 
|  | // The directory to delete is identified by the open descriptor of the parent | 
|  | // directory (dir_fd) and the subpath to resolve within that directory (entry). | 
|  | // dir_path contains the path components that were used when opening dir_fd and | 
|  | // is only used for error reporting purposes. | 
|  | // | 
|  | // dir_path is an in/out parameter updated with the path to the directory being | 
|  | // processed. This avoids the need to construct unnecessary intermediate paths, | 
|  | // as this algorithm works purely on file descriptors: the paths are only used | 
|  | // for error reporting purposes, and therefore are only formatted at that | 
|  | // point. | 
|  | // | 
|  | // Returns 0 on success. Returns -1 on error and posts an exception. | 
|  | static int DeleteTreesBelow(JNIEnv* env, std::vector<std::string>* dir_path, | 
|  | const int dir_fd, const char* entry) { | 
|  | DIROrError dir_or_error = ForceOpendir(env, *dir_path, dir_fd, entry); | 
|  | DIR *dir = dir_or_error.dir; | 
|  | if (dir == nullptr) { | 
|  | if (dir_or_error.error == ENOENT) { | 
|  | return 0; | 
|  | } | 
|  | BAZEL_CHECK_NE(env->ExceptionOccurred(), nullptr); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | dir_path->push_back(entry); | 
|  | // On macOS and some other non-Linux OSes, on some filesystems, readdir(dir) | 
|  | // may return NULL after an entry in dir is deleted even if not all files have | 
|  | // been read yet - see | 
|  | // https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html; | 
|  | // "If a file is removed from or added to the directory after the most recent | 
|  | // call to opendir() or rewinddir(), whether a subsequent call to readdir() | 
|  | // returns an entry for that file is unspecified." We thus read all the names | 
|  | // of dir's entries before deleting. We don't want to simply use fts(3) | 
|  | // because we want to be able to chmod at any point in the directory hierarchy | 
|  | // to retry a filesystem operation after hitting an EACCES. | 
|  | // If in the future we hit any problems here due to the unspecified behavior | 
|  | // of readdir() when a file has been deleted by a different thread we can use | 
|  | // some form of locking to make sure the threads don't try to clean up the | 
|  | // same directory at the same time; or doing it in a loop until the directory | 
|  | // is really empty. | 
|  | std::vector<std::string> dir_files, dir_subdirs; | 
|  | for (;;) { | 
|  | errno = 0; | 
|  | struct dirent* de = readdir(dir); | 
|  | if (de == nullptr) { | 
|  | if (errno != 0 && errno != ENOENT) { | 
|  | PostDeleteTreesBelowException(env, errno, "readdir", *dir_path, | 
|  | nullptr); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | bool is_dir; | 
|  | if (IsSubdir(env, *dir_path, dirfd(dir), de, &is_dir) == -1) { | 
|  | BAZEL_CHECK_NE(env->ExceptionOccurred(), nullptr); | 
|  | break; | 
|  | } | 
|  | if (is_dir) { | 
|  | dir_subdirs.push_back(de->d_name); | 
|  | } else { | 
|  | dir_files.push_back(de->d_name); | 
|  | } | 
|  | } | 
|  | if (env->ExceptionOccurred() == nullptr) { | 
|  | for (const auto &file : dir_files) { | 
|  | if (ForceDelete(env, *dir_path, dirfd(dir), file.c_str(), false) == -1) { | 
|  | BAZEL_CHECK_NE(env->ExceptionOccurred(), nullptr); | 
|  | break; | 
|  | } | 
|  | } | 
|  | // DeleteTreesBelow is recursive; don't hold on to file names unnecessarily. | 
|  | dir_files.clear(); | 
|  | } | 
|  | if (env->ExceptionOccurred() == nullptr) { | 
|  | for (const auto &subdir : dir_subdirs) { | 
|  | if (DeleteTreesBelow(env, dir_path, dirfd(dir), subdir.c_str()) == -1) { | 
|  | BAZEL_CHECK_NE(env->ExceptionOccurred(), nullptr); | 
|  | break; | 
|  | } | 
|  | if (ForceDelete(env, *dir_path, dirfd(dir), subdir.c_str(), true) == -1) { | 
|  | BAZEL_CHECK_NE(env->ExceptionOccurred(), nullptr); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (closedir(dir) == -1) { | 
|  | // Prefer reporting the error encountered while processing entries, | 
|  | // not the (unlikely) error on close. | 
|  | if (env->ExceptionOccurred() == nullptr) { | 
|  | PostDeleteTreesBelowException(env, errno, "closedir", *dir_path, nullptr); | 
|  | } | 
|  | } | 
|  | dir_path->pop_back(); | 
|  | return env->ExceptionOccurred() == nullptr ? 0 : -1; | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | /* | 
|  | * Class:     com.google.devtools.build.lib.unix.NativePosixFiles | 
|  | * Method:    deleteTreesBelow | 
|  | * Signature: (Ljava/lang/String;)V | 
|  | * Throws:    java.io.IOException | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_deleteTreesBelow( | 
|  | JNIEnv *env, jclass clazz, jstring path) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | std::vector<std::string> dir_path; | 
|  | if (DeleteTreesBelow(env, &dir_path, AT_FDCWD, path_chars) == -1) { | 
|  | BAZEL_CHECK_NE(env->ExceptionOccurred(), nullptr); | 
|  | } | 
|  | BAZEL_CHECK(dir_path.empty()); | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////// | 
|  | // Linux extended file attributes | 
|  |  | 
|  | namespace { | 
|  | typedef ssize_t getxattr_func(const char *path, const char *name, | 
|  | void *value, size_t size, bool *attr_not_found); | 
|  |  | 
|  | static jbyteArray getxattr_common(JNIEnv *env, | 
|  | jstring path, | 
|  | jstring name, | 
|  | getxattr_func getxattr) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | const char *name_chars = GetStringLatin1Chars(env, name); | 
|  |  | 
|  | // TODO(bazel-team): on ERANGE, try again with larger buffer. | 
|  | jbyte value[4096]; | 
|  | jbyteArray result = nullptr; | 
|  | bool attr_not_found = false; | 
|  | ssize_t size = getxattr(path_chars, name_chars, value, arraysize(value), | 
|  | &attr_not_found); | 
|  | if (size == -1) { | 
|  | if (!attr_not_found) { | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | } else { | 
|  | result = env->NewByteArray(size); | 
|  | // Result may be NULL if allocation failed. In that case, we'll return the | 
|  | // NULL and an OOME will be thrown when we are back in Java. | 
|  | if (result != nullptr) { | 
|  | env->SetByteArrayRegion(result, 0, size, value); | 
|  | } | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | ReleaseStringLatin1Chars(name_chars); | 
|  | return result; | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | extern "C" JNIEXPORT jbyteArray JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_getxattr(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path, | 
|  | jstring name) { | 
|  | return getxattr_common(env, path, name, portable_getxattr); | 
|  | } | 
|  |  | 
|  | extern "C" JNIEXPORT jbyteArray JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_lgetxattr(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jstring path, | 
|  | jstring name) { | 
|  | return getxattr_common(env, path, name, portable_lgetxattr); | 
|  | } | 
|  |  | 
|  | extern "C" JNIEXPORT jint JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_openWrite( | 
|  | JNIEnv *env, jclass clazz, jstring path, jboolean append) { | 
|  | const char *path_chars = GetStringLatin1Chars(env, path); | 
|  | int flags = (O_WRONLY | O_CREAT) | (append ? O_APPEND : O_TRUNC); | 
|  | int fd; | 
|  | while ((fd = open(path_chars, flags, 0666)) == -1 && errno == EINTR) { | 
|  | } | 
|  | if (fd == -1) { | 
|  | PostException(env, errno, path_chars); | 
|  | } | 
|  | ReleaseStringLatin1Chars(path_chars); | 
|  | return fd; | 
|  | } | 
|  |  | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_close(JNIEnv *env, | 
|  | jclass clazz, | 
|  | jint fd, | 
|  | jobject ingore) { | 
|  | if (close(fd) == -1) { | 
|  | PostException(env, errno, "close"); | 
|  | } | 
|  | } | 
|  |  | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_unix_NativePosixFiles_write( | 
|  | JNIEnv *env, jclass clazz, jint fd, jbyteArray data, jint off, jint len) { | 
|  | int data_len = env->GetArrayLength(data); | 
|  | if (off < 0 || len < 0 || off > data_len || data_len - off < len) { | 
|  | jclass oob = env->FindClass("java/lang/IndexOutOfBoundsException"); | 
|  | if (oob != nullptr) { | 
|  | env->ThrowNew(oob, nullptr); | 
|  | } | 
|  | return; | 
|  | } | 
|  | jbyte *buf = static_cast<jbyte *>(malloc(len)); | 
|  | if (buf == nullptr) { | 
|  | PostException(env, ENOMEM, "write"); | 
|  | return; | 
|  | } | 
|  | env->GetByteArrayRegion(data, off, len, buf); | 
|  | // GetByteArrayRegion may raise ArrayIndexOutOfBoundsException if one of the | 
|  | // indexes in the region is not valid. As we obtain the inidices from the | 
|  | // caller, we have to check. | 
|  | if (!env->ExceptionOccurred()) { | 
|  | jbyte *p = buf; | 
|  | while (len > 0) { | 
|  | ssize_t res = write(fd, p, len); | 
|  | if (res == -1) { | 
|  | if (errno != EINTR) { | 
|  | PostException(env, errno, "write"); | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | p += res; | 
|  | len -= res; | 
|  | } | 
|  | } | 
|  | } | 
|  | free(buf); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com_google_devtools_build_lib_platform_SleepPreventionModule_SleepPrevention | 
|  | * Method:    pushDisableSleep | 
|  | * Signature: ()I | 
|  | */ | 
|  | extern "C" JNIEXPORT jint JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SleepPreventionModule_00024SleepPrevention_pushDisableSleep( | 
|  | JNIEnv *, jclass) { | 
|  | return portable_push_disable_sleep(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     com_google_devtools_build_lib_platform_SleepPreventionModule_SleepPrevention | 
|  | * Method:    popDisableSleep | 
|  | * Signature: ()I | 
|  | */ | 
|  | extern "C" JNIEXPORT jint JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SleepPreventionModule_00024SleepPrevention_popDisableSleep( | 
|  | JNIEnv *, jclass) { | 
|  | return portable_pop_disable_sleep(); | 
|  | } | 
|  |  | 
|  | jobject g_suspend_module; | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemSuspensionModule | 
|  | * Method:    registerJNI | 
|  | * Signature: ()V | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemSuspensionModule_registerJNI( | 
|  | JNIEnv *env, jobject local_object) { | 
|  |  | 
|  | if (g_suspend_module != nullptr) { | 
|  | PostAssertionError(env, | 
|  | "Singleton SystemSuspensionModule already registered"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JavaVM *java_vm = GetJavaVM(env); | 
|  | if (java_vm == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to get javaVM registering SystemSuspensionModule"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | g_suspend_module = env->NewGlobalRef(local_object); | 
|  | if (g_suspend_module == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to create global ref for SystemSuspensionModule"); | 
|  | return; | 
|  | } | 
|  | portable_start_suspend_monitoring(); | 
|  | } | 
|  |  | 
|  | void suspend_callback(SuspensionReason value) { | 
|  | if (g_suspend_module != nullptr) { | 
|  | PerformIntegerValueCallback(g_suspend_module, "suspendCallback", value); | 
|  | } | 
|  | } | 
|  |  | 
|  | jobject g_thermal_module; | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemThermalModule | 
|  | * Method:    registerJNI | 
|  | * Signature: ()V | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemThermalModule_registerJNI( | 
|  | JNIEnv *env, jobject local_object) { | 
|  |  | 
|  | if (g_thermal_module != nullptr) { | 
|  | PostAssertionError(env, | 
|  | "Singleton SystemThermalModule already registered"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JavaVM *java_vm = GetJavaVM(env); | 
|  | if (java_vm == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to get javaVM registering SystemThermalModule"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | g_thermal_module = env->NewGlobalRef(local_object); | 
|  | if (g_thermal_module == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to create global ref for SystemThermalModule"); | 
|  | return; | 
|  | } | 
|  | portable_start_thermal_monitoring(); | 
|  | } | 
|  |  | 
|  | void thermal_callback(int value) { | 
|  | if (g_thermal_module != nullptr) { | 
|  | PerformIntegerValueCallback(g_thermal_module, "thermalCallback", value); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemThermalModule | 
|  | * Method:    thermalLoad | 
|  | * Signature: ()I | 
|  | */ | 
|  | extern "C" JNIEXPORT jint JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemThermalModule_thermalLoad( | 
|  | JNIEnv *env, jclass) { | 
|  | return portable_thermal_load(); | 
|  | } | 
|  |  | 
|  | jobject g_system_load_advisory_module; | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemLoadAdvisoryModule | 
|  | * Method:    registerJNI | 
|  | * Signature: ()V | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemLoadAdvisoryModule_registerJNI( | 
|  | JNIEnv *env, jobject local_object) { | 
|  |  | 
|  | if (g_system_load_advisory_module != nullptr) { | 
|  | PostAssertionError(env, | 
|  | "Singleton SystemLoadAdvisoryModule already registered"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JavaVM *java_vm = GetJavaVM(env); | 
|  | if (java_vm == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to get javaVM registering SystemLoadAdvisoryModule"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | g_system_load_advisory_module = env->NewGlobalRef(local_object); | 
|  | if (g_system_load_advisory_module == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to create global ref for SystemLoadAdvisoryModule"); | 
|  | return; | 
|  | } | 
|  | portable_start_system_load_advisory_monitoring(); | 
|  | } | 
|  |  | 
|  | void system_load_advisory_callback(int value) { | 
|  | if (g_system_load_advisory_module != nullptr) { | 
|  | PerformIntegerValueCallback(g_system_load_advisory_module, | 
|  | "systemLoadAdvisoryCallback", value); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemLoadAdvisoryModule | 
|  | * Method:    systemLoadAdvisory | 
|  | * Signature: ()I | 
|  | */ | 
|  | extern "C" JNIEXPORT jint JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemLoadAdvisoryModule_systemLoadAdvisory( | 
|  | JNIEnv *env, jclass) { | 
|  | return portable_system_load_advisory(); | 
|  | } | 
|  |  | 
|  | jobject g_memory_pressure_module; | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemMemoryPressureMonitor | 
|  | * Method:    registerJNI | 
|  | * Signature: ()I | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemMemoryPressureMonitor_registerJNI( | 
|  | JNIEnv *env, jobject local_object) { | 
|  |  | 
|  | if (g_memory_pressure_module != nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Singleton SystemMemoryPressureModule already registered"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JavaVM *java_vm = GetJavaVM(env); | 
|  | if (java_vm == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to get javaVM registering SystemMemoryPressureModule"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | g_memory_pressure_module = env->NewGlobalRef(local_object); | 
|  | if (g_memory_pressure_module == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to create global ref for SystemMemoryPressureModule"); | 
|  | return; | 
|  | } | 
|  | portable_start_memory_pressure_monitoring(); | 
|  | } | 
|  |  | 
|  | void memory_pressure_callback(MemoryPressureLevel level) { | 
|  | if (g_memory_pressure_module != nullptr) { | 
|  | PerformIntegerValueCallback(g_memory_pressure_module, | 
|  | "memoryPressureCallback", level); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemMemoryPressureMonitor | 
|  | * Method:    systemMemoryPressure | 
|  | * Signature: ()I | 
|  | */ | 
|  | extern "C" JNIEXPORT jint JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemMemoryPressureMonitor_systemMemoryPressure( | 
|  | JNIEnv *env, jclass) { | 
|  | return portable_memory_pressure(); | 
|  | } | 
|  |  | 
|  | jobject g_disk_space_module; | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemDiskSpaceModule | 
|  | * Method:    registerJNI | 
|  | * Signature: ()I | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemDiskSpaceModule_registerJNI( | 
|  | JNIEnv *env, jobject local_object) { | 
|  |  | 
|  | if (g_disk_space_module != nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Singleton SystemDiskSpaceModule already registered"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JavaVM *java_vm = GetJavaVM(env); | 
|  | if (java_vm == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to get javaVM registering SystemDiskSpaceModule"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | g_disk_space_module = env->NewGlobalRef(local_object); | 
|  | if (g_disk_space_module == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to create global ref for SystemDiskSpaceModule"); | 
|  | return; | 
|  | } | 
|  | portable_start_disk_space_monitoring(); | 
|  | } | 
|  |  | 
|  | void disk_space_callback(DiskSpaceLevel level) { | 
|  | if (g_disk_space_module != nullptr) { | 
|  | PerformIntegerValueCallback(g_disk_space_module, "diskSpaceCallback", | 
|  | level); | 
|  | } | 
|  | } | 
|  |  | 
|  | jobject g_cpu_speed_module; | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemCPUSpeedModule | 
|  | * Method:    registerJNI | 
|  | * Signature: ()I | 
|  | */ | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemCPUSpeedModule_registerJNI( | 
|  | JNIEnv *env, jobject local_object) { | 
|  |  | 
|  | if (g_cpu_speed_module != nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Singleton SystemCPUSpeedModule already registered"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JavaVM *java_vm = GetJavaVM(env); | 
|  | if (java_vm == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to get javaVM registering SystemCPUSpeedModule"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | g_cpu_speed_module = env->NewGlobalRef(local_object); | 
|  | if (g_cpu_speed_module == nullptr) { | 
|  | PostAssertionError( | 
|  | env, "Unable to create global ref for SystemCPUSpeedModule"); | 
|  | return; | 
|  | } | 
|  | portable_start_cpu_speed_monitoring(); | 
|  | } | 
|  |  | 
|  | void cpu_speed_callback(int speed) { | 
|  | if (g_cpu_speed_module != nullptr) { | 
|  | PerformIntegerValueCallback(g_cpu_speed_module, "cpuSpeedCallback", speed); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Class:     Java_com_google_devtools_build_lib_platform_SystemCPUSpeedModule | 
|  | * Method:    cpuSpeed | 
|  | * Signature: ()I | 
|  | * | 
|  | * Returns 1-100 to represent CPU speed. Returns -1 in case of error. | 
|  | */ | 
|  | extern "C" JNIEXPORT jint JNICALL | 
|  | Java_com_google_devtools_build_lib_platform_SystemCPUSpeedModule_cpuSpeed( | 
|  | JNIEnv *env, jclass) { | 
|  | return portable_cpu_speed(); | 
|  | } | 
|  |  | 
|  | static int convert_ipaddr(struct sockaddr *addr, int family, char *buf, | 
|  | int buf_len) { | 
|  | if (buf_len > 0) { | 
|  | buf[0] = 0; | 
|  | } | 
|  | int addr_len = 0; | 
|  | if (family == AF_INET) { | 
|  | addr_len = sizeof(struct sockaddr_in); | 
|  | } else if (family == AF_INET6) { | 
|  | addr_len = sizeof(struct sockaddr_in6); | 
|  | } | 
|  | if (addr_len != 0) { | 
|  | int err = | 
|  | getnameinfo(addr, addr_len, buf, buf_len, nullptr, 0, NI_NUMERICHOST); | 
|  | if (err != 0) { | 
|  | return err; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | extern "C" JNIEXPORT void JNICALL | 
|  | Java_com_google_devtools_build_lib_profiler_SystemNetworkStats_getNetIfAddrsNative( | 
|  | JNIEnv *env, jclass clazz, jobject addrs_list) { | 
|  | ifaddrs *ifaddr; | 
|  | if (getifaddrs(&ifaddr) == -1) { | 
|  | PostException(env, errno, "getifaddrs"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | jclass list_class = env->GetObjectClass(addrs_list); | 
|  | jmethodID list_add = | 
|  | env->GetMethodID(list_class, "add", "(Ljava/lang/Object;)Z"); | 
|  |  | 
|  | jclass addr_class = env->FindClass( | 
|  | "com/google/devtools/build/lib/profiler/SystemNetworkStats$NetIfAddr"); | 
|  | jmethodID addr_create = env->GetStaticMethodID( | 
|  | addr_class, "create", | 
|  | "(Ljava/lang/String;Lcom/google/devtools/build/lib/profiler/" | 
|  | "SystemNetworkStats$NetIfAddr$Family;Ljava/lang/String;)Lcom/google/" | 
|  | "devtools/build/lib/profiler/SystemNetworkStats$NetIfAddr;"); | 
|  |  | 
|  | jclass family_class = env->FindClass( | 
|  | "com/google/devtools/build/lib/profiler/" | 
|  | "SystemNetworkStats$NetIfAddr$Family"); | 
|  | const char *family_class_sig = | 
|  | "Lcom/google/devtools/build/lib/profiler/" | 
|  | "SystemNetworkStats$NetIfAddr$Family;"; | 
|  | jfieldID family_af_inet_id = | 
|  | env->GetStaticFieldID(family_class, "AF_INET", family_class_sig); | 
|  | jobject family_af_inet = | 
|  | env->GetStaticObjectField(family_class, family_af_inet_id); | 
|  | jfieldID family_af_inet6_id = | 
|  | env->GetStaticFieldID(family_class, "AF_INET6", family_class_sig); | 
|  | jobject family_af_inet6 = | 
|  | env->GetStaticObjectField(family_class, family_af_inet6_id); | 
|  | jfieldID family_unknown_id = | 
|  | env->GetStaticFieldID(family_class, "UNKNOWN", family_class_sig); | 
|  | jobject family_unknown = | 
|  | env->GetStaticObjectField(family_class, family_unknown_id); | 
|  |  | 
|  | for (ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { | 
|  | if (!ifa->ifa_addr) { | 
|  | continue; | 
|  | } | 
|  | jstring name = env->NewStringUTF(ifa->ifa_name); | 
|  |  | 
|  | int family = ifa->ifa_addr->sa_family; | 
|  |  | 
|  | jobject family_enum; | 
|  | switch (family) { | 
|  | case AF_INET: | 
|  | family_enum = family_af_inet; | 
|  | break; | 
|  | case AF_INET6: | 
|  | family_enum = family_af_inet6; | 
|  | break; | 
|  | default: | 
|  | family_enum = family_unknown; | 
|  | } | 
|  |  | 
|  | char buf[NI_MAXHOST]; | 
|  | int err = convert_ipaddr(ifa->ifa_addr, family, buf, sizeof(buf)); | 
|  | if (err != 0) { | 
|  | PostException(env, errno, "convert_ipaddr"); | 
|  | return; | 
|  | } | 
|  | jstring ipaddr = env->NewStringUTF(buf); | 
|  |  | 
|  | jobject addr = env->CallStaticObjectMethod(addr_class, addr_create, name, | 
|  | family_enum, ipaddr); | 
|  | env->CallObjectMethod(addrs_list, list_add, addr); | 
|  | } | 
|  |  | 
|  | freeifaddrs(ifaddr); | 
|  | } | 
|  |  | 
|  | }  // namespace blaze_jni |