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