| 1 | /* |
| 2 | * Copyright (c) 2000, 2018, 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 <sys/mman.h> |
| 27 | #include <sys/stat.h> |
| 28 | #include <fcntl.h> |
| 29 | #include <sys/types.h> |
| 30 | #include <unistd.h> |
| 31 | |
| 32 | #if defined(__linux__) || defined(__solaris__) |
| 33 | #include <sys/sendfile.h> |
| 34 | #elif defined(_AIX) |
| 35 | #include <sys/socket.h> |
| 36 | #elif defined(_ALLBSD_SOURCE) |
| 37 | #include <sys/socket.h> |
| 38 | #include <sys/uio.h> |
| 39 | #define lseek64 lseek |
| 40 | #define mmap64 mmap |
| 41 | #endif |
| 42 | |
| 43 | #include "jni.h" |
| 44 | #include "jni_util.h" |
| 45 | #include "jlong.h" |
| 46 | #include "nio.h" |
| 47 | #include "nio_util.h" |
| 48 | #include "sun_nio_ch_FileChannelImpl.h" |
| 49 | #include "java_lang_Integer.h" |
| 50 | |
| 51 | static jfieldID chan_fd; /* jobject 'fd' in sun.nio.ch.FileChannelImpl */ |
| 52 | |
| 53 | JNIEXPORT jlong JNICALL |
| 54 | Java_sun_nio_ch_FileChannelImpl_initIDs(JNIEnv *env, jclass clazz) |
| 55 | { |
| 56 | jlong pageSize = sysconf(_SC_PAGESIZE); |
| 57 | chan_fd = (*env)->GetFieldID(env, clazz, "fd" , "Ljava/io/FileDescriptor;" ); |
| 58 | return pageSize; |
| 59 | } |
| 60 | |
| 61 | static jlong |
| 62 | handle(JNIEnv *env, jlong rv, char *msg) |
| 63 | { |
| 64 | if (rv >= 0) |
| 65 | return rv; |
| 66 | if (errno == EINTR) |
| 67 | return IOS_INTERRUPTED; |
| 68 | JNU_ThrowIOExceptionWithLastError(env, msg); |
| 69 | return IOS_THROWN; |
| 70 | } |
| 71 | |
| 72 | |
| 73 | JNIEXPORT jlong JNICALL |
| 74 | Java_sun_nio_ch_FileChannelImpl_map0(JNIEnv *env, jobject this, |
| 75 | jint prot, jlong off, jlong len) |
| 76 | { |
| 77 | void *mapAddress = 0; |
| 78 | jobject fdo = (*env)->GetObjectField(env, this, chan_fd); |
| 79 | jint fd = fdval(env, fdo); |
| 80 | int protections = 0; |
| 81 | int flags = 0; |
| 82 | |
| 83 | if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) { |
| 84 | protections = PROT_READ; |
| 85 | flags = MAP_SHARED; |
| 86 | } else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) { |
| 87 | protections = PROT_WRITE | PROT_READ; |
| 88 | flags = MAP_SHARED; |
| 89 | } else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) { |
| 90 | protections = PROT_WRITE | PROT_READ; |
| 91 | flags = MAP_PRIVATE; |
| 92 | } |
| 93 | |
| 94 | mapAddress = mmap64( |
| 95 | 0, /* Let OS decide location */ |
| 96 | len, /* Number of bytes to map */ |
| 97 | protections, /* File permissions */ |
| 98 | flags, /* Changes are shared */ |
| 99 | fd, /* File descriptor of mapped file */ |
| 100 | off); /* Offset into file */ |
| 101 | |
| 102 | if (mapAddress == MAP_FAILED) { |
| 103 | if (errno == ENOMEM) { |
| 104 | JNU_ThrowOutOfMemoryError(env, "Map failed" ); |
| 105 | return IOS_THROWN; |
| 106 | } |
| 107 | return handle(env, -1, "Map failed" ); |
| 108 | } |
| 109 | |
| 110 | return ((jlong) (unsigned long) mapAddress); |
| 111 | } |
| 112 | |
| 113 | |
| 114 | JNIEXPORT jint JNICALL |
| 115 | Java_sun_nio_ch_FileChannelImpl_unmap0(JNIEnv *env, jobject this, |
| 116 | jlong address, jlong len) |
| 117 | { |
| 118 | void *a = (void *)jlong_to_ptr(address); |
| 119 | return handle(env, |
| 120 | munmap(a, (size_t)len), |
| 121 | "Unmap failed" ); |
| 122 | } |
| 123 | |
| 124 | JNIEXPORT jlong JNICALL |
| 125 | Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this, |
| 126 | jobject srcFDO, |
| 127 | jlong position, jlong count, |
| 128 | jobject dstFDO) |
| 129 | { |
| 130 | jint srcFD = fdval(env, srcFDO); |
| 131 | jint dstFD = fdval(env, dstFDO); |
| 132 | |
| 133 | #if defined(__linux__) |
| 134 | off64_t offset = (off64_t)position; |
| 135 | jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count); |
| 136 | if (n < 0) { |
| 137 | if (errno == EAGAIN) |
| 138 | return IOS_UNAVAILABLE; |
| 139 | if ((errno == EINVAL) && ((ssize_t)count >= 0)) |
| 140 | return IOS_UNSUPPORTED_CASE; |
| 141 | if (errno == EINTR) { |
| 142 | return IOS_INTERRUPTED; |
| 143 | } |
| 144 | JNU_ThrowIOExceptionWithLastError(env, "Transfer failed" ); |
| 145 | return IOS_THROWN; |
| 146 | } |
| 147 | return n; |
| 148 | #elif defined (__solaris__) |
| 149 | sendfilevec64_t sfv; |
| 150 | size_t numBytes = 0; |
| 151 | jlong result; |
| 152 | |
| 153 | sfv.sfv_fd = srcFD; |
| 154 | sfv.sfv_flag = 0; |
| 155 | sfv.sfv_off = (off64_t)position; |
| 156 | sfv.sfv_len = count; |
| 157 | |
| 158 | result = sendfilev64(dstFD, &sfv, 1, &numBytes); |
| 159 | |
| 160 | /* Solaris sendfilev() will return -1 even if some bytes have been |
| 161 | * transferred, so we check numBytes first. |
| 162 | */ |
| 163 | if (numBytes > 0) |
| 164 | return numBytes; |
| 165 | if (result < 0) { |
| 166 | if (errno == EAGAIN) |
| 167 | return IOS_UNAVAILABLE; |
| 168 | if (errno == EOPNOTSUPP) |
| 169 | return IOS_UNSUPPORTED_CASE; |
| 170 | if ((errno == EINVAL) && ((ssize_t)count >= 0)) |
| 171 | return IOS_UNSUPPORTED_CASE; |
| 172 | if (errno == EINTR) |
| 173 | return IOS_INTERRUPTED; |
| 174 | JNU_ThrowIOExceptionWithLastError(env, "Transfer failed" ); |
| 175 | return IOS_THROWN; |
| 176 | } |
| 177 | return result; |
| 178 | #elif defined(__APPLE__) |
| 179 | off_t numBytes; |
| 180 | int result; |
| 181 | |
| 182 | numBytes = count; |
| 183 | |
| 184 | result = sendfile(srcFD, dstFD, position, &numBytes, NULL, 0); |
| 185 | |
| 186 | if (numBytes > 0) |
| 187 | return numBytes; |
| 188 | |
| 189 | if (result == -1) { |
| 190 | if (errno == EAGAIN) |
| 191 | return IOS_UNAVAILABLE; |
| 192 | if (errno == EOPNOTSUPP || errno == ENOTSOCK || errno == ENOTCONN) |
| 193 | return IOS_UNSUPPORTED_CASE; |
| 194 | if ((errno == EINVAL) && ((ssize_t)count >= 0)) |
| 195 | return IOS_UNSUPPORTED_CASE; |
| 196 | if (errno == EINTR) |
| 197 | return IOS_INTERRUPTED; |
| 198 | JNU_ThrowIOExceptionWithLastError(env, "Transfer failed" ); |
| 199 | return IOS_THROWN; |
| 200 | } |
| 201 | |
| 202 | return result; |
| 203 | |
| 204 | #elif defined(_AIX) |
| 205 | jlong max = (jlong)java_lang_Integer_MAX_VALUE; |
| 206 | struct sf_parms sf_iobuf; |
| 207 | jlong result; |
| 208 | |
| 209 | if (position > max) |
| 210 | return IOS_UNSUPPORTED_CASE; |
| 211 | |
| 212 | if (count > max) |
| 213 | count = max; |
| 214 | |
| 215 | memset(&sf_iobuf, 0, sizeof(sf_iobuf)); |
| 216 | sf_iobuf.file_descriptor = srcFD; |
| 217 | sf_iobuf.file_offset = (off_t)position; |
| 218 | sf_iobuf.file_bytes = count; |
| 219 | |
| 220 | result = send_file(&dstFD, &sf_iobuf, SF_SYNC_CACHE); |
| 221 | |
| 222 | /* AIX send_file() will return 0 when this operation complete successfully, |
| 223 | * return 1 when partial bytes transfered and return -1 when an error has |
| 224 | * Occured. |
| 225 | */ |
| 226 | if (result == -1) { |
| 227 | if (errno == EWOULDBLOCK) |
| 228 | return IOS_UNAVAILABLE; |
| 229 | if ((errno == EINVAL) && ((ssize_t)count >= 0)) |
| 230 | return IOS_UNSUPPORTED_CASE; |
| 231 | if (errno == EINTR) |
| 232 | return IOS_INTERRUPTED; |
| 233 | if (errno == ENOTSOCK) |
| 234 | return IOS_UNSUPPORTED; |
| 235 | JNU_ThrowIOExceptionWithLastError(env, "Transfer failed" ); |
| 236 | return IOS_THROWN; |
| 237 | } |
| 238 | |
| 239 | if (sf_iobuf.bytes_sent > 0) |
| 240 | return (jlong)sf_iobuf.bytes_sent; |
| 241 | |
| 242 | return IOS_UNSUPPORTED_CASE; |
| 243 | #else |
| 244 | return IOS_UNSUPPORTED_CASE; |
| 245 | #endif |
| 246 | } |
| 247 | |
| 248 | |