1 /*
2 * Copyright (c) 2000, 2019, 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 <sys/mman.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #if defined(__linux__) || defined(__solaris__)
33 #include <sys/sendfile.h>
34 #elif defined(_AIX)
35 #include <string.h>
36 #include <sys/socket.h>
37 #elif defined(_ALLBSD_SOURCE)
38 #include <sys/socket.h>
39 #include <sys/uio.h>
40 #define lseek64 lseek
41 #define mmap64 mmap
42 #endif
43
44 #include "jni.h"
45 #include "jni_util.h"
46 #include "jlong.h"
47 #include "nio.h"
48 #include "nio_util.h"
49 #include "sun_nio_ch_FileChannelImpl.h"
50 #include "java_lang_Integer.h"
51 #include <assert.h>
52
53 static jfieldID chan_fd; /* jobject 'fd' in sun.nio.ch.FileChannelImpl */
54
55 JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_initIDs(JNIEnv * env,jclass clazz)56 Java_sun_nio_ch_FileChannelImpl_initIDs(JNIEnv *env, jclass clazz)
57 {
58 jlong pageSize = sysconf(_SC_PAGESIZE);
59 chan_fd = (*env)->GetFieldID(env, clazz, "fd", "Ljava/io/FileDescriptor;");
60 return pageSize;
61 }
62
63 static jlong
handle(JNIEnv * env,jlong rv,char * msg)64 handle(JNIEnv *env, jlong rv, char *msg)
65 {
66 if (rv >= 0)
67 return rv;
68 if (errno == EINTR)
69 return IOS_INTERRUPTED;
70 JNU_ThrowIOExceptionWithLastError(env, msg);
71 return IOS_THROWN;
72 }
73
74
75 JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_map0(JNIEnv * env,jobject this,jint prot,jlong off,jlong len,jboolean map_sync)76 Java_sun_nio_ch_FileChannelImpl_map0(JNIEnv *env, jobject this,
77 jint prot, jlong off, jlong len, jboolean map_sync)
78 {
79 void *mapAddress = 0;
80 jobject fdo = (*env)->GetObjectField(env, this, chan_fd);
81 jint fd = fdval(env, fdo);
82 int protections = 0;
83 int flags = 0;
84
85 // should never be called with map_sync and prot == PRIVATE
86 assert((prot != sun_nio_ch_FileChannelImpl_MAP_PV) || !map_sync);
87
88 if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) {
89 protections = PROT_READ;
90 flags = MAP_SHARED;
91 } else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) {
92 protections = PROT_WRITE | PROT_READ;
93 flags = MAP_SHARED;
94 } else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) {
95 protections = PROT_WRITE | PROT_READ;
96 flags = MAP_PRIVATE;
97 }
98
99 // if MAP_SYNC and MAP_SHARED_VALIDATE are not defined then it is
100 // best to define them here. This ensures the code compiles on old
101 // OS releases which do not provide the relevant headers. If run
102 // on the same machine then it will work if the kernel contains
103 // the necessary support otherwise mmap should fail with an
104 // invalid argument error
105
106 #ifndef MAP_SYNC
107 #define MAP_SYNC 0x80000
108 #endif
109 #ifndef MAP_SHARED_VALIDATE
110 #define MAP_SHARED_VALIDATE 0x03
111 #endif
112
113 if (map_sync) {
114 // ensure
115 // 1) this is Linux on AArch64 or x86_64
116 // 2) the mmap APIs are available/ at compile time
117 #if !defined(LINUX) || ! (defined(aarch64) || (defined(amd64) && defined(_LP64)))
118 // TODO - implement for solaris/AIX/BSD/WINDOWS and for 32 bit
119 JNU_ThrowInternalError(env, "should never call map on platform where MAP_SYNC is unimplemented");
120 return IOS_THROWN;
121 #else
122 flags |= MAP_SYNC | MAP_SHARED_VALIDATE;
123 #endif
124 }
125
126 mapAddress = mmap64(
127 0, /* Let OS decide location */
128 len, /* Number of bytes to map */
129 protections, /* File permissions */
130 flags, /* Changes are shared */
131 fd, /* File descriptor of mapped file */
132 off); /* Offset into file */
133
134 if (mapAddress == MAP_FAILED) {
135 if (map_sync && errno == ENOTSUP) {
136 JNU_ThrowIOExceptionWithLastError(env, "map with mode MAP_SYNC unsupported");
137 return IOS_THROWN;
138 }
139
140 if (errno == ENOMEM) {
141 JNU_ThrowOutOfMemoryError(env, "Map failed");
142 return IOS_THROWN;
143 }
144 return handle(env, -1, "Map failed");
145 }
146
147 return ((jlong) (unsigned long) mapAddress);
148 }
149
150
151 JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileChannelImpl_unmap0(JNIEnv * env,jobject this,jlong address,jlong len)152 Java_sun_nio_ch_FileChannelImpl_unmap0(JNIEnv *env, jobject this,
153 jlong address, jlong len)
154 {
155 void *a = (void *)jlong_to_ptr(address);
156 return handle(env,
157 munmap(a, (size_t)len),
158 "Unmap failed");
159 }
160
161 JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv * env,jobject this,jobject srcFDO,jlong position,jlong count,jobject dstFDO)162 Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
163 jobject srcFDO,
164 jlong position, jlong count,
165 jobject dstFDO)
166 {
167 jint srcFD = fdval(env, srcFDO);
168 jint dstFD = fdval(env, dstFDO);
169
170 #if defined(__linux__)
171 off64_t offset = (off64_t)position;
172 jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count);
173 if (n < 0) {
174 if (errno == EAGAIN)
175 return IOS_UNAVAILABLE;
176 if ((errno == EINVAL) && ((ssize_t)count >= 0))
177 return IOS_UNSUPPORTED_CASE;
178 if (errno == EINTR) {
179 return IOS_INTERRUPTED;
180 }
181 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
182 return IOS_THROWN;
183 }
184 return n;
185 #elif defined (__solaris__)
186 sendfilevec64_t sfv;
187 size_t numBytes = 0;
188 jlong result;
189
190 sfv.sfv_fd = srcFD;
191 sfv.sfv_flag = 0;
192 sfv.sfv_off = (off64_t)position;
193 sfv.sfv_len = count;
194
195 result = sendfilev64(dstFD, &sfv, 1, &numBytes);
196
197 /* Solaris sendfilev() will return -1 even if some bytes have been
198 * transferred, so we check numBytes first.
199 */
200 if (numBytes > 0)
201 return numBytes;
202 if (result < 0) {
203 if (errno == EAGAIN)
204 return IOS_UNAVAILABLE;
205 if (errno == EOPNOTSUPP)
206 return IOS_UNSUPPORTED_CASE;
207 if ((errno == EINVAL) && ((ssize_t)count >= 0))
208 return IOS_UNSUPPORTED_CASE;
209 if (errno == EINTR)
210 return IOS_INTERRUPTED;
211 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
212 return IOS_THROWN;
213 }
214 return result;
215 #elif defined(__APPLE__)
216 off_t numBytes;
217 int result;
218
219 numBytes = count;
220
221 result = sendfile(srcFD, dstFD, position, &numBytes, NULL, 0);
222
223 if (numBytes > 0)
224 return numBytes;
225
226 if (result == -1) {
227 if (errno == EAGAIN)
228 return IOS_UNAVAILABLE;
229 if (errno == EOPNOTSUPP || errno == ENOTSOCK || errno == ENOTCONN)
230 return IOS_UNSUPPORTED_CASE;
231 if ((errno == EINVAL) && ((ssize_t)count >= 0))
232 return IOS_UNSUPPORTED_CASE;
233 if (errno == EINTR)
234 return IOS_INTERRUPTED;
235 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
236 return IOS_THROWN;
237 }
238
239 return result;
240
241 #elif defined(_AIX)
242 jlong max = (jlong)java_lang_Integer_MAX_VALUE;
243 struct sf_parms sf_iobuf;
244 jlong result;
245
246 if (position > max)
247 return IOS_UNSUPPORTED_CASE;
248
249 if (count > max)
250 count = max;
251
252 memset(&sf_iobuf, 0, sizeof(sf_iobuf));
253 sf_iobuf.file_descriptor = srcFD;
254 sf_iobuf.file_offset = (off_t)position;
255 sf_iobuf.file_bytes = count;
256
257 result = send_file(&dstFD, &sf_iobuf, SF_SYNC_CACHE);
258
259 /* AIX send_file() will return 0 when this operation complete successfully,
260 * return 1 when partial bytes transfered and return -1 when an error has
261 * Occured.
262 */
263 if (result == -1) {
264 if (errno == EWOULDBLOCK)
265 return IOS_UNAVAILABLE;
266 if ((errno == EINVAL) && ((ssize_t)count >= 0))
267 return IOS_UNSUPPORTED_CASE;
268 if (errno == EINTR)
269 return IOS_INTERRUPTED;
270 if (errno == ENOTSOCK)
271 return IOS_UNSUPPORTED;
272 JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
273 return IOS_THROWN;
274 }
275
276 if (sf_iobuf.bytes_sent > 0)
277 return (jlong)sf_iobuf.bytes_sent;
278
279 return IOS_UNSUPPORTED_CASE;
280 #else
281 return IOS_UNSUPPORTED_CASE;
282 #endif
283 }
284
285