/* * Copyright (c) 2000, 2017, 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 #include #include #include "sun_nio_ch_FileChannelImpl.h" #include "java_lang_Integer.h" #include "nio.h" #include "nio_util.h" #include #if defined(__linux__) || defined(__solaris__) #include #elif defined(_AIX) #include #elif defined(_ALLBSD_SOURCE) #include #include #include #define lseek64 lseek #define mmap64 mmap #endif static jfieldID chan_fd; /* jobject 'fd' in sun.nio.ch.FileChannelImpl */ JNIEXPORT jlong JNICALL Java_sun_nio_ch_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 Java_sun_nio_ch_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 Java_sun_nio_ch_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 Java_sun_nio_ch_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 Java_sun_nio_ch_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 Java_sun_nio_ch_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 }