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