/* * 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 "JNIHelp.h" #define NATIVE_METHOD(className, functionName, signature) \ { #functionName, signature, (void*)(className ## _ ## functionName) } #if defined(__linux__) || defined(__solaris__) #include <sys/sendfile.h> #elif defined(_ALLBSD_SOURCE) #include <sys/types.h> #include <sys/socket.h> #include <sys/uio.h> #define lseek64 lseek #define mmap64 mmap #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, jint srcFD, jlong position, jlong count, jint dstFD) { #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; #ifdef __APPLE__ result = sendfile(srcFD, dstFD, position, &numBytes, NULL, 0); #endif 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; #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, "(IJJI)J"), }; void register_sun_nio_ch_FileChannelImpl(JNIEnv* env) { jniRegisterNativeMethods(env, "sun/nio/ch/FileChannelImpl", gMethods, NELEM(gMethods)); }