OSDN Git Service

* config/i386/i386.md (*sinxf2): Rename to *sinxf2_i387.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / native / jni / java-nio / gnu_java_nio_channels_FileChannelImpl.c
1 /* gnu_java_nio_channels_FileChannelImpl.c -
2    Copyright (C) 2003, 2004, 2005, 2006  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
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
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
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
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
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
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
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
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
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
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   fd = get_native_fd (env, obj);
542
543   prot = PROT_READ;
544   if (mode == '+' || mode == 'c')
545     {
546       /* When writing we need to make sure the file is big enough,
547          otherwise the result of mmap is undefined. */
548       jlong filesize;
549       filesize = Java_gnu_java_nio_channels_FileChannelImpl_size(env, obj);
550       if (filesize == -1)
551         return NULL;
552       if (position + size > filesize)
553         if (ftruncate(fd, position + size) == -1)
554           {
555             JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
556             return NULL;
557           }
558       prot |= PROT_WRITE;
559     }
560
561   flags = (mode == 'c' ? MAP_PRIVATE : MAP_SHARED);
562   p = mmap (NULL, (size_t) ALIGN_UP (size, pagesize), prot, flags,
563             fd, ALIGN_DOWN (position, pagesize));
564   if (p == MAP_FAILED)
565     {
566       JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
567       return NULL;
568     }
569
570   /* Unalign the mapped value back up, since we aligned offset
571      down to a multiple of the page size. */
572   address = (void *) ((char *) p + (position % pagesize));
573
574   Pointer_instance = JCL_NewRawDataObject(env, address);
575
576   MappedByteBufferImpl_class = (*env)->FindClass (env,
577                                                   "java/nio/MappedByteBufferImpl");
578   if (MappedByteBufferImpl_class != NULL)
579     {
580       MappedByteBufferImpl_init =
581         (*env)->GetMethodID (env, MappedByteBufferImpl_class,
582                              "<init>", "(Lgnu/classpath/Pointer;IZ)V");
583     }
584
585   if ((*env)->ExceptionOccurred (env))
586     {
587       munmap (p, ALIGN_UP (size, pagesize));
588       return NULL;
589     }
590   if (MappedByteBufferImpl_init == NULL)
591     {
592       JCL_ThrowException (env, "java/lang/InternalError",
593                           "could not get MappedByteBufferImpl constructor");
594       munmap (p, ALIGN_UP (size, pagesize));
595       return NULL;
596     }
597
598   buffer = (*env)->NewObject (env, MappedByteBufferImpl_class,
599                               MappedByteBufferImpl_init, Pointer_instance,
600                               (jint) size, mode == 'r');
601   return buffer;
602 #else
603   (void) obj;
604   (void) mode;
605   (void) position;
606   (void) size;
607   JCL_ThrowException (env, IO_EXCEPTION,
608                       "memory-mapped files not implemented");
609   return 0;
610 #endif /* HAVE_MMAP */
611 }
612
613 /*
614  * Read a single byte from the file descriptor
615  * Return byte read or -1 on eof, exception on error
616  */
617 JNIEXPORT jint JNICALL
618 Java_gnu_java_nio_channels_FileChannelImpl_read__ (JNIEnv * env, jobject obj)
619 {
620   int native_fd;
621   char data;
622   ssize_t bytes_read;
623   int result;
624
625   native_fd = get_native_fd (env, obj);
626
627   bytes_read = 0;
628   do
629     {
630       TARGET_NATIVE_FILE_READ (native_fd, &data, 1, bytes_read, result);
631       if ((result == TARGET_NATIVE_OK) && (bytes_read == 0))
632         {
633           return (-1);
634         }
635       if ((result != TARGET_NATIVE_OK)
636           && (TARGET_NATIVE_LAST_ERROR () !=
637               TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
638         {
639           JCL_ThrowException (env, IO_EXCEPTION,
640                               TARGET_NATIVE_LAST_ERROR_STRING ());
641           return (-1);
642         }
643     }
644   while (result != TARGET_NATIVE_OK);
645
646   return ((jint) (data & 0xFF));
647 }
648
649 /*
650  * Reads to a byte buffer from the specified file descriptor
651  * Return number of bytes read or -1 on eof, exception on error
652  */
653 JNIEXPORT jint JNICALL
654 Java_gnu_java_nio_channels_FileChannelImpl_read___3BII (JNIEnv * env,
655                                                         jobject obj,
656                                                         jbyteArray buffer,
657                                                         jint offset,
658                                                         jint length)
659 {
660   int native_fd;
661   jbyte *bufptr;
662   ssize_t bytes_read;
663   ssize_t n;
664   int result;
665
666   native_fd = get_native_fd (env, obj);
667
668   /* Must return 0 if an attempt is made to read 0 bytes. */
669   if (length == 0)
670     return 0;
671
672   if (offset < 0)
673     {
674       JCL_ThrowException (env, IO_EXCEPTION, "negative offset");
675       return -1;
676     }
677
678   bufptr = (*env)->GetByteArrayElements (env, buffer, 0);
679   if (!bufptr)
680     {
681       JCL_ThrowException (env, IO_EXCEPTION, "Unexpected JNI error");
682       return (-1);
683     }
684
685   if (length + offset > (*env)->GetArrayLength (env, buffer))
686     {
687       JCL_ThrowException (env, IO_EXCEPTION,
688                           "length + offset > buffer.length");
689       return -1;
690     }
691
692   bytes_read = 0;
693   do
694     {
695       TARGET_NATIVE_FILE_READ (native_fd, (bufptr + offset + bytes_read),
696                                (length - bytes_read), n, result);
697       if ((result == TARGET_NATIVE_OK) && (n == 0))
698         {
699           (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
700           if (bytes_read == 0)
701             return -1;          /* Signal end of file to Java */
702           else
703             return CONVERT_SSIZE_T_TO_JINT (bytes_read);
704         }
705       if ((result != TARGET_NATIVE_OK)
706           && (TARGET_NATIVE_LAST_ERROR () !=
707               TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
708         {
709           JCL_ThrowException (env, IO_EXCEPTION,
710                               TARGET_NATIVE_LAST_ERROR_STRING ());
711           (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
712           return -1;
713         }
714       if (result == TARGET_NATIVE_OK)
715         bytes_read += n;
716     }
717   while (bytes_read < 1);
718
719   (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
720   return CONVERT_SSIZE_T_TO_JINT (bytes_read);
721 }
722
723 /*
724  * Writes a single byte to the specified file descriptor
725  * Return status code, exception on error
726  */
727 JNIEXPORT void JNICALL
728 Java_gnu_java_nio_channels_FileChannelImpl_write__I (JNIEnv * env,
729                                                      jobject obj, jint b)
730 {
731   int native_fd;
732   char native_data;
733   ssize_t bytes_written;
734   int result;
735
736   native_fd = get_native_fd (env, obj);
737   native_data = (char) (CONVERT_JINT_TO_INT (b) & 0xFF);
738
739   do
740     {
741       TARGET_NATIVE_FILE_WRITE (native_fd, &native_data, 1, bytes_written,
742                                 result);
743       if ((result != TARGET_NATIVE_OK)
744           && (TARGET_NATIVE_LAST_ERROR () !=
745               TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
746         {
747           JCL_ThrowException (env, IO_EXCEPTION,
748                               TARGET_NATIVE_LAST_ERROR_STRING ());
749           return;
750         }
751     }
752   while (result != TARGET_NATIVE_OK);
753 }
754
755 /*
756  * Copies all parts of a file to disk.
757  */
758 JNIEXPORT void JNICALL
759 Java_gnu_java_nio_channels_FileChannelImpl_force (JNIEnv * env,
760                                                   jobject obj)
761 {
762   int native_fd;
763   int result;
764   native_fd = get_native_fd (env, obj);
765   TARGET_NATIVE_FILE_FSYNC (native_fd, result);
766   if (result != TARGET_NATIVE_OK)
767     JCL_ThrowException (env, IO_EXCEPTION,
768                         TARGET_NATIVE_LAST_ERROR_STRING ());
769 }
770
771 /*
772  * Writes a byte buffer to the specified file descriptor
773  * Return status code, exception on error
774  */
775 JNIEXPORT void JNICALL
776 Java_gnu_java_nio_channels_FileChannelImpl_write___3BII (JNIEnv * env,
777                                                          jobject obj,
778                                                          jbyteArray buffer,
779                                                          jint offset,
780                                                          jint length)
781 {
782   int native_fd;
783   jbyte *bufptr;
784   ssize_t bytes_written;
785   ssize_t n;
786   int result;
787
788   native_fd = get_native_fd (env, obj);
789
790   /* Just return if an attempt is made to write 0 bytes. */
791   if (length == 0)
792     return;
793
794   bufptr = (*env)->GetByteArrayElements (env, buffer, 0);
795   if (!bufptr)
796     {
797       JCL_ThrowException (env, IO_EXCEPTION, "Unexpected JNI error");
798       return;
799     }
800
801   bytes_written = 0;
802   while (bytes_written < CONVERT_JINT_TO_SSIZE_T (length))
803     {
804       TARGET_NATIVE_FILE_WRITE (native_fd, (bufptr + offset + bytes_written),
805                                 (length - bytes_written), n, result);
806       if ((result != TARGET_NATIVE_OK)
807           && (TARGET_NATIVE_LAST_ERROR () !=
808               TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL))
809         {
810           JCL_ThrowException (env, IO_EXCEPTION,
811                               TARGET_NATIVE_LAST_ERROR_STRING ());
812           (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
813           return;
814         }
815       if (result == TARGET_NATIVE_OK)
816         bytes_written += n;
817     }
818
819   (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0);
820 }
821
822 JNIEXPORT jboolean JNICALL
823 Java_gnu_java_nio_channels_FileChannelImpl_lock (JNIEnv *env, jobject obj,
824                                                  jlong position, jlong size,
825                                                  jboolean shared, jboolean wait)
826 {
827 #ifdef HAVE_FCNTL
828   int fd = get_native_fd (env, obj);
829   int cmd = wait ? F_SETLKW : F_SETLK;
830   struct flock flock;
831   int ret;
832
833   flock.l_type = shared ? F_RDLCK : F_WRLCK;
834   flock.l_whence = SEEK_SET;
835   flock.l_start = (off_t) position;
836   /* Long.MAX_VALUE means lock everything possible starting at pos. */
837   if (size == 9223372036854775807LL)
838     flock.l_len = 0;
839   else
840     flock.l_len = (off_t) size;
841
842   ret = fcntl (fd, cmd, &flock);
843   /* 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); */
844   if (ret)
845     {
846       /* Linux man pages for fcntl state that errno might be either
847          EACCES or EAGAIN if we try F_SETLK, and another process has
848          an overlapping lock. We should not get an unexpected errno. */
849       if (errno != EACCES && errno != EAGAIN)
850         {
851           JCL_ThrowException (env, "java/lang/InternalError",
852                               strerror (errno));
853         }
854       return JNI_FALSE;
855     }
856   return JNI_TRUE;
857 #else
858   (void) obj;
859   (void) position;
860   (void) size;
861   (void) shared;
862   (void) wait;
863   JCL_ThrowException (env, "java/lang/UnsupportedOperationException",
864                       "file locks not implemented on this platform");
865   return JNI_FALSE;
866 #endif /* HAVE_FCNTL */
867 }
868
869 JNIEXPORT void JNICALL
870 Java_gnu_java_nio_channels_FileChannelImpl_unlock (JNIEnv *env,
871                                                    jobject obj,
872                                                    jlong position,
873                                                    jlong length)
874 {
875 #ifdef HAVE_FCNTL
876   int fd = get_native_fd (env, obj);
877   struct flock flock;
878   int ret;
879
880   flock.l_type = F_UNLCK;
881   flock.l_whence = SEEK_SET;
882   flock.l_start = (off_t) position;
883   /* Long.MAX_VALUE means unlock everything possible starting at pos. */
884   if (length == 9223372036854775807LL)
885     flock.l_len = 0;
886   else
887     flock.l_len = (off_t) length;
888
889   ret = fcntl (fd, F_SETLK, &flock);
890   if (ret)
891     {
892       JCL_ThrowException (env, "java/lang/InternalError",
893                           strerror (errno));
894     }
895 #else
896   (void) obj;
897   (void) position;
898   (void) length;
899   JCL_ThrowException (env, "java/lang/UnsupportedOperationException",
900                       "file locks not implemented on this platform");
901 #endif /* HAVE_FCNTL */
902 }