1 /* 2 * Copyright (c) 2000, 2009, 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 "jlong.h" 30 #include <sys/mman.h> 31 #include <sys/stat.h> 32 #include "sun_nio_ch_FileChannelImpl.h" 33 #include "java_lang_Integer.h" 34 #include "nio.h" 35 #include "nio_util.h" 36 #include <dlfcn.h> 37 38 static jfieldID chan_fd; /* jobject 'fd' in sun.io.FileChannelImpl */ 39 40 #ifdef __solaris__ 41 typedef struct sendfilevec64 { 42 int sfv_fd; /* input fd */ 43 uint_t sfv_flag; /* Flags. see below */ 44 off64_t sfv_off; /* offset to start reading from */ 45 size_t sfv_len; /* amount of data */ 46 } sendfilevec_t; 47 48 /* Function pointer for sendfilev on Solaris 8+ */ 49 typedef ssize_t sendfile_func(int fildes, const struct sendfilevec64 *vec, 50 int sfvcnt, size_t *xferred); 51 52 sendfile_func* my_sendfile_func = NULL; 53 #endif 54 55 #ifdef __linux__ 56 #include <sys/sendfile.h> 57 58 /* Function pointer for sendfile64 on Linux 2.6 (and newer 2.4 kernels) */ 59 typedef ssize_t sendfile64_func(int out_fd, int in_fd, off64_t *offset, size_t count); 60 61 sendfile64_func* my_sendfile64_func = NULL; 62 #endif 63 64 JNIEXPORT jlong JNICALL 65 Java_sun_nio_ch_FileChannelImpl_initIDs(JNIEnv *env, jclass clazz) 66 { 67 jlong pageSize = sysconf(_SC_PAGESIZE); 68 chan_fd = (*env)->GetFieldID(env, clazz, "fd", "Ljava/io/FileDescriptor;"); 69 70 #ifdef __solaris__ 71 if (dlopen("/usr/lib/libsendfile.so.1", RTLD_GLOBAL | RTLD_LAZY) != NULL) { 72 my_sendfile_func = (sendfile_func*) dlsym(RTLD_DEFAULT, "sendfilev64"); 73 } 74 #endif 75 76 #ifdef __linux__ 77 my_sendfile64_func = (sendfile64_func*) dlsym(RTLD_DEFAULT, "sendfile64"); 78 #endif 79 80 return pageSize; 81 } 82 83 static jlong 84 handle(JNIEnv *env, jlong rv, char *msg) 85 { 86 if (rv >= 0) 87 return rv; 88 if (errno == EINTR) 89 return IOS_INTERRUPTED; 90 JNU_ThrowIOExceptionWithLastError(env, msg); 91 return IOS_THROWN; 92 } 93 94 95 JNIEXPORT jlong JNICALL 96 Java_sun_nio_ch_FileChannelImpl_map0(JNIEnv *env, jobject this, 97 jint prot, jlong off, jlong len) 98 { 99 void *mapAddress = 0; 100 jobject fdo = (*env)->GetObjectField(env, this, chan_fd); 101 jint fd = fdval(env, fdo); 102 int protections = 0; 103 int flags = 0; 104 105 if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) { 106 protections = PROT_READ; 107 flags = MAP_SHARED; 108 } else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) { 109 protections = PROT_WRITE | PROT_READ; 110 flags = MAP_SHARED; 111 } else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) { 112 protections = PROT_WRITE | PROT_READ; 113 flags = MAP_PRIVATE; 114 } 115 116 mapAddress = mmap64( 117 0, /* Let OS decide location */ 118 len, /* Number of bytes to map */ 119 protections, /* File permissions */ 120 flags, /* Changes are shared */ 121 fd, /* File descriptor of mapped file */ 122 off); /* Offset into file */ 123 124 if (mapAddress == MAP_FAILED) { 125 if (errno == ENOMEM) { 126 JNU_ThrowOutOfMemoryError(env, "Map failed"); 127 return IOS_THROWN; 128 } 129 return handle(env, -1, "Map failed"); 130 } 131 132 return ((jlong) (unsigned long) mapAddress); 133 } 134 135 136 JNIEXPORT jint JNICALL 137 Java_sun_nio_ch_FileChannelImpl_unmap0(JNIEnv *env, jobject this, 138 jlong address, jlong len) 139 { 140 void *a = (void *)jlong_to_ptr(address); 141 return handle(env, 142 munmap(a, (size_t)len), 143 "Unmap failed"); 144 } 145 146 147 JNIEXPORT jlong JNICALL 148 Java_sun_nio_ch_FileChannelImpl_position0(JNIEnv *env, jobject this, 149 jobject fdo, jlong offset) 150 { 151 jint fd = fdval(env, fdo); 152 jlong result = 0; 153 154 if (offset < 0) { 155 result = lseek64(fd, 0, SEEK_CUR); 156 } else { 157 result = lseek64(fd, offset, SEEK_SET); 158 } 159 return handle(env, result, "Position failed"); 160 } 161 162 163 JNIEXPORT void JNICALL 164 Java_sun_nio_ch_FileChannelImpl_close0(JNIEnv *env, jobject this, jobject fdo) 165 { 166 jint fd = fdval(env, fdo); 167 if (fd != -1) { 168 jlong result = close(fd); 169 if (result < 0) { 170 JNU_ThrowIOExceptionWithLastError(env, "Close failed"); 171 } 172 } 173 } 174 175 JNIEXPORT jlong JNICALL 176 Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this, 177 jint srcFD, 178 jlong position, jlong count, 179 jint dstFD) 180 { 181 #ifdef __linux__ 182 jlong max = (jlong)java_lang_Integer_MAX_VALUE; 183 jlong n; 184 185 if (my_sendfile64_func == NULL) { 186 off_t offset; 187 if (position > max) 188 return IOS_UNSUPPORTED_CASE; 189 if (count > max) 190 count = max; 191 offset = (off_t)position; 192 n = sendfile(dstFD, srcFD, &offset, (size_t)count); 193 } else { 194 off64_t offset = (off64_t)position; 195 n = (*my_sendfile64_func)(dstFD, srcFD, &offset, (size_t)count); 196 } 197 if (n < 0) { 198 if (errno == EAGAIN) 199 return IOS_UNAVAILABLE; 200 if ((errno == EINVAL) && ((ssize_t)count >= 0)) 201 return IOS_UNSUPPORTED_CASE; 202 if (errno == EINTR) { 203 return IOS_INTERRUPTED; 204 } 205 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); 206 return IOS_THROWN; 207 } 208 return n; 209 #endif 210 211 #ifdef __solaris__ 212 if (my_sendfile_func == NULL) { 213 return IOS_UNSUPPORTED; 214 } else { 215 sendfilevec_t sfv; 216 size_t numBytes = 0; 217 jlong result; 218 219 sfv.sfv_fd = srcFD; 220 sfv.sfv_flag = 0; 221 sfv.sfv_off = (off64_t)position; 222 sfv.sfv_len = count; 223 224 result = (*my_sendfile_func)(dstFD, &sfv, 1, &numBytes); 225 226 /* Solaris sendfilev() will return -1 even if some bytes have been 227 * transferred, so we check numBytes first. 228 */ 229 if (numBytes > 0) 230 return numBytes; 231 if (result < 0) { 232 if (errno == EAGAIN) 233 return IOS_UNAVAILABLE; 234 if (errno == EOPNOTSUPP) 235 return IOS_UNSUPPORTED_CASE; 236 if ((errno == EINVAL) && ((ssize_t)count >= 0)) 237 return IOS_UNSUPPORTED_CASE; 238 if (errno == EINTR) 239 return IOS_INTERRUPTED; 240 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); 241 return IOS_THROWN; 242 } 243 return result; 244 } 245 #endif 246 }