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 <nativehelper/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(_AIX) 46 #include <sys/socket.h> 47 #elif defined(_ALLBSD_SOURCE) 48 #include <sys/types.h> 49 #include <sys/socket.h> 50 #include <sys/uio.h> 51 52 #define lseek64 lseek 53 #define mmap64 mmap 54 #endif 55 56 static jfieldID chan_fd; /* jobject 'fd' in sun.io.FileChannelImpl */ 57 58 JNIEXPORT jlong JNICALL 59 FileChannelImpl_initIDs(JNIEnv *env, jclass clazz) 60 { 61 jlong pageSize = sysconf(_SC_PAGESIZE); 62 chan_fd = (*env)->GetFieldID(env, clazz, "fd", "Ljava/io/FileDescriptor;"); 63 return pageSize; 64 } 65 66 static jlong 67 handle(JNIEnv *env, jlong rv, char *msg) 68 { 69 if (rv >= 0) 70 return rv; 71 if (errno == EINTR) 72 return IOS_INTERRUPTED; 73 JNU_ThrowIOExceptionWithLastError(env, msg); 74 return IOS_THROWN; 75 } 76 77 78 JNIEXPORT jlong JNICALL 79 FileChannelImpl_map0(JNIEnv *env, jobject this, 80 jint prot, jlong off, jlong len) 81 { 82 void *mapAddress = 0; 83 jobject fdo = (*env)->GetObjectField(env, this, chan_fd); 84 jint fd = fdval(env, fdo); 85 int protections = 0; 86 int flags = 0; 87 88 if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) { 89 protections = PROT_READ; 90 flags = MAP_SHARED; 91 } else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) { 92 protections = PROT_WRITE | PROT_READ; 93 flags = MAP_SHARED; 94 } else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) { 95 protections = PROT_WRITE | PROT_READ; 96 flags = MAP_PRIVATE; 97 } 98 99 mapAddress = mmap64( 100 0, /* Let OS decide location */ 101 len, /* Number of bytes to map */ 102 protections, /* File permissions */ 103 flags, /* Changes are shared */ 104 fd, /* File descriptor of mapped file */ 105 off); /* Offset into file */ 106 107 if (mapAddress == MAP_FAILED) { 108 if (errno == ENOMEM) { 109 JNU_ThrowOutOfMemoryError(env, "Map failed"); 110 return IOS_THROWN; 111 } 112 return handle(env, -1, "Map failed"); 113 } 114 115 return ((jlong) (unsigned long) mapAddress); 116 } 117 118 119 JNIEXPORT jint JNICALL 120 FileChannelImpl_unmap0(JNIEnv *env, jobject this, 121 jlong address, jlong len) 122 { 123 void *a = (void *)jlong_to_ptr(address); 124 return handle(env, 125 munmap(a, (size_t)len), 126 "Unmap failed"); 127 } 128 129 130 JNIEXPORT jlong JNICALL 131 FileChannelImpl_position0(JNIEnv *env, jobject this, 132 jobject fdo, jlong offset) 133 { 134 jint fd = fdval(env, fdo); 135 jlong result = 0; 136 137 if (offset < 0) { 138 result = lseek64(fd, 0, SEEK_CUR); 139 } else { 140 result = lseek64(fd, offset, SEEK_SET); 141 } 142 return handle(env, result, "Position failed"); 143 } 144 145 146 JNIEXPORT void JNICALL 147 FileChannelImpl_close0(JNIEnv *env, jobject this, jobject fdo) 148 { 149 jint fd = fdval(env, fdo); 150 if (fd != -1) { 151 jlong result = close(fd); 152 if (result < 0) { 153 JNU_ThrowIOExceptionWithLastError(env, "Close failed"); 154 } 155 } 156 } 157 158 JNIEXPORT jlong JNICALL 159 FileChannelImpl_transferTo0(JNIEnv *env, jobject this, 160 jobject srcFDO, 161 jlong position, jlong count, 162 jobject dstFDO) 163 { 164 jint srcFD = fdval(env, srcFDO); 165 jint dstFD = fdval(env, dstFDO); 166 167 #if defined(__linux__) 168 off64_t offset = (off64_t)position; 169 jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count); 170 if (n < 0) { 171 if (errno == EAGAIN) 172 return IOS_UNAVAILABLE; 173 if ((errno == EINVAL) && ((ssize_t)count >= 0)) 174 return IOS_UNSUPPORTED_CASE; 175 if (errno == EINTR) { 176 return IOS_INTERRUPTED; 177 } 178 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); 179 return IOS_THROWN; 180 } 181 return n; 182 #elif defined (__solaris__) 183 sendfilevec64_t sfv; 184 size_t numBytes = 0; 185 jlong result; 186 187 sfv.sfv_fd = srcFD; 188 sfv.sfv_flag = 0; 189 sfv.sfv_off = (off64_t)position; 190 sfv.sfv_len = count; 191 192 result = sendfilev64(dstFD, &sfv, 1, &numBytes); 193 194 /* Solaris sendfilev() will return -1 even if some bytes have been 195 * transferred, so we check numBytes first. 196 */ 197 if (numBytes > 0) 198 return numBytes; 199 if (result < 0) { 200 if (errno == EAGAIN) 201 return IOS_UNAVAILABLE; 202 if (errno == EOPNOTSUPP) 203 return IOS_UNSUPPORTED_CASE; 204 if ((errno == EINVAL) && ((ssize_t)count >= 0)) 205 return IOS_UNSUPPORTED_CASE; 206 if (errno == EINTR) 207 return IOS_INTERRUPTED; 208 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); 209 return IOS_THROWN; 210 } 211 return result; 212 #elif defined(__APPLE__) 213 off_t numBytes; 214 int result; 215 216 numBytes = count; 217 218 result = sendfile(srcFD, dstFD, position, &numBytes, NULL, 0); 219 220 if (numBytes > 0) 221 return numBytes; 222 223 if (result == -1) { 224 if (errno == EAGAIN) 225 return IOS_UNAVAILABLE; 226 if (errno == EOPNOTSUPP || errno == ENOTSOCK || errno == ENOTCONN) 227 return IOS_UNSUPPORTED_CASE; 228 if ((errno == EINVAL) && ((ssize_t)count >= 0)) 229 return IOS_UNSUPPORTED_CASE; 230 if (errno == EINTR) 231 return IOS_INTERRUPTED; 232 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); 233 return IOS_THROWN; 234 } 235 236 return result; 237 238 #elif defined(_AIX) 239 jlong max = (jlong)java_lang_Integer_MAX_VALUE; 240 struct sf_parms sf_iobuf; 241 jlong result; 242 243 if (position > max) 244 return IOS_UNSUPPORTED_CASE; 245 246 if (count > max) 247 count = max; 248 249 memset(&sf_iobuf, 0, sizeof(sf_iobuf)); 250 sf_iobuf.file_descriptor = srcFD; 251 sf_iobuf.file_offset = (off_t)position; 252 sf_iobuf.file_bytes = count; 253 254 result = send_file(&dstFD, &sf_iobuf, SF_SYNC_CACHE); 255 256 /* AIX send_file() will return 0 when this operation complete successfully, 257 * return 1 when partial bytes transfered and return -1 when an error has 258 * Occured. 259 */ 260 if (result == -1) { 261 if (errno == EWOULDBLOCK) 262 return IOS_UNAVAILABLE; 263 if ((errno == EINVAL) && ((ssize_t)count >= 0)) 264 return IOS_UNSUPPORTED_CASE; 265 if (errno == EINTR) 266 return IOS_INTERRUPTED; 267 if (errno == ENOTSOCK) 268 return IOS_UNSUPPORTED; 269 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); 270 return IOS_THROWN; 271 } 272 273 if (sf_iobuf.bytes_sent > 0) 274 return (jlong)sf_iobuf.bytes_sent; 275 276 return IOS_UNSUPPORTED_CASE; 277 #else 278 return IOS_UNSUPPORTED_CASE; 279 #endif 280 } 281 282 static JNINativeMethod gMethods[] = { 283 NATIVE_METHOD(FileChannelImpl, initIDs, "()J"), 284 NATIVE_METHOD(FileChannelImpl, map0, "(IJJ)J"), 285 NATIVE_METHOD(FileChannelImpl, unmap0, "(JJ)I"), 286 NATIVE_METHOD(FileChannelImpl, position0, "(Ljava/io/FileDescriptor;J)J"), 287 NATIVE_METHOD(FileChannelImpl, transferTo0, "(Ljava/io/FileDescriptor;JJLjava/io/FileDescriptor;)J"), 288 }; 289 290 void register_sun_nio_ch_FileChannelImpl(JNIEnv* env) { 291 jniRegisterNativeMethods(env, "sun/nio/ch/FileChannelImpl", gMethods, NELEM(gMethods)); 292 } 293