1 /* 2 * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "jni.h" 27 #include "jni_util.h" 28 #include "jvm.h" 29 #include "jvm_md.h" 30 #include "jlong.h" 31 #include <sys/mman.h> 32 #include <sys/stat.h> 33 #include <fcntl.h> 34 #include "sun_nio_ch_FileChannelImpl.h" 35 #include "nio.h" 36 #include "nio_util.h" 37 #include <dlfcn.h> 38 #include "JNIHelp.h" 39 40 #define NATIVE_METHOD(className, functionName, signature) \ 41 { #functionName, signature, (void*)(className ## _ ## functionName) } 42 43 #if defined(__linux__) || defined(__solaris__) 44 #include <sys/sendfile.h> 45 #elif defined(_ALLBSD_SOURCE) 46 #include <sys/types.h> 47 #include <sys/socket.h> 48 #include <sys/uio.h> 49 50 #define lseek64 lseek 51 #define mmap64 mmap 52 #endif 53 54 static jfieldID chan_fd; /* jobject 'fd' in sun.io.FileChannelImpl */ 55 56 JNIEXPORT jlong JNICALL 57 FileChannelImpl_initIDs(JNIEnv *env, jclass clazz) 58 { 59 jlong pageSize = sysconf(_SC_PAGESIZE); 60 chan_fd = (*env)->GetFieldID(env, clazz, "fd", "Ljava/io/FileDescriptor;"); 61 return pageSize; 62 } 63 64 static jlong 65 handle(JNIEnv *env, jlong rv, char *msg) 66 { 67 if (rv >= 0) 68 return rv; 69 if (errno == EINTR) 70 return IOS_INTERRUPTED; 71 JNU_ThrowIOExceptionWithLastError(env, msg); 72 return IOS_THROWN; 73 } 74 75 76 JNIEXPORT jlong JNICALL 77 FileChannelImpl_map0(JNIEnv *env, jobject this, 78 jint prot, jlong off, jlong len) 79 { 80 void *mapAddress = 0; 81 jobject fdo = (*env)->GetObjectField(env, this, chan_fd); 82 jint fd = fdval(env, fdo); 83 int protections = 0; 84 int flags = 0; 85 86 if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) { 87 protections = PROT_READ; 88 flags = MAP_SHARED; 89 } else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) { 90 protections = PROT_WRITE | PROT_READ; 91 flags = MAP_SHARED; 92 } else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) { 93 protections = PROT_WRITE | PROT_READ; 94 flags = MAP_PRIVATE; 95 } 96 97 mapAddress = mmap64( 98 0, /* Let OS decide location */ 99 len, /* Number of bytes to map */ 100 protections, /* File permissions */ 101 flags, /* Changes are shared */ 102 fd, /* File descriptor of mapped file */ 103 off); /* Offset into file */ 104 105 if (mapAddress == MAP_FAILED) { 106 if (errno == ENOMEM) { 107 JNU_ThrowOutOfMemoryError(env, "Map failed"); 108 return IOS_THROWN; 109 } 110 return handle(env, -1, "Map failed"); 111 } 112 113 return ((jlong) (unsigned long) mapAddress); 114 } 115 116 117 JNIEXPORT jint JNICALL 118 FileChannelImpl_unmap0(JNIEnv *env, jobject this, 119 jlong address, jlong len) 120 { 121 void *a = (void *)jlong_to_ptr(address); 122 return handle(env, 123 munmap(a, (size_t)len), 124 "Unmap failed"); 125 } 126 127 128 JNIEXPORT jlong JNICALL 129 FileChannelImpl_position0(JNIEnv *env, jobject this, 130 jobject fdo, jlong offset) 131 { 132 jint fd = fdval(env, fdo); 133 jlong result = 0; 134 135 if (offset < 0) { 136 result = lseek64(fd, 0, SEEK_CUR); 137 } else { 138 result = lseek64(fd, offset, SEEK_SET); 139 } 140 return handle(env, result, "Position failed"); 141 } 142 143 144 JNIEXPORT void JNICALL 145 FileChannelImpl_close0(JNIEnv *env, jobject this, jobject fdo) 146 { 147 jint fd = fdval(env, fdo); 148 if (fd != -1) { 149 jlong result = close(fd); 150 if (result < 0) { 151 JNU_ThrowIOExceptionWithLastError(env, "Close failed"); 152 } 153 } 154 } 155 156 JNIEXPORT jlong JNICALL 157 FileChannelImpl_transferTo0(JNIEnv *env, jobject this, 158 jint srcFD, 159 jlong position, jlong count, 160 jint dstFD) 161 { 162 #if defined(__linux__) 163 off64_t offset = (off64_t)position; 164 jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count); 165 if (n < 0) { 166 if (errno == EAGAIN) 167 return IOS_UNAVAILABLE; 168 if ((errno == EINVAL) && ((ssize_t)count >= 0)) 169 return IOS_UNSUPPORTED_CASE; 170 if (errno == EINTR) { 171 return IOS_INTERRUPTED; 172 } 173 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); 174 return IOS_THROWN; 175 } 176 return n; 177 #elif defined (__solaris__) 178 sendfilevec64_t sfv; 179 size_t numBytes = 0; 180 jlong result; 181 182 sfv.sfv_fd = srcFD; 183 sfv.sfv_flag = 0; 184 sfv.sfv_off = (off64_t)position; 185 sfv.sfv_len = count; 186 187 result = sendfilev64(dstFD, &sfv, 1, &numBytes); 188 189 /* Solaris sendfilev() will return -1 even if some bytes have been 190 * transferred, so we check numBytes first. 191 */ 192 if (numBytes > 0) 193 return numBytes; 194 if (result < 0) { 195 if (errno == EAGAIN) 196 return IOS_UNAVAILABLE; 197 if (errno == EOPNOTSUPP) 198 return IOS_UNSUPPORTED_CASE; 199 if ((errno == EINVAL) && ((ssize_t)count >= 0)) 200 return IOS_UNSUPPORTED_CASE; 201 if (errno == EINTR) 202 return IOS_INTERRUPTED; 203 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); 204 return IOS_THROWN; 205 } 206 return result; 207 #elif defined(__APPLE__) 208 off_t numBytes; 209 int result; 210 211 numBytes = count; 212 213 #ifdef __APPLE__ 214 result = sendfile(srcFD, dstFD, position, &numBytes, NULL, 0); 215 #endif 216 217 if (numBytes > 0) 218 return numBytes; 219 220 if (result == -1) { 221 if (errno == EAGAIN) 222 return IOS_UNAVAILABLE; 223 if (errno == EOPNOTSUPP || errno == ENOTSOCK || errno == ENOTCONN) 224 return IOS_UNSUPPORTED_CASE; 225 if ((errno == EINVAL) && ((ssize_t)count >= 0)) 226 return IOS_UNSUPPORTED_CASE; 227 if (errno == EINTR) 228 return IOS_INTERRUPTED; 229 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); 230 return IOS_THROWN; 231 } 232 233 return result; 234 #else 235 return IOS_UNSUPPORTED_CASE; 236 #endif 237 } 238 239 static JNINativeMethod gMethods[] = { 240 NATIVE_METHOD(FileChannelImpl, initIDs, "()J"), 241 NATIVE_METHOD(FileChannelImpl, map0, "(IJJ)J"), 242 NATIVE_METHOD(FileChannelImpl, unmap0, "(JJ)I"), 243 NATIVE_METHOD(FileChannelImpl, position0, "(Ljava/io/FileDescriptor;J)J"), 244 NATIVE_METHOD(FileChannelImpl, transferTo0, "(IJJI)J"), 245 }; 246 247 void register_sun_nio_ch_FileChannelImpl(JNIEnv* env) { 248 jniRegisterNativeMethods(env, "sun/nio/ch/FileChannelImpl", gMethods, NELEM(gMethods)); 249 } 250