| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| |
| // get the autoconf settings |
| #include "config.h" |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <grp.h> |
| #include <jni.h> |
| #include <pwd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "org_apache_hadoop.h" |
| #include "org_apache_hadoop_io_nativeio_NativeIO.h" |
| #include "file_descriptor.h" |
| #include "errno_enum.h" |
| |
| // the NativeIO$Stat inner class and its constructor |
| static jclass stat_clazz; |
| static jmethodID stat_ctor; |
| |
| // the NativeIOException class and its constructor |
| static jclass nioe_clazz; |
| static jmethodID nioe_ctor; |
| |
| // Internal functions |
| static void throw_ioe(JNIEnv* env, int errnum); |
| static ssize_t get_pw_buflen(); |
| |
| |
| static void stat_init(JNIEnv *env) { |
| // Init Stat |
| jclass clazz = (*env)->FindClass(env, "org/apache/hadoop/io/nativeio/NativeIO$Stat"); |
| PASS_EXCEPTIONS(env); |
| stat_clazz = (*env)->NewGlobalRef(env, clazz); |
| stat_ctor = (*env)->GetMethodID(env, stat_clazz, "<init>", |
| "(Ljava/lang/String;Ljava/lang/String;I)V"); |
| } |
| |
| static void stat_deinit(JNIEnv *env) { |
| if (stat_clazz != NULL) { |
| (*env)->DeleteGlobalRef(env, stat_clazz); |
| stat_clazz = NULL; |
| } |
| } |
| |
| static void nioe_init(JNIEnv *env) { |
| // Init NativeIOException |
| nioe_clazz = (*env)->FindClass( |
| env, "org/apache/hadoop/io/nativeio/NativeIOException"); |
| PASS_EXCEPTIONS(env); |
| |
| nioe_clazz = (*env)->NewGlobalRef(env, nioe_clazz); |
| nioe_ctor = (*env)->GetMethodID(env, nioe_clazz, "<init>", |
| "(Ljava/lang/String;Lorg/apache/hadoop/io/nativeio/Errno;)V"); |
| } |
| |
| static void nioe_deinit(JNIEnv *env) { |
| if (nioe_clazz != NULL) { |
| (*env)->DeleteGlobalRef(env, nioe_clazz); |
| nioe_clazz = NULL; |
| } |
| nioe_ctor = NULL; |
| } |
| |
| /* |
| * private static native void initNative(); |
| * |
| * We rely on this function rather than lazy initialization because |
| * the lazy approach may have a race if multiple callers try to |
| * init at the same time. |
| */ |
| JNIEXPORT void JNICALL |
| Java_org_apache_hadoop_io_nativeio_NativeIO_initNative( |
| JNIEnv *env, jclass clazz) { |
| |
| stat_init(env); |
| PASS_EXCEPTIONS_GOTO(env, error); |
| nioe_init(env); |
| PASS_EXCEPTIONS_GOTO(env, error); |
| fd_init(env); |
| PASS_EXCEPTIONS_GOTO(env, error); |
| errno_enum_init(env); |
| PASS_EXCEPTIONS_GOTO(env, error); |
| return; |
| error: |
| // these are all idempodent and safe to call even if the |
| // class wasn't initted yet |
| stat_deinit(env); |
| nioe_deinit(env); |
| fd_deinit(env); |
| errno_enum_deinit(env); |
| } |
| |
| /* |
| * public static native Stat fstat(FileDescriptor fd); |
| */ |
| JNIEXPORT jobject JNICALL |
| Java_org_apache_hadoop_io_nativeio_NativeIO_fstat( |
| JNIEnv *env, jclass clazz, jobject fd_object) |
| { |
| jobject ret = NULL; |
| char *pw_buf = NULL; |
| |
| int fd = fd_get(env, fd_object); |
| PASS_EXCEPTIONS_GOTO(env, cleanup); |
| |
| struct stat s; |
| int rc = fstat(fd, &s); |
| if (rc != 0) { |
| throw_ioe(env, errno); |
| goto cleanup; |
| } |
| |
| size_t pw_buflen = get_pw_buflen(); |
| if ((pw_buf = malloc(pw_buflen)) == NULL) { |
| THROW(env, "java/lang/OutOfMemoryError", "Couldn't allocate memory for pw buffer"); |
| goto cleanup; |
| } |
| |
| // Grab username |
| struct passwd pwd, *pwdp; |
| while ((rc = getpwuid_r(s.st_uid, &pwd, pw_buf, pw_buflen, &pwdp)) != 0) { |
| if (rc != ERANGE) { |
| throw_ioe(env, rc); |
| goto cleanup; |
| } |
| free(pw_buf); |
| pw_buflen *= 2; |
| if ((pw_buf = malloc(pw_buflen)) == NULL) { |
| THROW(env, "java/lang/OutOfMemoryError", "Couldn't allocate memory for pw buffer"); |
| goto cleanup; |
| } |
| } |
| assert(pwdp == &pwd); |
| |
| jstring jstr_username = (*env)->NewStringUTF(env, pwd.pw_name); |
| if (jstr_username == NULL) goto cleanup; |
| |
| // Grab group |
| struct group grp, *grpp; |
| while ((rc = getgrgid_r(s.st_gid, &grp, pw_buf, pw_buflen, &grpp)) != 0) { |
| if (rc != ERANGE) { |
| throw_ioe(env, rc); |
| goto cleanup; |
| } |
| free(pw_buf); |
| pw_buflen *= 2; |
| if ((pw_buf = malloc(pw_buflen)) == NULL) { |
| THROW(env, "java/lang/OutOfMemoryError", "Couldn't allocate memory for pw buffer"); |
| goto cleanup; |
| } |
| } |
| assert(grpp == &grp); |
| |
| jstring jstr_groupname = (*env)->NewStringUTF(env, grp.gr_name); |
| PASS_EXCEPTIONS_GOTO(env, cleanup); |
| |
| // Construct result |
| ret = (*env)->NewObject(env, stat_clazz, stat_ctor, |
| jstr_username, jstr_groupname, s.st_mode); |
| |
| cleanup: |
| if (pw_buf != NULL) free(pw_buf); |
| return ret; |
| } |
| |
| |
| /* |
| * public static native FileDescriptor open(String path, int flags, int mode); |
| */ |
| JNIEXPORT jobject JNICALL |
| Java_org_apache_hadoop_io_nativeio_NativeIO_open( |
| JNIEnv *env, jclass clazz, jstring j_path, |
| jint flags, jint mode) |
| { |
| jobject ret = NULL; |
| |
| const char *path = (*env)->GetStringUTFChars(env, j_path, NULL); |
| if (path == NULL) goto cleanup; // JVM throws Exception for us |
| |
| int fd; |
| if (flags & O_CREAT) { |
| fd = open(path, flags, mode); |
| } else { |
| fd = open(path, flags); |
| } |
| |
| if (fd == -1) { |
| throw_ioe(env, errno); |
| goto cleanup; |
| } |
| |
| ret = fd_create(env, fd); |
| |
| cleanup: |
| if (path != NULL) { |
| (*env)->ReleaseStringUTFChars(env, j_path, path); |
| } |
| return ret; |
| } |
| |
| /* |
| * Throw a java.IO.IOException, generating the message from errno. |
| */ |
| static void throw_ioe(JNIEnv* env, int errnum) |
| { |
| const char* message; |
| char buffer[80]; |
| jstring jstr_message; |
| |
| buffer[0] = 0; |
| #ifdef STRERROR_R_CHAR_P |
| // GNU strerror_r |
| message = strerror_r(errnum, buffer, sizeof(buffer)); |
| assert (message != NULL); |
| #else |
| int ret = strerror_r(errnum, buffer, sizeof(buffer)); |
| if (ret == 0) { |
| message = buffer; |
| } else { |
| message = "Unknown error"; |
| } |
| #endif |
| jobject errno_obj = errno_to_enum(env, errnum); |
| |
| if ((jstr_message = (*env)->NewStringUTF(env, message)) == NULL) |
| goto err; |
| |
| jthrowable obj = (jthrowable)(*env)->NewObject(env, nioe_clazz, nioe_ctor, |
| jstr_message, errno_obj); |
| if (obj == NULL) goto err; |
| |
| (*env)->Throw(env, obj); |
| return; |
| |
| err: |
| if (jstr_message != NULL) |
| (*env)->ReleaseStringUTFChars(env, jstr_message, message); |
| } |
| |
| |
| /* |
| * Determine how big a buffer we need for reentrant getpwuid_r and getgrnam_r |
| */ |
| ssize_t get_pw_buflen() { |
| size_t ret = 0; |
| #ifdef _SC_GETPW_R_SIZE_MAX |
| ret = sysconf(_SC_GETPW_R_SIZE_MAX); |
| #endif |
| return (ret > 512) ? ret : 512; |
| } |
| /** |
| * vim: sw=2: ts=2: et: |
| */ |
| |