1 
2 // natFileChannelImplPosix.cc - Native part of FileChannelImpl class.
3 
4 /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006, 2007  Free Software Foundation
5 
6    This file is part of libgcj.
7 
8 This software is copyrighted work licensed under the terms of the
9 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
10 details.  */
11 
12 #include <config.h>
13 #include <platform.h>
14 
15 #include <gcj/cni.h>
16 #include <gcj/javaprims.h>
17 #include <jvm.h>
18 
19 #include "posix.h"
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <sys/param.h>
27 
28 #include <gnu/gcj/RawData.h>
29 #include <gnu/java/nio/FileLockImpl.h>
30 #include <gnu/java/nio/channels/FileChannelImpl.h>
31 #include <java/io/FileNotFoundException.h>
32 #include <java/io/IOException.h>
33 #include <java/io/SyncFailedException.h>
34 #include <java/io/InterruptedIOException.h>
35 #include <java/io/EOFException.h>
36 #include <java/lang/ArrayIndexOutOfBoundsException.h>
37 #include <java/lang/NullPointerException.h>
38 #include <java/lang/System.h>
39 #include <java/lang/String.h>
40 #include <java/lang/StringBuffer.h>
41 #include <java/lang/Thread.h>
42 #include <java/nio/ByteBuffer.h>
43 #include <java/nio/MappedByteBufferImpl.h>
44 #include <java/nio/channels/FileChannel.h>
45 #include <java/nio/channels/FileLock.h>
46 #include <gnu/java/nio/channels/FileChannelImpl.h>
47 
48 #ifdef HAVE_SYS_IOCTL_H
49 #define BSD_COMP /* Get FIONREAD on Solaris2. */
50 #include <sys/ioctl.h>
51 #endif
52 
53 // Pick up FIONREAD on Solaris 2.5.
54 #ifdef HAVE_SYS_FILIO_H
55 #include <sys/filio.h>
56 #endif
57 
58 #ifdef HAVE_MMAP
59 #include <sys/mman.h>
60 
61 // Use overload resolution to find out the argument types.
62 // E.g. Solaris 2.6 uses different argument types for munmap and msync.
63 // This is in case _POSIX_C_SOURCES is smaller than 3.
64 
65 template <typename T_implPtr, typename T_implLen>
66 static inline int
munmap_adaptor(int (* munmap)(T_implPtr caddr,T_implLen sizet),void * caddr,size_t sizet)67 munmap_adaptor(int (*munmap)(T_implPtr caddr, T_implLen sizet),
68 		 void* caddr, size_t sizet)
69 {
70   return munmap ((T_implPtr) caddr, (T_implLen) sizet);
71 }
72 
73 template <typename T_implPtr, typename T_implLen, typename T_msync>
74 static inline int
msync_adaptor(int (* msync)(T_implPtr caddr,T_implLen sizet,T_msync msynct),void * caddr,size_t sizet,int msynct)75 msync_adaptor(int (*msync)(T_implPtr caddr, T_implLen sizet, T_msync msynct),
76 	      void* caddr, size_t sizet, int msynct)
77 {
78   return msync ((T_implPtr) caddr, (T_implLen) sizet, (T_msync) msynct);
79 }
80 #endif
81 
82 using gnu::gcj::RawData;
83 using java::io::IOException;
84 using java::nio::MappedByteBufferImpl;
85 using java::io::InterruptedIOException;
86 using java::io::FileNotFoundException;
87 using java::lang::ArrayIndexOutOfBoundsException;
88 using gnu::java::nio::channels::FileChannelImpl;
89 
90 #define NO_FSYNC_MESSAGE "sync unsupported"
91 
92 void
init(void)93 FileChannelImpl::init(void)
94 {
95   in = new FileChannelImpl((jint) 0, FileChannelImpl::READ);
96   out = new FileChannelImpl((jint) 1, FileChannelImpl::WRITE);
97   err = new FileChannelImpl((jint) 2, FileChannelImpl::WRITE);
98 }
99 
100 #if 0
101 jboolean
102 FileChannelImpl::valid (void)
103 {
104   struct stat sb;
105   return fd >= 0 && ::fstat (fd, &sb) == 0;
106 }
107 
108 void
109 FileChannelImpl::sync (void)
110 {
111   // Some files don't support fsync.  We don't bother reporting these
112   // as errors.
113 #ifdef HAVE_FSYNC
114   if (::fsync (fd) && errno != EROFS && errno != EINVAL)
115     throw new SyncFailedException (JvNewStringLatin1 (strerror (errno)));
116 #else
117   throw new SyncFailedException (JvNewStringLatin1 (NO_FSYNC_MESSAGE));
118 #endif
119 }
120 #endif
121 
122 jint
open(jstring path,jint jflags)123 FileChannelImpl::open (jstring path, jint jflags)
124 {
125   fd = -1;
126   char *buf = (char *) _Jv_AllocBytes (_Jv_GetStringUTFLength (path) + 1);
127   jsize total = JvGetStringUTFRegion (path, 0, path->length(), buf);
128   buf[total] = '\0';
129   int flags = 0;
130 #ifdef O_BINARY
131   flags |= O_BINARY;
132 #endif
133 
134   JvAssert ((jflags & READ) || (jflags & WRITE));
135   int mode = 0666;
136   if ((jflags & READ) && (jflags & WRITE))
137     flags |= O_RDWR | O_CREAT;
138   else if ((jflags & READ))
139     flags |= O_RDONLY;
140   else
141     {
142       flags |= O_WRONLY | O_CREAT;
143       if ((jflags & APPEND))
144 	flags |= O_APPEND;
145       else
146 	flags |= O_TRUNC;
147 
148       if ((jflags & EXCL))
149 	{
150 	  flags |= O_EXCL;
151 	  // In this case we are making a temp file.
152 	  mode = 0600;
153 	}
154     }
155 
156   if ((jflags & SYNC))
157     flags |= O_SYNC;
158 
159   if ((jflags & DSYNC))
160     flags |= O_DSYNC;
161 
162   int fd = ::open (buf, flags, mode);
163   if (fd == -1 && errno == EMFILE)
164     {
165       // Because finalize () calls close () we might be able to continue.
166       ::java::lang::System::gc ();
167       ::java::lang::System::runFinalization ();
168       fd = ::open (buf, flags, mode);
169     }
170   if (fd == -1)
171     {
172       // We choose the formatting here for JDK compatibility, believe
173       // it or not.
174       ::java::lang::StringBuffer *msg = new ::java::lang::StringBuffer (path);
175       msg->append (JvNewStringUTF (" ("));
176       msg->append (JvNewStringUTF (strerror (errno)));
177       msg->append (JvNewStringUTF (")"));
178       throw new ::java::io::FileNotFoundException (msg->toString ());
179     }
180 
181   return fd;
182 }
183 
184 void
write(jint b)185 FileChannelImpl::write (jint b)
186 {
187   jbyte d = (jbyte) b;
188   int r = 0;
189   while (r != 1)
190     {
191       r = ::write (fd, &d, 1);
192       if (r == -1)
193         {
194 	  if (::java::lang::Thread::interrupted())
195 	    {
196 	      ::java::io::InterruptedIOException *iioe
197 		= new ::java::io::InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
198 	      iioe->bytesTransferred = r == -1 ? 0 : r;
199 	      throw iioe;
200 	    }
201 	  if (errno != EINTR)
202 	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
203 	}
204     }
205   pos++;
206 }
207 
208 void
write(jbyteArray b,jint offset,jint len)209 FileChannelImpl::write (jbyteArray b, jint offset, jint len)
210 {
211   if (! b)
212     throw new ::java::lang::NullPointerException;
213   if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
214     throw new ArrayIndexOutOfBoundsException;
215   jbyte *bytes = elements (b) + offset;
216 
217   int written = 0;
218   while (len > 0)
219     {
220       int r = ::write (fd, bytes, len);
221       if (r == -1)
222         {
223 	  if (::java::lang::Thread::interrupted())
224 	    {
225 	      InterruptedIOException *iioe
226 		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
227 	      iioe->bytesTransferred = written;
228 	      throw iioe;
229 	    }
230 	  if (errno != EINTR)
231 	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
232 	  continue;
233 	}
234 
235       written += r;
236       len -= r;
237       bytes += r;
238       pos += r;
239     }
240 }
241 
242 void
implCloseChannel(void)243 FileChannelImpl::implCloseChannel (void)
244 {
245   jint save = fd;
246   fd = -1;
247   if (::close (save))
248     throw new IOException (JvNewStringLatin1 (strerror (errno)));
249 }
250 
251 void
implTruncate(jlong size)252 FileChannelImpl::implTruncate (jlong size)
253 {
254   struct stat sb;
255 
256 #ifdef HAVE_FTRUNCATE
257   if (::fstat (fd, &sb))
258     throw new IOException (JvNewStringLatin1 (strerror (errno)));
259 
260   if ((jlong) sb.st_size == size)
261     return;
262 
263   // If the file is too short, we extend it.  We can't rely on
264   // ftruncate() extending the file.  So we lseek() to 1 byte less
265   // than we want, and then we write a single byte at the end.
266   if ((jlong) sb.st_size < size)
267     {
268       if (::lseek (fd, (off_t) (size - 1), SEEK_SET) == -1)
269 	throw new IOException (JvNewStringLatin1 (strerror (errno)));
270       char out = '\0';
271       int r = ::write (fd, &out, 1);
272       if (r <= 0 || ::lseek (fd, pos, SEEK_SET) == -1)
273 	throw new IOException (JvNewStringLatin1 (strerror (errno)));
274     }
275   else
276     {
277       if (::ftruncate (fd, (off_t) size))
278 	throw new IOException (JvNewStringLatin1 (strerror (errno)));
279       if (pos > size
280 	  && ::lseek (fd, (off_t) size, SEEK_SET) == -1)
281 	throw new IOException (JvNewStringLatin1 (strerror (errno)));
282       pos = size;
283     }
284 #else /* HAVE_FTRUNCATE */
285   throw new IOException (JvNewStringLatin1 ("FileDescriptor.setLength not implemented"));
286 #endif /* HAVE_FTRUNCATE */
287 }
288 
289 void
seek(jlong newPos)290 FileChannelImpl::seek (jlong newPos)
291 {
292   off_t r = ::lseek (fd, (off_t) newPos, SEEK_SET);
293   if (r == -1)
294     throw new IOException (JvNewStringLatin1 (strerror (errno)));
295   pos = r;
296 }
297 
298 jlong
size(void)299 FileChannelImpl::size (void)
300 {
301   struct stat sb;
302   if (::fstat (fd, &sb))
303     throw new IOException (JvNewStringLatin1 (strerror (errno)));
304   return sb.st_size;
305 }
306 
307 jlong
implPosition(void)308 FileChannelImpl::implPosition (void)
309 {
310   return pos;
311 }
312 
313 jint
read(void)314 FileChannelImpl::read (void)
315 {
316   jbyte b;
317   int r;
318   do
319     {
320       r = ::read (fd, &b, 1);
321       if (r == 0)
322 	return -1;
323       if (r == -1)
324 	{
325 	  if (::java::lang::Thread::interrupted())
326 	    {
327 	      InterruptedIOException *iioe
328 		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
329 	      iioe->bytesTransferred = r == -1 ? 0 : r;
330 	      throw iioe;
331 	    }
332 	  if (errno != EINTR)
333 	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
334 	}
335     }
336   while (r != 1);
337   pos++;
338   return b & 0xFF;
339 }
340 
341 jint
read(jbyteArray buffer,jint offset,jint count)342 FileChannelImpl::read (jbyteArray buffer, jint offset, jint count)
343 {
344   if (! buffer)
345     throw new ::java::lang::NullPointerException;
346   jsize bsize = JvGetArrayLength (buffer);
347   if (offset < 0 || count < 0 || offset + count > bsize)
348     throw new ::java::lang::ArrayIndexOutOfBoundsException;
349 
350   // Must return 0 if an attempt is made to read 0 bytes.
351   if (count == 0)
352     return 0;
353 
354   jbyte *bytes = elements (buffer) + offset;
355   int r;
356   do
357     {
358       r = ::read (fd, bytes, count);
359       if (r == 0)
360 	return -1;
361       if (r == -1)
362 	{
363 	  if (::java::lang::Thread::interrupted())
364 	    {
365 	      InterruptedIOException *iioe
366 		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
367 	      iioe->bytesTransferred = r == -1 ? 0 : r;
368 	      throw iioe;
369 	    }
370 	  if (errno != EINTR)
371 	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
372 	}
373     }
374   while (r <= 0);
375   pos += r;
376   return r;
377 }
378 
379 jint
available(void)380 FileChannelImpl::available (void)
381 {
382 #if defined (FIONREAD) || defined (HAVE_SELECT) || defined (HAVE_FSTAT)
383   int num = 0;
384   int r = 0;
385   bool num_set = false;
386 
387 #if defined (FIONREAD)
388   r = ::ioctl (fd, FIONREAD, &num);
389   if (r == -1 && errno == ENOTTY)
390     {
391       // If the ioctl doesn't work, we don't care.
392       r = 0;
393       num = 0;
394     }
395   else
396     num_set = true;
397 #elif defined (HAVE_SELECT)
398   if (fd < 0)
399     {
400       errno = EBADF;
401       r = -1;
402     }
403 #endif
404 
405   if (r == -1)
406     {
407     posix_error:
408       throw new IOException (JvNewStringLatin1 (strerror (errno)));
409     }
410 
411   // If we didn't get anything, and we have fstat, then see if see if
412   // we're reading a regular file.  On many systems, FIONREAD does not
413   // work on regular files; select() likewise returns a useless
414   // result.  This is run incorrectly when FIONREAD does work on
415   // regular files and we are at the end of the file.  However, this
416   // case probably isn't very important.
417 #if defined (HAVE_FSTAT)
418   if (! num_set)
419     {
420       struct stat sb;
421       off_t where = 0;
422       if (fstat (fd, &sb) != -1
423 	  && S_ISREG (sb.st_mode)
424 	  && (where = lseek (fd, 0, SEEK_CUR)) != (off_t) -1)
425 	{
426 	  num = (int) (sb.st_size - where);
427 	  num_set = true;
428 	}
429     }
430 #endif /* HAVE_FSTAT */
431 
432 #if defined (HAVE_SELECT)
433   if (! num_set)
434     {
435       fd_set rd;
436       FD_ZERO (&rd);
437       FD_SET (fd, &rd);
438       struct timeval tv;
439       tv.tv_sec = 0;
440       tv.tv_usec = 0;
441       r = _Jv_select (fd + 1, &rd, NULL, NULL, &tv);
442       if (r == -1)
443 	goto posix_error;
444       num = r == 0 ? 0 : 1;
445     }
446 #endif /* HAVE_SELECT */
447 
448   return (jint) num;
449 #else
450   return 0;
451 #endif
452 }
453 
454 jboolean
lock(jlong pos,jlong len,jboolean shared,jboolean wait)455 FileChannelImpl::lock
456 (jlong pos, jlong len, jboolean shared, jboolean wait)
457 {
458   struct flock lockdata;
459 
460   lockdata.l_type = shared ? F_RDLCK : F_WRLCK;
461   lockdata.l_whence = SEEK_SET;
462   lockdata.l_start = pos;
463   lockdata.l_len = len;
464 
465   if (::fcntl (fd, wait ? F_SETLKW : F_SETLK, &lockdata) == -1)
466     {
467       if (! wait && (errno == EACCES || errno == EAGAIN))
468 	return false;
469       throw new IOException (JvNewStringLatin1 (strerror (errno)));
470     }
471   return true;
472 }
473 
474 void
unlock(jlong pos,jlong len)475 FileChannelImpl::unlock (jlong pos, jlong len)
476 {
477   struct flock lockdata;
478 
479   lockdata.l_type = F_UNLCK;
480   lockdata.l_whence = SEEK_SET;
481   lockdata.l_start = pos;
482   lockdata.l_len = len;
483 
484   if (::fcntl (fd, F_SETLK, &lockdata) == -1)
485     throw new IOException (JvNewStringLatin1 (strerror (errno)));
486 }
487 
488 java::nio::MappedByteBuffer *
mapImpl(jchar mmode,jlong position,jint size)489 FileChannelImpl::mapImpl (jchar mmode, jlong position, jint size)
490 {
491 #if defined(HAVE_MMAP)
492   int prot, flags;
493   if (mmode == 'r')
494     {
495       prot = PROT_READ;
496       flags = MAP_PRIVATE;
497     }
498   else
499     {
500       prot = PROT_READ|PROT_WRITE;
501       flags = mmode == '+' ? MAP_SHARED : MAP_PRIVATE;
502 
503       // If the file is too short, we must extend it.  While using
504       // ftruncate() to extend a file is not portable in general, it
505       // should work on all systems where you can mmap() a file.
506       struct stat st;
507       if (fstat (fd, &st) == -1)
508 	throw new IOException (JvNewStringLatin1 (strerror (errno)));
509       if (position + size > st.st_size)
510 	{
511 	  if (ftruncate (fd, position + size) == -1)
512 	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
513 	}
514     }
515   jint page_size = ::getpagesize();
516   jint offset = position & ~(page_size-1);
517   jint align = position - offset;
518   void* ptr = ::mmap(NULL, size + align, prot, flags, fd, offset);
519   MappedByteBufferImpl *buf
520     = new MappedByteBufferImpl ((RawData *) ((char *) ptr + align),
521 				size, mmode == 'r');
522   if (ptr == (void *) MAP_FAILED)
523     throw new IOException (JvNewStringLatin1 (strerror (errno)));
524   buf->implPtr = reinterpret_cast<RawData*> (ptr);
525   buf->implLen = size+align;
526   return buf;
527 #else
528   throw new IOException (JvNewStringUTF ("mmap not implemented"));
529 #endif
530 }
531 
532 void
unmapImpl()533 MappedByteBufferImpl::unmapImpl ()
534 {
535 #if defined(HAVE_MMAP)
536   munmap_adaptor(munmap, implPtr, implLen);
537 #endif
538 }
539 
540 void
loadImpl()541 MappedByteBufferImpl::loadImpl ()
542 {
543 }
544 
545 jboolean
isLoadedImpl()546 MappedByteBufferImpl::isLoadedImpl ()
547 {
548   return true;
549 }
550 
551 void
forceImpl()552 MappedByteBufferImpl::forceImpl ()
553 {
554 #if defined(HAVE_MMAP)
555   ::msync_adaptor(msync, implPtr, implLen, MS_SYNC);
556 #endif
557 }
558