Home | History | Annotate | Download | only in native
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 #include "JNIHelp.h"
     19 
     20 #include <stdlib.h>
     21 #include <unistd.h>
     22 #include <sys/types.h>
     23 #include <sys/stat.h>
     24 #include <fcntl.h>
     25 #include <errno.h>
     26 #include <assert.h>
     27 #include <sys/ioctl.h>
     28 
     29 /*
     30  * These are JNI field IDs for the stuff we're interested in.  They're
     31  * computed when the class is loaded.
     32  */
     33 static struct {
     34     jfieldID    descriptor;       /* int */
     35     jmethodID   constructorInt;
     36     jmethodID   setFD;
     37     jclass      clazz;
     38 } gCachedFields;
     39 
     40 /*
     41  * Internal helper function.
     42  *
     43  * Get the file descriptor.
     44  */
     45 static inline int getFd(JNIEnv* env, jobject obj)
     46 {
     47     return (*env)->GetIntField(env, obj, gCachedFields.descriptor);
     48 }
     49 
     50 /*
     51  * Internal helper function.
     52  *
     53  * Set the file descriptor.
     54  */
     55 static inline void setFd(JNIEnv* env, jobject obj, jint value)
     56 {
     57     (*env)->SetIntField(env, obj, gCachedFields.descriptor, value);
     58 }
     59 
     60 /*
     61  * native private static void nativeClassInit()
     62  *
     63  * Perform one-time initialization.  If the class is unloaded and re-loaded,
     64  * this will be called again.
     65  */
     66 static void nativeClassInit(JNIEnv* env, jclass clazz)
     67 {
     68     gCachedFields.clazz = (*env)->NewGlobalRef(env, clazz);
     69 
     70     gCachedFields.descriptor =
     71         (*env)->GetFieldID(env, clazz, "descriptor", "I");
     72 
     73     if(gCachedFields.descriptor == NULL) {
     74         jniThrowException(env, "java/lang/NoSuchFieldError", "FileDescriptor");
     75         return;
     76     }
     77 
     78     gCachedFields.constructorInt =
     79         (*env)->GetMethodID(env, clazz, "<init>", "()V");
     80 
     81     if(gCachedFields.constructorInt == NULL) {
     82         jniThrowException(env, "java/lang/NoSuchMethodError", "<init>()V");
     83         return;
     84     }
     85 }
     86 
     87 /*
     88  * public native void sync()
     89  */
     90 static void fd_sync(JNIEnv* env, jobject obj) {
     91     int fd = getFd(env, obj);
     92 
     93     if (fsync(fd) != 0) {
     94         /*
     95          * If fd is a socket, then fsync(fd) is defined to fail with
     96          * errno EINVAL. This isn't actually cause for concern.
     97          * TODO: Look into not bothering to call fsync() at all if
     98          * we know we are dealing with a socket.
     99          */
    100         if (errno != EINVAL) {
    101             jniThrowException(env, "java/io/SyncFailedException", "");
    102         }
    103     }
    104 }
    105 
    106 /* checks to see if class is inited and inits if needed, returning -1
    107  * on fail and 0 on success
    108  */
    109 static int checkClassInit (JNIEnv *env) {
    110     if(gCachedFields.clazz == NULL) {
    111         /* this should cause the class to be inited and
    112          * our static variables to be filled in
    113          *
    114          * (Note that FindClass just loads the class; it doesn't get
    115          * initialized until we try to do something with it.)
    116          */
    117         jclass clazz;
    118         clazz = (*env)->FindClass(env, "java/io/FileDescriptor");
    119         if(clazz == NULL) {
    120             jniThrowException(env, "java/lang/ClassNotFoundException",
    121                                     "java.io.FileDescriptor");
    122             return -1;
    123         }
    124 
    125         jfieldID readWriteId;
    126         readWriteId = (*env)->GetStaticFieldID(env, clazz, "in",
    127                 "Ljava/io/FileDescriptor;");
    128         if(readWriteId == NULL) {
    129             jniThrowException(env, "java/lang/NoSuchFieldException",
    130                                     "FileDescriptor.readOnly(Z)");
    131             return -1;
    132         }
    133 
    134         (void) (*env)->GetStaticObjectField(env, clazz, readWriteId);
    135     }
    136 
    137     return 0;
    138 }
    139 
    140 
    141 /*
    142  * For JNIHelp.c
    143  * Create a java.io.FileDescriptor given an integer fd
    144  */
    145 
    146 jobject jniCreateFileDescriptor (JNIEnv *env, int fd) {
    147     jobject ret;
    148 
    149     /* the class may not have been loaded yet */
    150     if(checkClassInit(env) < 0) {
    151         return NULL;
    152     }
    153 
    154     ret = (*env)->NewObject(env, gCachedFields.clazz,
    155             gCachedFields.constructorInt);
    156 
    157     (*env)->SetIntField(env, ret, gCachedFields.descriptor, fd);
    158 
    159     return ret;
    160 }
    161 
    162 /*
    163  * For JNIHelp.c
    164  * Get an int file descriptor from a java.io.FileDescriptor
    165  */
    166 
    167 int jniGetFDFromFileDescriptor (JNIEnv* env, jobject fileDescriptor) {
    168     /* should already be initialized if it's an actual FileDescriptor */
    169     assert(fileDescriptor != NULL);
    170     assert(gCachedFields.clazz != NULL);
    171 
    172     return getFd(env, fileDescriptor);
    173 }
    174 
    175 /*
    176  * For JNIHelp.c
    177  * Set the descriptor of a java.io.FileDescriptor
    178  */
    179 
    180 void jniSetFileDescriptorOfFD (JNIEnv* env, jobject fileDescriptor, int value) {
    181     /* should already be initialized if it's an actual FileDescriptor */
    182     assert(fileDescriptor != NULL);
    183     assert(gCachedFields.clazz != NULL);
    184 
    185     setFd(env, fileDescriptor, value);
    186 }
    187 
    188 /*
    189  * JNI registration
    190  */
    191 static JNINativeMethod gMethods[] = {
    192     /* name, signature, funcPtr */
    193     { "oneTimeInitialization", "()V",              nativeClassInit },
    194     { "syncImpl",           "()V",                 fd_sync }
    195 };
    196 int register_java_io_FileDescriptor(JNIEnv* env) {
    197     return jniRegisterNativeMethods(env, "java/io/FileDescriptor",
    198         gMethods, NELEM(gMethods));
    199 }
    200