| /* |
| * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include "jni.h" |
| #include "jni_util.h" |
| #include "jvm.h" |
| #include "jvm_md.h" |
| #include "jlong.h" |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include "sun_nio_ch_FileChannelImpl.h" |
| #include "nio.h" |
| #include "nio_util.h" |
| #include <dlfcn.h> |
| #include <nativehelper/JNIHelp.h> |
| |
| #define NATIVE_METHOD(className, functionName, signature) \ |
| { #functionName, signature, (void*)(className ## _ ## functionName) } |
| |
| #if defined(__linux__) || defined(__solaris__) |
| #include <sys/sendfile.h> |
| #elif defined(_AIX) |
| #include <sys/socket.h> |
| #elif defined(_ALLBSD_SOURCE) |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/uio.h> |
| |
| #define lseek64 lseek |
| #define mmap64 mmap |
| // BEGIN Android-added: Alias *64 on Fuchsia builds. http://b/119496969 |
| #elif defined(__Fuchsia__) |
| #define lseek64 lseek |
| #define mmap64 mmap |
| // END Android-added: Alias *64 on Fuchsia builds. http://b/119496969 |
| #endif |
| |
| static jfieldID chan_fd; /* jobject 'fd' in sun.io.FileChannelImpl */ |
| |
| JNIEXPORT jlong JNICALL |
| FileChannelImpl_initIDs(JNIEnv *env, jclass clazz) |
| { |
| jlong pageSize = sysconf(_SC_PAGESIZE); |
| chan_fd = (*env)->GetFieldID(env, clazz, "fd", "Ljava/io/FileDescriptor;"); |
| return pageSize; |
| } |
| |
| static jlong |
| handle(JNIEnv *env, jlong rv, char *msg) |
| { |
| if (rv >= 0) |
| return rv; |
| if (errno == EINTR) |
| return IOS_INTERRUPTED; |
| JNU_ThrowIOExceptionWithLastError(env, msg); |
| return IOS_THROWN; |
| } |
| |
| |
| JNIEXPORT jlong JNICALL |
| FileChannelImpl_map0(JNIEnv *env, jobject this, |
| jint prot, jlong off, jlong len) |
| { |
| void *mapAddress = 0; |
| jobject fdo = (*env)->GetObjectField(env, this, chan_fd); |
| jint fd = fdval(env, fdo); |
| int protections = 0; |
| int flags = 0; |
| |
| if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) { |
| protections = PROT_READ; |
| flags = MAP_SHARED; |
| } else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) { |
| protections = PROT_WRITE | PROT_READ; |
| flags = MAP_SHARED; |
| } else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) { |
| protections = PROT_WRITE | PROT_READ; |
| flags = MAP_PRIVATE; |
| } |
| |
| mapAddress = mmap64( |
| 0, /* Let OS decide location */ |
| len, /* Number of bytes to map */ |
| protections, /* File permissions */ |
| flags, /* Changes are shared */ |
| fd, /* File descriptor of mapped file */ |
| off); /* Offset into file */ |
| |
| if (mapAddress == MAP_FAILED) { |
| if (errno == ENOMEM) { |
| JNU_ThrowOutOfMemoryError(env, "Map failed"); |
| return IOS_THROWN; |
| } |
| return handle(env, -1, "Map failed"); |
| } |
| |
| return ((jlong) (unsigned long) mapAddress); |
| } |
| |
| |
| JNIEXPORT jint JNICALL |
| FileChannelImpl_unmap0(JNIEnv *env, jobject this, |
| jlong address, jlong len) |
| { |
| void *a = (void *)jlong_to_ptr(address); |
| return handle(env, |
| munmap(a, (size_t)len), |
| "Unmap failed"); |
| } |
| |
| |
| JNIEXPORT jlong JNICALL |
| FileChannelImpl_position0(JNIEnv *env, jobject this, |
| jobject fdo, jlong offset) |
| { |
| jint fd = fdval(env, fdo); |
| jlong result = 0; |
| |
| if (offset < 0) { |
| result = lseek64(fd, 0, SEEK_CUR); |
| } else { |
| result = lseek64(fd, offset, SEEK_SET); |
| } |
| return handle(env, result, "Position failed"); |
| } |
| |
| |
| JNIEXPORT void JNICALL |
| FileChannelImpl_close0(JNIEnv *env, jobject this, jobject fdo) |
| { |
| jint fd = fdval(env, fdo); |
| if (fd != -1) { |
| jlong result = close(fd); |
| if (result < 0) { |
| JNU_ThrowIOExceptionWithLastError(env, "Close failed"); |
| } |
| } |
| } |
| |
| JNIEXPORT jlong JNICALL |
| FileChannelImpl_transferTo0(JNIEnv *env, jobject this, |
| jobject srcFDO, |
| jlong position, jlong count, |
| jobject dstFDO) |
| { |
| jint srcFD = fdval(env, srcFDO); |
| jint dstFD = fdval(env, dstFDO); |
| |
| #if defined(__linux__) |
| off64_t offset = (off64_t)position; |
| jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count); |
| if (n < 0) { |
| if (errno == EAGAIN) |
| return IOS_UNAVAILABLE; |
| if ((errno == EINVAL) && ((ssize_t)count >= 0)) |
| return IOS_UNSUPPORTED_CASE; |
| if (errno == EINTR) { |
| return IOS_INTERRUPTED; |
| } |
| JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); |
| return IOS_THROWN; |
| } |
| return n; |
| #elif defined (__solaris__) |
| sendfilevec64_t sfv; |
| size_t numBytes = 0; |
| jlong result; |
| |
| sfv.sfv_fd = srcFD; |
| sfv.sfv_flag = 0; |
| sfv.sfv_off = (off64_t)position; |
| sfv.sfv_len = count; |
| |
| result = sendfilev64(dstFD, &sfv, 1, &numBytes); |
| |
| /* Solaris sendfilev() will return -1 even if some bytes have been |
| * transferred, so we check numBytes first. |
| */ |
| if (numBytes > 0) |
| return numBytes; |
| if (result < 0) { |
| if (errno == EAGAIN) |
| return IOS_UNAVAILABLE; |
| if (errno == EOPNOTSUPP) |
| return IOS_UNSUPPORTED_CASE; |
| if ((errno == EINVAL) && ((ssize_t)count >= 0)) |
| return IOS_UNSUPPORTED_CASE; |
| if (errno == EINTR) |
| return IOS_INTERRUPTED; |
| JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); |
| return IOS_THROWN; |
| } |
| return result; |
| #elif defined(__APPLE__) |
| off_t numBytes; |
| int result; |
| |
| numBytes = count; |
| |
| result = sendfile(srcFD, dstFD, position, &numBytes, NULL, 0); |
| |
| if (numBytes > 0) |
| return numBytes; |
| |
| if (result == -1) { |
| if (errno == EAGAIN) |
| return IOS_UNAVAILABLE; |
| if (errno == EOPNOTSUPP || errno == ENOTSOCK || errno == ENOTCONN) |
| return IOS_UNSUPPORTED_CASE; |
| if ((errno == EINVAL) && ((ssize_t)count >= 0)) |
| return IOS_UNSUPPORTED_CASE; |
| if (errno == EINTR) |
| return IOS_INTERRUPTED; |
| JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); |
| return IOS_THROWN; |
| } |
| |
| return result; |
| |
| #elif defined(_AIX) |
| jlong max = (jlong)java_lang_Integer_MAX_VALUE; |
| struct sf_parms sf_iobuf; |
| jlong result; |
| |
| if (position > max) |
| return IOS_UNSUPPORTED_CASE; |
| |
| if (count > max) |
| count = max; |
| |
| memset(&sf_iobuf, 0, sizeof(sf_iobuf)); |
| sf_iobuf.file_descriptor = srcFD; |
| sf_iobuf.file_offset = (off_t)position; |
| sf_iobuf.file_bytes = count; |
| |
| result = send_file(&dstFD, &sf_iobuf, SF_SYNC_CACHE); |
| |
| /* AIX send_file() will return 0 when this operation complete successfully, |
| * return 1 when partial bytes transfered and return -1 when an error has |
| * Occured. |
| */ |
| if (result == -1) { |
| if (errno == EWOULDBLOCK) |
| return IOS_UNAVAILABLE; |
| if ((errno == EINVAL) && ((ssize_t)count >= 0)) |
| return IOS_UNSUPPORTED_CASE; |
| if (errno == EINTR) |
| return IOS_INTERRUPTED; |
| if (errno == ENOTSOCK) |
| return IOS_UNSUPPORTED; |
| JNU_ThrowIOExceptionWithLastError(env, "Transfer failed"); |
| return IOS_THROWN; |
| } |
| |
| if (sf_iobuf.bytes_sent > 0) |
| return (jlong)sf_iobuf.bytes_sent; |
| |
| return IOS_UNSUPPORTED_CASE; |
| #else |
| return IOS_UNSUPPORTED_CASE; |
| #endif |
| } |
| |
| static JNINativeMethod gMethods[] = { |
| NATIVE_METHOD(FileChannelImpl, initIDs, "()J"), |
| NATIVE_METHOD(FileChannelImpl, map0, "(IJJ)J"), |
| NATIVE_METHOD(FileChannelImpl, unmap0, "(JJ)I"), |
| NATIVE_METHOD(FileChannelImpl, position0, "(Ljava/io/FileDescriptor;J)J"), |
| NATIVE_METHOD(FileChannelImpl, transferTo0, "(Ljava/io/FileDescriptor;JJLjava/io/FileDescriptor;)J"), |
| }; |
| |
| void register_sun_nio_ch_FileChannelImpl(JNIEnv* env) { |
| jniRegisterNativeMethods(env, "sun/nio/ch/FileChannelImpl", gMethods, NELEM(gMethods)); |
| } |