/* * Copyright (c) 2000, 2019, 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 #include #include #include #include #if defined(__linux__) #include #elif defined(_AIX) #include #include #elif defined(_ALLBSD_SOURCE) #include #include #define lseek64 lseek #define mmap64 mmap #endif #include "jni.h" #include "jni_util.h" #include "jlong.h" #include "nio.h" #include "nio_util.h" #include "sun_nio_ch_FileChannelImpl.h" #include "java_lang_Integer.h" #include 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, jboolean map_sync) { void *mapAddress = 0; jobject fdo = (*env)->GetObjectField(env, this, chan_fd); jint fd = fdval(env, fdo); int protections = 0; int flags = 0; // should never be called with map_sync and prot == PRIVATE assert((prot != sun_nio_ch_FileChannelImpl_MAP_PV) || !map_sync); 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; } // if MAP_SYNC and MAP_SHARED_VALIDATE are not defined then it is // best to define them here. This ensures the code compiles on old // OS releases which do not provide the relevant headers. If run // on the same machine then it will work if the kernel contains // the necessary support otherwise mmap should fail with an // invalid argument error #ifndef MAP_SYNC #define MAP_SYNC 0x80000 #endif #ifndef MAP_SHARED_VALIDATE #define MAP_SHARED_VALIDATE 0x03 #endif if (map_sync) { // ensure // 1) this is Linux on AArch64, x86_64, or PPC64 LE // 2) the mmap APIs are available at compile time #if !defined(LINUX) || ! (defined(aarch64) || (defined(amd64) && defined(_LP64)) || defined(ppc64le)) // TODO - implement for solaris/AIX/BSD/WINDOWS and for 32 bit JNU_ThrowInternalError(env, "should never call map on platform where MAP_SYNC is unimplemented"); return IOS_THROWN; #else flags |= MAP_SYNC | MAP_SHARED_VALIDATE; #endif } 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 (map_sync && errno == ENOTSUP) { JNU_ThrowIOExceptionWithLastError(env, "map with mode MAP_SYNC unsupported"); return IOS_THROWN; } 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_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(__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 }