summaryrefslogtreecommitdiff
path: root/libjava/gnu/java/nio/channels/natFileChannelPosix.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/gnu/java/nio/channels/natFileChannelPosix.cc')
-rw-r--r--libjava/gnu/java/nio/channels/natFileChannelPosix.cc557
1 files changed, 557 insertions, 0 deletions
diff --git a/libjava/gnu/java/nio/channels/natFileChannelPosix.cc b/libjava/gnu/java/nio/channels/natFileChannelPosix.cc
new file mode 100644
index 000000000..e05904c50
--- /dev/null
+++ b/libjava/gnu/java/nio/channels/natFileChannelPosix.cc
@@ -0,0 +1,557 @@
+
+// natFileChannelImplPosix.cc - Native part of FileChannelImpl class.
+
+/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006, 2007 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+#include <config.h>
+#include <platform.h>
+
+#include <gcj/cni.h>
+#include <gcj/javaprims.h>
+#include <jvm.h>
+
+#include "posix.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <gnu/gcj/RawData.h>
+#include <gnu/java/nio/FileLockImpl.h>
+#include <gnu/java/nio/channels/FileChannelImpl.h>
+#include <java/io/FileNotFoundException.h>
+#include <java/io/IOException.h>
+#include <java/io/SyncFailedException.h>
+#include <java/io/InterruptedIOException.h>
+#include <java/io/EOFException.h>
+#include <java/lang/ArrayIndexOutOfBoundsException.h>
+#include <java/lang/NullPointerException.h>
+#include <java/lang/System.h>
+#include <java/lang/String.h>
+#include <java/lang/StringBuffer.h>
+#include <java/lang/Thread.h>
+#include <java/nio/ByteBuffer.h>
+#include <java/nio/MappedByteBufferImpl.h>
+#include <java/nio/channels/FileChannel.h>
+#include <java/nio/channels/FileLock.h>
+#include <gnu/java/nio/channels/FileChannelImpl.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+#define BSD_COMP /* Get FIONREAD on Solaris2. */
+#include <sys/ioctl.h>
+#endif
+
+// Pick up FIONREAD on Solaris 2.5.
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+
+// Use overload resolution to find out the argument types.
+// E.g. Solaris 2.6 uses different argument types for munmap and msync.
+// This is in case _POSIX_C_SOURCES is smaller than 3.
+
+template <typename T_implPtr, typename T_implLen>
+static inline int
+munmap_adaptor(int (*munmap)(T_implPtr caddr, T_implLen sizet),
+ void* caddr, size_t sizet)
+{
+ return munmap ((T_implPtr) caddr, (T_implLen) sizet);
+}
+
+template <typename T_implPtr, typename T_implLen, typename T_msync>
+static inline int
+msync_adaptor(int (*msync)(T_implPtr caddr, T_implLen sizet, T_msync msynct),
+ void* caddr, size_t sizet, int msynct)
+{
+ return msync ((T_implPtr) caddr, (T_implLen) sizet, (T_msync) msynct);
+}
+#endif
+
+using gnu::gcj::RawData;
+using java::io::IOException;
+using java::nio::MappedByteBufferImpl;
+using java::io::InterruptedIOException;
+using java::io::FileNotFoundException;
+using java::lang::ArrayIndexOutOfBoundsException;
+using gnu::java::nio::channels::FileChannelImpl;
+
+#define NO_FSYNC_MESSAGE "sync unsupported"
+
+void
+FileChannelImpl::init(void)
+{
+ in = new FileChannelImpl((jint) 0, FileChannelImpl::READ);
+ out = new FileChannelImpl((jint) 1, FileChannelImpl::WRITE);
+ err = new FileChannelImpl((jint) 2, FileChannelImpl::WRITE);
+}
+
+#if 0
+jboolean
+FileChannelImpl::valid (void)
+{
+ struct stat sb;
+ return fd >= 0 && ::fstat (fd, &sb) == 0;
+}
+
+void
+FileChannelImpl::sync (void)
+{
+ // Some files don't support fsync. We don't bother reporting these
+ // as errors.
+#ifdef HAVE_FSYNC
+ if (::fsync (fd) && errno != EROFS && errno != EINVAL)
+ throw new SyncFailedException (JvNewStringLatin1 (strerror (errno)));
+#else
+ throw new SyncFailedException (JvNewStringLatin1 (NO_FSYNC_MESSAGE));
+#endif
+}
+#endif
+
+jint
+FileChannelImpl::open (jstring path, jint jflags)
+{
+ fd = -1;
+ char *buf = (char *) _Jv_AllocBytes (_Jv_GetStringUTFLength (path) + 1);
+ jsize total = JvGetStringUTFRegion (path, 0, path->length(), buf);
+ buf[total] = '\0';
+ int flags = 0;
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
+
+ JvAssert ((jflags & READ) || (jflags & WRITE));
+ int mode = 0666;
+ if ((jflags & READ) && (jflags & WRITE))
+ flags |= O_RDWR | O_CREAT;
+ else if ((jflags & READ))
+ flags |= O_RDONLY;
+ else
+ {
+ flags |= O_WRONLY | O_CREAT;
+ if ((jflags & APPEND))
+ flags |= O_APPEND;
+ else
+ flags |= O_TRUNC;
+
+ if ((jflags & EXCL))
+ {
+ flags |= O_EXCL;
+ // In this case we are making a temp file.
+ mode = 0600;
+ }
+ }
+
+ if ((jflags & SYNC))
+ flags |= O_SYNC;
+
+ if ((jflags & DSYNC))
+ flags |= O_DSYNC;
+
+ int fd = ::open (buf, flags, mode);
+ if (fd == -1 && errno == EMFILE)
+ {
+ // Because finalize () calls close () we might be able to continue.
+ ::java::lang::System::gc ();
+ ::java::lang::System::runFinalization ();
+ fd = ::open (buf, flags, mode);
+ }
+ if (fd == -1)
+ {
+ // We choose the formatting here for JDK compatibility, believe
+ // it or not.
+ ::java::lang::StringBuffer *msg = new ::java::lang::StringBuffer (path);
+ msg->append (JvNewStringUTF (" ("));
+ msg->append (JvNewStringUTF (strerror (errno)));
+ msg->append (JvNewStringUTF (")"));
+ throw new ::java::io::FileNotFoundException (msg->toString ());
+ }
+
+ return fd;
+}
+
+void
+FileChannelImpl::write (jint b)
+{
+ jbyte d = (jbyte) b;
+ int r = 0;
+ while (r != 1)
+ {
+ r = ::write (fd, &d, 1);
+ if (r == -1)
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ ::java::io::InterruptedIOException *iioe
+ = new ::java::io::InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = r == -1 ? 0 : r;
+ throw iioe;
+ }
+ if (errno != EINTR)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ }
+ pos++;
+}
+
+void
+FileChannelImpl::write (jbyteArray b, jint offset, jint len)
+{
+ if (! b)
+ throw new ::java::lang::NullPointerException;
+ if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
+ throw new ArrayIndexOutOfBoundsException;
+ jbyte *bytes = elements (b) + offset;
+
+ int written = 0;
+ while (len > 0)
+ {
+ int r = ::write (fd, bytes, len);
+ if (r == -1)
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = written;
+ throw iioe;
+ }
+ if (errno != EINTR)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ continue;
+ }
+
+ written += r;
+ len -= r;
+ bytes += r;
+ pos += r;
+ }
+}
+
+void
+FileChannelImpl::implCloseChannel (void)
+{
+ jint save = fd;
+ fd = -1;
+ if (::close (save))
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+}
+
+void
+FileChannelImpl::implTruncate (jlong size)
+{
+ struct stat sb;
+
+#ifdef HAVE_FTRUNCATE
+ if (::fstat (fd, &sb))
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+
+ if ((jlong) sb.st_size == size)
+ return;
+
+ // If the file is too short, we extend it. We can't rely on
+ // ftruncate() extending the file. So we lseek() to 1 byte less
+ // than we want, and then we write a single byte at the end.
+ if ((jlong) sb.st_size < size)
+ {
+ if (::lseek (fd, (off_t) (size - 1), SEEK_SET) == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ char out = '\0';
+ int r = ::write (fd, &out, 1);
+ if (r <= 0 || ::lseek (fd, pos, SEEK_SET) == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ else
+ {
+ if (::ftruncate (fd, (off_t) size))
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ if (pos > size
+ && ::lseek (fd, (off_t) size, SEEK_SET) == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ pos = size;
+ }
+#else /* HAVE_FTRUNCATE */
+ throw new IOException (JvNewStringLatin1 ("FileDescriptor.setLength not implemented"));
+#endif /* HAVE_FTRUNCATE */
+}
+
+void
+FileChannelImpl::seek (jlong newPos)
+{
+ off_t r = ::lseek (fd, (off_t) newPos, SEEK_SET);
+ if (r == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ pos = r;
+}
+
+jlong
+FileChannelImpl::size (void)
+{
+ struct stat sb;
+ if (::fstat (fd, &sb))
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ return sb.st_size;
+}
+
+jlong
+FileChannelImpl::implPosition (void)
+{
+ return pos;
+}
+
+jint
+FileChannelImpl::read (void)
+{
+ jbyte b;
+ int r;
+ do
+ {
+ r = ::read (fd, &b, 1);
+ if (r == 0)
+ return -1;
+ if (r == -1)
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = r == -1 ? 0 : r;
+ throw iioe;
+ }
+ if (errno != EINTR)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ }
+ while (r != 1);
+ pos++;
+ return b & 0xFF;
+}
+
+jint
+FileChannelImpl::read (jbyteArray buffer, jint offset, jint count)
+{
+ if (! buffer)
+ throw new ::java::lang::NullPointerException;
+ jsize bsize = JvGetArrayLength (buffer);
+ if (offset < 0 || count < 0 || offset + count > bsize)
+ throw new ::java::lang::ArrayIndexOutOfBoundsException;
+
+ // Must return 0 if an attempt is made to read 0 bytes.
+ if (count == 0)
+ return 0;
+
+ jbyte *bytes = elements (buffer) + offset;
+ int r;
+ do
+ {
+ r = ::read (fd, bytes, count);
+ if (r == 0)
+ return -1;
+ if (r == -1)
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = r == -1 ? 0 : r;
+ throw iioe;
+ }
+ if (errno != EINTR)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ }
+ while (r <= 0);
+ pos += r;
+ return r;
+}
+
+jint
+FileChannelImpl::available (void)
+{
+#if defined (FIONREAD) || defined (HAVE_SELECT) || defined (HAVE_FSTAT)
+ int num = 0;
+ int r = 0;
+ bool num_set = false;
+
+#if defined (FIONREAD)
+ r = ::ioctl (fd, FIONREAD, &num);
+ if (r == -1 && errno == ENOTTY)
+ {
+ // If the ioctl doesn't work, we don't care.
+ r = 0;
+ num = 0;
+ }
+ else
+ num_set = true;
+#elif defined (HAVE_SELECT)
+ if (fd < 0)
+ {
+ errno = EBADF;
+ r = -1;
+ }
+#endif
+
+ if (r == -1)
+ {
+ posix_error:
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+
+ // If we didn't get anything, and we have fstat, then see if see if
+ // we're reading a regular file. On many systems, FIONREAD does not
+ // work on regular files; select() likewise returns a useless
+ // result. This is run incorrectly when FIONREAD does work on
+ // regular files and we are at the end of the file. However, this
+ // case probably isn't very important.
+#if defined (HAVE_FSTAT)
+ if (! num_set)
+ {
+ struct stat sb;
+ off_t where = 0;
+ if (fstat (fd, &sb) != -1
+ && S_ISREG (sb.st_mode)
+ && (where = lseek (fd, 0, SEEK_CUR)) != (off_t) -1)
+ {
+ num = (int) (sb.st_size - where);
+ num_set = true;
+ }
+ }
+#endif /* HAVE_FSTAT */
+
+#if defined (HAVE_SELECT)
+ if (! num_set)
+ {
+ fd_set rd;
+ FD_ZERO (&rd);
+ FD_SET (fd, &rd);
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ r = _Jv_select (fd + 1, &rd, NULL, NULL, &tv);
+ if (r == -1)
+ goto posix_error;
+ num = r == 0 ? 0 : 1;
+ }
+#endif /* HAVE_SELECT */
+
+ return (jint) num;
+#else
+ return 0;
+#endif
+}
+
+jboolean
+FileChannelImpl::lock
+(jlong pos, jlong len, jboolean shared, jboolean wait)
+{
+ struct flock lockdata;
+
+ lockdata.l_type = shared ? F_RDLCK : F_WRLCK;
+ lockdata.l_whence = SEEK_SET;
+ lockdata.l_start = pos;
+ lockdata.l_len = len;
+
+ if (::fcntl (fd, wait ? F_SETLKW : F_SETLK, &lockdata) == -1)
+ {
+ if (! wait && (errno == EACCES || errno == EAGAIN))
+ return false;
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ return true;
+}
+
+void
+FileChannelImpl::unlock (jlong pos, jlong len)
+{
+ struct flock lockdata;
+
+ lockdata.l_type = F_UNLCK;
+ lockdata.l_whence = SEEK_SET;
+ lockdata.l_start = pos;
+ lockdata.l_len = len;
+
+ if (::fcntl (fd, F_SETLK, &lockdata) == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+}
+
+java::nio::MappedByteBuffer *
+FileChannelImpl::mapImpl (jchar mmode, jlong position, jint size)
+{
+#if defined(HAVE_MMAP)
+ int prot, flags;
+ if (mmode == 'r')
+ {
+ prot = PROT_READ;
+ flags = MAP_PRIVATE;
+ }
+ else
+ {
+ prot = PROT_READ|PROT_WRITE;
+ flags = mmode == '+' ? MAP_SHARED : MAP_PRIVATE;
+
+ // If the file is too short, we must extend it. While using
+ // ftruncate() to extend a file is not portable in general, it
+ // should work on all systems where you can mmap() a file.
+ struct stat st;
+ if (fstat (fd, &st) == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ if (position + size > st.st_size)
+ {
+ if (ftruncate (fd, position + size) == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ }
+ jint page_size = ::getpagesize();
+ jint offset = position & ~(page_size-1);
+ jint align = position - offset;
+ void* ptr = ::mmap(NULL, size + align, prot, flags, fd, offset);
+ MappedByteBufferImpl *buf
+ = new MappedByteBufferImpl ((RawData *) ((char *) ptr + align),
+ size, mmode == 'r');
+ if (ptr == (void *) MAP_FAILED)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ buf->implPtr = reinterpret_cast<RawData*> (ptr);
+ buf->implLen = size+align;
+ return buf;
+#else
+ throw new IOException (JvNewStringUTF ("mmap not implemented"));
+#endif
+}
+
+void
+MappedByteBufferImpl::unmapImpl ()
+{
+#if defined(HAVE_MMAP)
+ munmap_adaptor(munmap, implPtr, implLen);
+#endif
+}
+
+void
+MappedByteBufferImpl::loadImpl ()
+{
+}
+
+jboolean
+MappedByteBufferImpl::isLoadedImpl ()
+{
+ return true;
+}
+
+void
+MappedByteBufferImpl::forceImpl ()
+{
+#if defined(HAVE_MMAP)
+ ::msync_adaptor(msync, implPtr, implLen, MS_SYNC);
+#endif
+}