1 /* gnu_java_nio_channels_FileChannelImpl.c -
2 Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 /* do not move; needed here because of some macro definitions */
39 #include <config.h>
40
41 #include <stdlib.h>
42 #include <errno.h>
43
44 #include <jni.h>
45 #include <jcl.h>
46
47 #include "target_native.h"
48 #ifndef WITHOUT_FILESYSTEM
49 #include "target_native_file.h"
50 #endif
51 #include "target_native_math_int.h"
52
53 #include "gnu_java_nio_channels_FileChannelImpl.h"
54
55 #ifdef HAVE_FCNTL_H
56 #include <fcntl.h>
57 #endif /* HAVE_FCNTL_H */
58
59 #ifdef HAVE_SYS_MMAN_H
60 #include <sys/mman.h>
61 #endif /* HAVE_SYS_MMAN_H */
62
63 /* These values must be kept in sync with FileChannelImpl.java. */
64 #define FILECHANNELIMPL_READ 1
65 #define FILECHANNELIMPL_WRITE 2
66 #define FILECHANNELIMPL_APPEND 4
67
68 /* These values must be kept in sync with FileChannelImpl.java. */
69 /* #define FILECHANNELIMPL_FILESEEK_SET 0 */
70 /* #define FILECHANNELIMPL_FILESEEK_CUR 1 */
71 /* #define FILECHANNELIMPL_FILESEEK_END 2 */
72
73 #define FILECHANNELIMPL_FILEOPEN_FLAG_READ 1
74 #define FILECHANNELIMPL_FILEOPEN_FLAG_WRITE 2
75 #define FILECHANNELIMPL_FILEOPEN_FLAG_APPEND 4
76 #define FILECHANNELIMPL_FILEOPEN_FLAG_EXCL 8
77 #define FILECHANNELIMPL_FILEOPEN_FLAG_SYNC 16
78 #define FILECHANNELIMPL_FILEOPEN_FLAG_DSYNC 32
79
80 #define IO_EXCEPTION "java/io/IOException"
81
82 /* FIXME: This can't be right. Need converter macros. */
83 #define CONVERT_JLONG_TO_INT(x) TARGET_NATIVE_MATH_INT_INT64_TO_INT32(x)
84 #define CONVERT_INT_TO_JLONG(x) TARGET_NATIVE_MATH_INT_INT32_TO_INT64(x)
85
86 /* FIXME: This can't be right. Need converter macros. */
87 #define CONVERT_JLONG_TO_OFF_T(x) TARGET_NATIVE_MATH_INT_INT64_TO_INT32(x)
88 #define CONVERT_OFF_T_TO_JLONG(x) TARGET_NATIVE_MATH_INT_INT32_TO_INT64(x)
89
90 /* FIXME: This can't be right. Need converter macros */
91 #define CONVERT_JINT_TO_INT(x) ((int)(x & 0xFFFFFFFF))
92 #define CONVERT_INT_TO_JINT(x) ((int)(x & 0xFFFFFFFF))
93
94 /* FIXME: This can't be right. Need converter macros. */
95 #define CONVERT_SSIZE_T_TO_JINT(x) ((jint)(x & 0xFFFFFFFF))
96 #define CONVERT_JINT_TO_SSIZE_T(x) (x)
97
98 /* Align a value up or down to a multiple of the pagesize. */
99 #define ALIGN_DOWN(p,s) ((p) - ((p) % (s)))
100 #define ALIGN_UP(p,s) ((p) + ((s) - ((p) % (s))))
101
102 /* cached fieldID of gnu.java.nio.channels.FileChannelImpl.fd */
103 static jfieldID native_fd_fieldID;
104
105 static jint
get_native_fd(JNIEnv * env,jobject obj)106 get_native_fd (JNIEnv * env, jobject obj)
107 {
108 return (*env)->GetIntField (env, obj, native_fd_fieldID);
109 }
110
111 /*
112 * Library initialization routine. Called as part of java.io.FileDescriptor
113 * static initialization.
114 */
115 JNIEXPORT void JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_init(JNIEnv * env,jclass clazz)116 Java_gnu_java_nio_channels_FileChannelImpl_init (JNIEnv * env,
117 jclass clazz
118 __attribute__ ((__unused__)))
119 {
120 jclass clazz_fc;
121 jfieldID field;
122
123 /* Initialize native_fd_fieldID so we only compute it once! */
124 clazz_fc = (*env)->FindClass (env, "gnu/java/nio/channels/FileChannelImpl");
125 if (!clazz_fc)
126 {
127 JCL_ThrowException (env, IO_EXCEPTION, "Internal error");
128 return;
129 }
130
131 field = (*env)->GetFieldID (env, clazz_fc, "fd", "I");
132 if (!field)
133 {
134 JCL_ThrowException (env, IO_EXCEPTION, "Internal error");
135 return;
136 }
137
138 native_fd_fieldID = field;
139 }
140
141 /*
142 * Open the specified file and return a native file descriptor
143 */
144 JNIEXPORT jint JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_open(JNIEnv * env,jobject obj,jstring name,jint mode)145 Java_gnu_java_nio_channels_FileChannelImpl_open (JNIEnv * env,
146 jobject obj
147 __attribute__ ((__unused__)),
148 jstring name, jint mode)
149 {
150 const char *filename;
151 int flags;
152 int permissions;
153 int native_fd;
154 int result;
155
156 filename = JCL_jstring_to_cstring (env, name);
157 if (filename == NULL)
158 return (-1); /* Exception will already have been thrown */
159
160 /* get file/permission flags for open() */
161 if ((mode & FILECHANNELIMPL_FILEOPEN_FLAG_READ)
162 && (mode & FILECHANNELIMPL_FILEOPEN_FLAG_WRITE))
163 {
164 /* read/write */
165 flags =
166 TARGET_NATIVE_FILE_FILEFLAG_CREATE |
167 TARGET_NATIVE_FILE_FILEFLAG_READWRITE;
168 permissions = TARGET_NATIVE_FILE_FILEPERMISSION_NORMAL;
169 }
170 else if ((mode & FILECHANNELIMPL_FILEOPEN_FLAG_READ))
171 {
172 /* read */
173 flags = TARGET_NATIVE_FILE_FILEFLAG_READ;
174 permissions = TARGET_NATIVE_FILE_FILEPERMISSION_NORMAL;
175 }
176 else
177 {
178 /* write */
179 flags =
180 TARGET_NATIVE_FILE_FILEFLAG_CREATE |
181 TARGET_NATIVE_FILE_FILEFLAG_WRITE;
182 if ((mode & FILECHANNELIMPL_FILEOPEN_FLAG_APPEND))
183 {
184 flags |= TARGET_NATIVE_FILE_FILEFLAG_APPEND;
185 }
186 else
187 {
188 flags |= TARGET_NATIVE_FILE_FILEFLAG_TRUNCATE;
189 }
190 permissions = TARGET_NATIVE_FILE_FILEPERMISSION_NORMAL;
191 }
192
193 if ((mode & FILECHANNELIMPL_FILEOPEN_FLAG_SYNC))
194 {
195 flags |= TARGET_NATIVE_FILE_FILEFLAG_SYNC;
196 }
197
198 if ((mode & FILECHANNELIMPL_FILEOPEN_FLAG_DSYNC))
199 {
200 flags |= TARGET_NATIVE_FILE_FILEFLAG_DSYNC;
201 }
202 #ifdef O_BINARY
203 flags |= TARGET_NATIVE_FILE_FILEFLAG_BINARY;
204 #endif
205
206 TARGET_NATIVE_FILE_OPEN (filename, native_fd, flags, permissions, result);
207
208 if (result != TARGET_NATIVE_OK)
209 {
210 char message[256]; /* Fixed size we don't need to malloc. */
211 char *error_string = TARGET_NATIVE_LAST_ERROR_STRING ();
212
213 snprintf(message, 256, "%s: %s", error_string, filename);
214 /* We are only allowed to throw FileNotFoundException. */
215 JCL_ThrowException (env,
216 "java/io/FileNotFoundException",
217 message);
218 JCL_free_cstring (env, name, filename);
219 return TARGET_NATIVE_MATH_INT_INT64_CONST_MINUS_1;
220 }
221
222 JCL_free_cstring (env, name, filename);
223 return native_fd;
224 }
225
226 /*
227 * Closes the specified file descriptor and return status code.
228 * Exception on error
229 */
230 JNIEXPORT void JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_implCloseChannel(JNIEnv * env,jobject obj)231 Java_gnu_java_nio_channels_FileChannelImpl_implCloseChannel (JNIEnv * env,
232 jobject obj)
233 {
234 int native_fd;
235 int result;
236
237 native_fd = get_native_fd (env, obj);
238
239 do
240 {
241 TARGET_NATIVE_FILE_CLOSE (native_fd, result);
242 if (result != TARGET_NATIVE_OK
243 && (TARGET_NATIVE_LAST_ERROR ()
244 != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
245 {
246 JCL_ThrowException (env, IO_EXCEPTION,
247 TARGET_NATIVE_LAST_ERROR_STRING ());
248 return;
249 }
250 }
251 while (result != TARGET_NATIVE_OK);
252 }
253
254 /*
255 * Return number of bytes that can be read from the file w/o blocking.
256 * Exception on error
257 */
258 JNIEXPORT jint JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_available(JNIEnv * env,jobject obj)259 Java_gnu_java_nio_channels_FileChannelImpl_available (JNIEnv * env,
260 jobject obj)
261 {
262 int native_fd;
263 jlong bytes_available;
264 int result;
265
266 native_fd = get_native_fd (env, obj);
267
268 do
269 {
270 TARGET_NATIVE_FILE_AVAILABLE (native_fd, bytes_available, result);
271 if (result != TARGET_NATIVE_OK
272 && (TARGET_NATIVE_LAST_ERROR ()
273 != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
274 {
275 JCL_ThrowException (env, IO_EXCEPTION,
276 TARGET_NATIVE_LAST_ERROR_STRING ());
277 return 0;
278 }
279 }
280 while (result != TARGET_NATIVE_OK);
281
282 /* FIXME NYI ??? why only jint and not jlong? */
283 return TARGET_NATIVE_MATH_INT_INT64_TO_INT32 (bytes_available);
284 }
285
286 JNIEXPORT jlong JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_size(JNIEnv * env,jobject obj)287 Java_gnu_java_nio_channels_FileChannelImpl_size (JNIEnv * env, jobject obj)
288 {
289 int native_fd;
290 jlong file_size;
291 int result;
292
293 native_fd = get_native_fd (env, obj);
294
295 TARGET_NATIVE_FILE_SIZE (native_fd, file_size, result);
296 if (result != TARGET_NATIVE_OK)
297 {
298 JCL_ThrowException (env, IO_EXCEPTION,
299 TARGET_NATIVE_LAST_ERROR_STRING ());
300 return TARGET_NATIVE_MATH_INT_INT64_CONST_MINUS_1;
301 }
302
303 return file_size;
304 }
305
306 /*
307 * Return the current position of the file pointer
308 * Exception on error
309 */
310 JNIEXPORT jlong JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_implPosition(JNIEnv * env,jobject obj)311 Java_gnu_java_nio_channels_FileChannelImpl_implPosition (JNIEnv * env,
312 jobject obj)
313 {
314 int native_fd;
315 jlong current_offset;
316 int result;
317
318 native_fd = get_native_fd (env, obj);
319
320 TARGET_NATIVE_FILE_TELL (native_fd, current_offset, result);
321 if (result != TARGET_NATIVE_OK)
322 {
323 JCL_ThrowException (env, IO_EXCEPTION,
324 TARGET_NATIVE_LAST_ERROR_STRING ());
325 return TARGET_NATIVE_MATH_INT_INT64_CONST_MINUS_1;
326 }
327
328 return current_offset;
329 }
330
331 /*
332 * Wrapper around lseek call. Return new file position
333 * Exception on error
334 */
335 JNIEXPORT void JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_seek(JNIEnv * env,jobject obj,jlong offset)336 Java_gnu_java_nio_channels_FileChannelImpl_seek (JNIEnv * env, jobject obj,
337 jlong offset)
338 {
339 int native_fd;
340 jlong new_offset;
341 int result;
342
343 native_fd = get_native_fd (env, obj);
344
345 #if 0
346 /* Should there be such an exception? All native layer macros should
347 be accepting 64bit-values if needed. It some target is not able
348 to handle such values it should simply operate with 32bit-values
349 and convert 64bit-values appriopated. In this case I assume
350 problems should not occurre: if some specific target is not able
351 to handle 64bit-values the system is limited to 32bit at all, thus
352 the application can not do a seek() or something else beyond the
353 32bit limit. It this true?
354 */
355
356 /* FIXME: What do we do if offset > the max value of off_t on this 32bit
357 * system? How do we detect that and what do we do? */
358 if (CONVERT_OFF_T_TO_JLONG (native_offset) != offset)
359 {
360 JCL_ThrowException (env, IO_EXCEPTION,
361 "Cannot represent position correctly on this system");
362 }
363 #endif /* 0 */
364
365 result = TARGET_NATIVE_ERROR;
366 new_offset = TARGET_NATIVE_MATH_INT_INT64_CONST_MINUS_1;
367 TARGET_NATIVE_FILE_SEEK_BEGIN (native_fd, offset, new_offset, result);
368
369 if (result != TARGET_NATIVE_OK)
370 {
371 JCL_ThrowException (env, IO_EXCEPTION,
372 TARGET_NATIVE_LAST_ERROR_STRING ());
373 }
374 }
375
376 /*
377 * Set the length of the file
378 * Exception on error
379 */
380 JNIEXPORT void JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_implTruncate(JNIEnv * env,jobject obj,jlong len)381 Java_gnu_java_nio_channels_FileChannelImpl_implTruncate (JNIEnv * env,
382 jobject obj,
383 jlong len)
384 {
385 int native_fd;
386 jlong file_size;
387 int bytes_written;
388 jlong save_offset, new_offset;
389 char data;
390 int result;
391
392 native_fd = get_native_fd (env, obj);
393
394 #if 0
395 /* Should there be such an exception? All native layer macros should
396 be accepting 64bit-values if needed. It some target is not able
397 to handle such values it should simply operate with 32bit-values
398 and convert 64bit-values appriopated. In this case I assume
399 problems should not occurre: if some specific target is not able
400 to handle 64bit-values the system is limited to 32bit at all, thus
401 the application can not do a seek() or something else beyond the
402 32bit limit. It this true?
403 */
404
405 /* FIXME: What do we do if len > the max value of off_t on this 32bit
406 * system? How do we detect that and what do we do? */
407 if (CONVERT_OFF_T_TO_JLONG (native_len) != len)
408 {
409 JCL_ThrowException (env, IO_EXCEPTION,
410 "Cannot represent position correctly on this system");
411 return;
412 }
413 #endif /* 0 */
414
415 /* get file size */
416 TARGET_NATIVE_FILE_SIZE (native_fd, file_size, result);
417 if (result != TARGET_NATIVE_OK)
418 {
419 JCL_ThrowException (env, IO_EXCEPTION,
420 TARGET_NATIVE_LAST_ERROR_STRING ());
421 return;
422 }
423
424 /* Save off current position */
425 TARGET_NATIVE_FILE_TELL (native_fd, save_offset, result);
426 if (result != TARGET_NATIVE_OK)
427 {
428 JCL_ThrowException (env, IO_EXCEPTION,
429 TARGET_NATIVE_LAST_ERROR_STRING ());
430 return;
431 }
432
433 if (TARGET_NATIVE_MATH_INT_INT64_LT (file_size, len))
434 {
435 /* File is too short -- seek to one byte short of where we want,
436 * then write a byte */
437
438 /* move to position n-1 */
439 TARGET_NATIVE_FILE_SEEK_BEGIN (native_fd,
440 TARGET_NATIVE_MATH_INT_INT64_SUB (len,
441 1),
442 new_offset, result);
443 if (result != TARGET_NATIVE_OK)
444 {
445 JCL_ThrowException (env, IO_EXCEPTION,
446 TARGET_NATIVE_LAST_ERROR_STRING ());
447 return;
448 }
449
450 /* write a byte
451 Note: This will fail if we somehow get here in read only mode
452 * That shouldn't happen */
453 data = '\0';
454 TARGET_NATIVE_FILE_WRITE (native_fd, &data, 1, bytes_written, result);
455 if (result != TARGET_NATIVE_OK)
456 {
457 JCL_ThrowException (env, IO_EXCEPTION,
458 TARGET_NATIVE_LAST_ERROR_STRING ());
459 return;
460 }
461
462 /* Reposition file pointer to where we started if not beyond new len. */
463 if (TARGET_NATIVE_MATH_INT_INT64_LT (save_offset, len))
464 {
465 TARGET_NATIVE_FILE_SEEK_BEGIN (native_fd, save_offset,
466 new_offset, result);
467 if (result != TARGET_NATIVE_OK)
468 {
469 JCL_ThrowException (env, IO_EXCEPTION,
470 TARGET_NATIVE_LAST_ERROR_STRING ());
471 return;
472 }
473 }
474 }
475 else if (TARGET_NATIVE_MATH_INT_INT64_GT (file_size, len))
476 {
477 /* File is too long - use ftruncate if available */
478 #ifdef HAVE_FTRUNCATE
479 TARGET_NATIVE_FILE_TRUNCATE (native_fd, len, result);
480 if (result != TARGET_NATIVE_OK)
481 {
482 JCL_ThrowException (env, IO_EXCEPTION,
483 TARGET_NATIVE_LAST_ERROR_STRING ());
484 return;
485 }
486 #else /* HAVE_FTRUNCATE */
487 /* FIXME: Probably operation isn't supported, but this exception
488 * is too harsh as it will probably crash the program without need
489 JCL_ThrowException(env, "java/lang/UnsupportedOperationException",
490 "not implemented - can't shorten files on this platform");
491 */
492 JCL_ThrowException (env, IO_EXCEPTION, "Unable to shorten file length");
493 #endif /* HAVE_FTRUNCATE */
494
495 /* Reposition file pointer when it now is beyond the end of file. */
496 if (TARGET_NATIVE_MATH_INT_INT64_GT (save_offset, len))
497 {
498 TARGET_NATIVE_FILE_SEEK_BEGIN (native_fd, len, new_offset, result);
499 if (result != TARGET_NATIVE_OK)
500 {
501 JCL_ThrowException (env, IO_EXCEPTION,
502 TARGET_NATIVE_LAST_ERROR_STRING ());
503 return;
504 }
505 }
506 }
507 }
508
509 JNIEXPORT jobject JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_mapImpl(JNIEnv * env,jobject obj,jchar mode,jlong position,jint size)510 Java_gnu_java_nio_channels_FileChannelImpl_mapImpl (JNIEnv *env, jobject obj,
511 jchar mode, jlong position, jint size)
512 {
513 #ifdef HAVE_MMAP
514 jclass MappedByteBufferImpl_class;
515 jmethodID MappedByteBufferImpl_init = NULL;
516 jobject Pointer_instance;
517 volatile jobject buffer;
518 long pagesize;
519 int prot, flags;
520 int fd;
521 void *p;
522 void *address;
523
524 /* FIXME: should we just assume we're on an OS modern enough to
525 have 'sysconf'? And not check for 'getpagesize'? */
526 #if defined(HAVE_GETPAGESIZE)
527 pagesize = getpagesize ();
528 #elif defined(HAVE_SYSCONF)
529 pagesize = sysconf (_SC_PAGESIZE);
530 #else
531 JCL_ThrowException (env, IO_EXCEPTION,
532 "can't determine memory page size");
533 return NULL;
534 #endif /* HAVE_GETPAGESIZE/HAVE_SYSCONF */
535
536 if ((*env)->ExceptionOccurred (env))
537 {
538 return NULL;
539 }
540
541 prot = PROT_READ;
542 if (mode == '+')
543 prot |= PROT_WRITE;
544 flags = (mode == 'c' ? MAP_PRIVATE : MAP_SHARED);
545 fd = get_native_fd (env, obj);
546 p = mmap (NULL, (size_t) ALIGN_UP (size, pagesize), prot, flags,
547 fd, ALIGN_DOWN (position, pagesize));
548 if (p == MAP_FAILED)
549 {
550 JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
551 return NULL;
552 }
553
554 /* Unalign the mapped value back up, since we aligned offset
555 down to a multiple of the page size. */
556 address = (void *) ((char *) p + (position % pagesize));
557
558 Pointer_instance = JCL_NewRawDataObject(env, address);
559
560 MappedByteBufferImpl_class = (*env)->FindClass (env,
561 "java/nio/MappedByteBufferImpl");
562 if (MappedByteBufferImpl_class != NULL)
563 {
564 MappedByteBufferImpl_init =
565 (*env)->GetMethodID (env, MappedByteBufferImpl_class,
566 "<init>", "(Lgnu/classpath/Pointer;IZ)V");
567 }
568
569 if ((*env)->ExceptionOccurred (env))
570 {
571 munmap (p, ALIGN_UP (size, pagesize));
572 return NULL;
573 }
574 if (MappedByteBufferImpl_init == NULL)
575 {
576 JCL_ThrowException (env, "java/lang/InternalError",
577 "could not get MappedByteBufferImpl constructor");
578 munmap (p, ALIGN_UP (size, pagesize));
579 return NULL;
580 }
581
582 buffer = (*env)->NewObject (env, MappedByteBufferImpl_class,
583 MappedByteBufferImpl_init, Pointer_instance,
584 (jint) size, mode == 'r');
585 return buffer;
586 #else
587 (void) obj;
588 (void) mode;
589 (void) position;
590 (void) size;
591 JCL_ThrowException (env, IO_EXCEPTION,
592 "memory-mapped files not implemented");
593 return 0;
594 #endif /* HAVE_MMAP */
595 }
596
597 /*
598 * Read a single byte from the file descriptor
599 * Return byte read or -1 on eof, exception on error
600 */
601 JNIEXPORT jint JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_read__(JNIEnv * env,jobject obj)602 Java_gnu_java_nio_channels_FileChannelImpl_read__ (JNIEnv * env, jobject obj)
603 {
604 int native_fd;
605 char data;
606 ssize_t bytes_read;
607 int result;
608
609 native_fd = get_native_fd (env, obj);
610
611 bytes_read = 0;
612 do
613 {
614 TARGET_NATIVE_FILE_READ (native_fd, &data, 1, bytes_read, result);
615 if ((result == TARGET_NATIVE_OK) && (bytes_read == 0))
616 {
617 return (-1);
618 }
619 if ((result != TARGET_NATIVE_OK)
620 && (TARGET_NATIVE_LAST_ERROR () !=
621 TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
622 {
623 JCL_ThrowException (env, IO_EXCEPTION,
624 TARGET_NATIVE_LAST_ERROR_STRING ());
625 return (-1);
626 }
627 }
628 while (result != TARGET_NATIVE_OK);
629
630 return ((jint) (data & 0xFF));
631 }
632
633 /*
634 * Reads to a byte buffer from the specified file descriptor
635 * Return number of bytes read or -1 on eof, exception on error
636 */
637 JNIEXPORT jint JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_read___3BII(JNIEnv * env,jobject obj,jbyteArray buffer,jint offset,jint length)638 Java_gnu_java_nio_channels_FileChannelImpl_read___3BII (JNIEnv * env,
639 jobject obj,
640 jbyteArray buffer,
641 jint offset,
642 jint length)
643 {
644 int native_fd;
645 jbyte *bufptr;
646 ssize_t bytes_read;
647 ssize_t n;
648 int result;
649
650 native_fd = get_native_fd (env, obj);
651
652 /* Must return 0 if an attempt is made to read 0 bytes. */
653 if (length == 0)
654 return 0;
655
656 if (offset < 0)
657 {
658 JCL_ThrowException (env, IO_EXCEPTION, "negative offset");
659 return -1;
660 }
661
662 bufptr = (*env)->GetByteArrayElements (env, buffer, 0);
663 if (!bufptr)
664 {
665 JCL_ThrowException (env, IO_EXCEPTION, "Unexpected JNI error");
666 return (-1);
667 }
668
669 if (length + offset > (*env)->GetArrayLength (env, buffer))
670 {
671 JCL_ThrowException (env, IO_EXCEPTION,
672 "length + offset > buffer.length");
673 return -1;
674 }
675
676 bytes_read = 0;
677 do
678 {
679 TARGET_NATIVE_FILE_READ (native_fd, (bufptr + offset + bytes_read),
680 (length - bytes_read), n, result);
681 if ((result == TARGET_NATIVE_OK) && (n == 0))
682 {
683 (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
684 if (bytes_read == 0)
685 return -1; /* Signal end of file to Java */
686 else
687 return CONVERT_SSIZE_T_TO_JINT (bytes_read);
688 }
689 if ((result != TARGET_NATIVE_OK)
690 && (TARGET_NATIVE_LAST_ERROR () !=
691 TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
692 {
693 JCL_ThrowException (env, IO_EXCEPTION,
694 TARGET_NATIVE_LAST_ERROR_STRING ());
695 (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
696 return -1;
697 }
698 if (result == TARGET_NATIVE_OK)
699 bytes_read += n;
700 }
701 while (bytes_read < 1);
702
703 (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
704 return CONVERT_SSIZE_T_TO_JINT (bytes_read);
705 }
706
707 /*
708 * Writes a single byte to the specified file descriptor
709 * Return status code, exception on error
710 */
711 JNIEXPORT void JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_write__I(JNIEnv * env,jobject obj,jint b)712 Java_gnu_java_nio_channels_FileChannelImpl_write__I (JNIEnv * env,
713 jobject obj, jint b)
714 {
715 int native_fd;
716 char native_data;
717 ssize_t bytes_written;
718 int result;
719
720 native_fd = get_native_fd (env, obj);
721 native_data = (char) (CONVERT_JINT_TO_INT (b) & 0xFF);
722
723 do
724 {
725 TARGET_NATIVE_FILE_WRITE (native_fd, &native_data, 1, bytes_written,
726 result);
727 if ((result != TARGET_NATIVE_OK)
728 && (TARGET_NATIVE_LAST_ERROR () !=
729 TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
730 {
731 JCL_ThrowException (env, IO_EXCEPTION,
732 TARGET_NATIVE_LAST_ERROR_STRING ());
733 return;
734 }
735 }
736 while (result != TARGET_NATIVE_OK);
737 }
738
739 /*
740 * Copies all parts of a file to disk.
741 */
742 JNIEXPORT void JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_force(JNIEnv * env,jobject obj)743 Java_gnu_java_nio_channels_FileChannelImpl_force (JNIEnv * env,
744 jobject obj)
745 {
746 int native_fd;
747 int result;
748 native_fd = get_native_fd (env, obj);
749 TARGET_NATIVE_FILE_FSYNC (native_fd, result);
750 if (result != TARGET_NATIVE_OK)
751 JCL_ThrowException (env, IO_EXCEPTION,
752 TARGET_NATIVE_LAST_ERROR_STRING ());
753 }
754
755 /*
756 * Writes a byte buffer to the specified file descriptor
757 * Return status code, exception on error
758 */
759 JNIEXPORT void JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_write___3BII(JNIEnv * env,jobject obj,jbyteArray buffer,jint offset,jint length)760 Java_gnu_java_nio_channels_FileChannelImpl_write___3BII (JNIEnv * env,
761 jobject obj,
762 jbyteArray buffer,
763 jint offset,
764 jint length)
765 {
766 int native_fd;
767 jbyte *bufptr;
768 ssize_t bytes_written;
769 ssize_t n;
770 int result;
771
772 native_fd = get_native_fd (env, obj);
773
774 /* Just return if an attempt is made to write 0 bytes. */
775 if (length == 0)
776 return;
777
778 bufptr = (*env)->GetByteArrayElements (env, buffer, 0);
779 if (!bufptr)
780 {
781 JCL_ThrowException (env, IO_EXCEPTION, "Unexpected JNI error");
782 return;
783 }
784
785 bytes_written = 0;
786 while (bytes_written < CONVERT_JINT_TO_SSIZE_T (length))
787 {
788 TARGET_NATIVE_FILE_WRITE (native_fd, (bufptr + offset + bytes_written),
789 (length - bytes_written), n, result);
790 if ((result != TARGET_NATIVE_OK)
791 && (TARGET_NATIVE_LAST_ERROR () !=
792 TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
793 {
794 JCL_ThrowException (env, IO_EXCEPTION,
795 TARGET_NATIVE_LAST_ERROR_STRING ());
796 (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
797 return;
798 }
799 if (result == TARGET_NATIVE_OK)
800 bytes_written += n;
801 }
802
803 (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
804 }
805
806 JNIEXPORT jboolean JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_lock(JNIEnv * env,jobject obj,jlong position,jlong size,jboolean shared,jboolean wait)807 Java_gnu_java_nio_channels_FileChannelImpl_lock (JNIEnv *env, jobject obj,
808 jlong position, jlong size,
809 jboolean shared, jboolean wait)
810 {
811 #ifdef HAVE_FCNTL
812 int fd = get_native_fd (env, obj);
813 int cmd = wait ? F_SETLKW : F_SETLK;
814 struct flock flock;
815 int ret;
816
817 flock.l_type = shared ? F_RDLCK : F_WRLCK;
818 flock.l_whence = SEEK_SET;
819 flock.l_start = (off_t) position;
820 /* Long.MAX_VALUE means lock everything possible starting at pos. */
821 if (size == 9223372036854775807LL)
822 flock.l_len = 0;
823 else
824 flock.l_len = (off_t) size;
825
826 ret = fcntl (fd, cmd, &flock);
827 /* fprintf(stderr, "fd %d, wait %d, shared %d, ret %d, position %lld, size %lld, l_start %ld, l_len %ld\n", fd, wait, shared,ret, position, size, (long) flock.l_start, (long) flock.l_len); */
828 if (ret)
829 {
830 /* Linux man pages for fcntl state that errno might be either
831 EACCES or EAGAIN if we try F_SETLK, and another process has
832 an overlapping lock. We should not get an unexpected errno. */
833 if (errno != EACCES && errno != EAGAIN)
834 {
835 JCL_ThrowException (env, "java/lang/InternalError",
836 strerror (errno));
837 }
838 return JNI_FALSE;
839 }
840 return JNI_TRUE;
841 #else
842 (void) obj;
843 (void) position;
844 (void) size;
845 (void) shared;
846 (void) wait;
847 JCL_ThrowException (env, "java/lang/UnsupportedOperationException",
848 "file locks not implemented on this platform");
849 return JNI_FALSE;
850 #endif /* HAVE_FCNTL */
851 }
852
853 JNIEXPORT void JNICALL
Java_gnu_java_nio_channels_FileChannelImpl_unlock(JNIEnv * env,jobject obj,jlong position,jlong length)854 Java_gnu_java_nio_channels_FileChannelImpl_unlock (JNIEnv *env,
855 jobject obj,
856 jlong position,
857 jlong length)
858 {
859 #ifdef HAVE_FCNTL
860 int fd = get_native_fd (env, obj);
861 struct flock flock;
862 int ret;
863
864 flock.l_type = F_UNLCK;
865 flock.l_whence = SEEK_SET;
866 flock.l_start = (off_t) position;
867 /* Long.MAX_VALUE means unlock everything possible starting at pos. */
868 if (length == 9223372036854775807LL)
869 flock.l_len = 0;
870 else
871 flock.l_len = (off_t) length;
872
873 ret = fcntl (fd, F_SETLK, &flock);
874 if (ret)
875 {
876 JCL_ThrowException (env, "java/lang/InternalError",
877 strerror (errno));
878 }
879 #else
880 (void) obj;
881 (void) position;
882 (void) length;
883 JCL_ThrowException (env, "java/lang/UnsupportedOperationException",
884 "file locks not implemented on this platform");
885 #endif /* HAVE_FCNTL */
886 }
887