diff options
Diffstat (limited to 'native/jni/java-nio/gnu_java_nio_VMChannel.c')
-rw-r--r-- | native/jni/java-nio/gnu_java_nio_VMChannel.c | 1345 |
1 files changed, 1308 insertions, 37 deletions
diff --git a/native/jni/java-nio/gnu_java_nio_VMChannel.c b/native/jni/java-nio/gnu_java_nio_VMChannel.c index 5571bed27..b4f444361 100644 --- a/native/jni/java-nio/gnu_java_nio_VMChannel.c +++ b/native/jni/java-nio/gnu_java_nio_VMChannel.c @@ -36,25 +36,43 @@ obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ +#ifdef HAVE_CONFIG_H #include <config.h> +#endif + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/uio.h> + +#include <netinet/in.h> + #include <stdlib.h> #include <errno.h> #include <unistd.h> -#include <sys/uio.h> #include <string.h> #include <jni.h> #include <jcl.h> #include "gnu_java_nio_VMChannel.h" +#include "javanio.h" #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif /* HAVE_FCNTL_H */ #define IO_EXCEPTION "java/io/IOException" +#define INTERRUPTED_IO_EXCEPTION "java/io/InterruptedIOException" #define NON_READABLE_CHANNEL_EXCEPTION "java/nio/channels/NonReadableChannelException" #define NON_WRITABLE_CHANNEL_EXCEPTION "java/nio/channels/NonWritableChannelException" +#define SOCKET_TIMEOUT_EXCEPTION "java/net/SocketTimeoutException" + +/* Align a value up or down to a multiple of the pagesize. */ +#define ALIGN_DOWN(p,s) ((p) - ((p) % (s))) +#define ALIGN_UP(p,s) ((p) + ((s) - ((p) % (s)))) /* * Limit to maximum of 16 buffers @@ -66,7 +84,7 @@ extern "C" { #endif -enum JCL_buffer_type { DIRECT, ARRAY, UNKNOWN }; +enum JCL_buffer_type { DIRECT, HEAP, ARRAY, UNKNOWN }; struct JCL_buffer { @@ -98,6 +116,7 @@ get_method_id(JNIEnv *env, jclass clazz, const char *name, const char *sig) { jmethodID mid = (*env)->GetMethodID(env, clazz, name, sig); +/* NIODBG("name: %s; sig: %s", name, sig); */ if (mid == NULL) { JCL_ThrowException(env, "java/lang/InternalError", name); @@ -107,18 +126,19 @@ get_method_id(JNIEnv *env, jclass clazz, const char *name, return mid; } -void +inline void JCL_print_buffer(JNIEnv *env __attribute__((__unused__)), struct JCL_buffer *buf) { - fprintf(stdout, "Buffer - type: %d, ptr: %p\n", buf->type, buf->ptr); - fflush(stdout); + fprintf (stderr, "Buffer - type: %d, ptr: %p\n", buf->type, buf->ptr); } int JCL_init_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf) { - jobject address = (*env)->GetObjectField(env, bbuf, address_fid); + void *addr = (*env)->GetDirectBufferAddress (env, bbuf); + +/* NIODBG("buf: %p; bbuf: %p; addr: %p", (void *) buf, bbuf, addr); */ buf->position = (*env)->CallIntMethod(env, bbuf, get_position_mid); buf->limit = (*env)->CallIntMethod(env, bbuf, get_limit_mid); @@ -126,11 +146,10 @@ JCL_init_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf) buf->count = 0; buf->type = UNKNOWN; - if (address != NULL) + if (addr != NULL) { - buf->ptr = (jbyte *) JCL_GetRawData(env, address); + buf->ptr = (jbyte *) addr; buf->type = DIRECT; - (*env)->DeleteLocalRef(env, address); } else { @@ -148,7 +167,12 @@ JCL_init_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf) } else { - return -1; + jobject address = (*env)->GetObjectField (env, bbuf, address_fid); + if (address == NULL) + return -1; /* XXX handle non-array, non-native buffers? */ + buf->ptr = (jbyte *) JCL_GetRawData(env, address); + buf->type = HEAP; + (*env)->DeleteLocalRef(env, address); } } @@ -160,6 +184,8 @@ JCL_release_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf, jint action) { jbyteArray arr; + +/* NIODBG("buf: %p; bbuf: %p; action: %x", (void *) buf, bbuf, action); */ /* Set the position to the appropriate value */ if (buf->count > 0) @@ -173,6 +199,7 @@ JCL_release_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf, switch (buf->type) { case DIRECT: + case HEAP: break; case ARRAY: arr = (*env)->CallObjectMethod(env, bbuf, array_mid); @@ -194,6 +221,9 @@ JCL_cleanup_buffers(JNIEnv *env, jlong num_bytes) { jint i; + +/* NIODBG("bi_list: %p; vec_len: %d; bbufs: %p; offset: %d; num_bytes: %lld", */ +/* (void *) bi_list, vec_len, bbufs, offset, num_bytes); */ /* Update all of the bbufs with the approriate information */ for (i = 0; i < vec_len; i++) @@ -217,13 +247,57 @@ JCL_cleanup_buffers(JNIEnv *env, } +/* + * Class: gnu_java_nio_VMChannel + * Method: stdin_fd + * Signature: ()I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_stdin_1fd (JNIEnv *env __attribute__((unused)), + jclass c __attribute__((unused))) +{ +/* NIODBG("%d", fileno (stdin)); */ + return fileno (stdin); +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: stdout_fd + * Signature: ()I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_stdout_1fd (JNIEnv *env __attribute__((unused)), + jclass c __attribute__((unused))) +{ +/* NIODBG("%d", fileno (stdout)); */ + return fileno (stdout); +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: stderr_fd + * Signature: ()I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_stderr_1fd (JNIEnv *env __attribute__((unused)), + jclass c __attribute__((unused))) +{ +/* NIODBG("%d", fileno (stderr)); */ + return fileno (stderr); +} + + JNIEXPORT void JNICALL Java_gnu_java_nio_VMChannel_initIDs (JNIEnv *env, jclass clazz __attribute__ ((__unused__))) { jclass bufferClass = JCL_FindClass(env, "java/nio/Buffer"); jclass byteBufferClass = JCL_FindClass(env, "java/nio/ByteBuffer"); - + +/* NIODBG("%s", "..."); */ + address_fid = (*env)->GetFieldID(env, bufferClass, "address", "Lgnu/classpath/Pointer;"); if (address_fid == NULL) @@ -252,6 +326,8 @@ Java_gnu_java_nio_VMChannel_setBlocking (JNIEnv *env, { int opts; +/* NIODBG("fd: %d; blocking: %d", fd, blocking); */ + opts = fcntl(fd, F_GETFL); if (opts < 0) { @@ -260,10 +336,10 @@ Java_gnu_java_nio_VMChannel_setBlocking (JNIEnv *env, return; } - if (blocking) - opts |= O_NONBLOCK; - else + if (blocking == JNI_TRUE) opts &= ~(O_NONBLOCK); + else + opts |= O_NONBLOCK; opts = fcntl(fd, F_SETFL, opts); @@ -277,14 +353,17 @@ Java_gnu_java_nio_VMChannel_setBlocking (JNIEnv *env, JNIEXPORT jint JNICALL -Java_gnu_java_nio_VMChannel_read (JNIEnv *env, - jobject o __attribute__ ((__unused__)), - jint fd, - jobject bbuf) +Java_gnu_java_nio_VMChannel_read__ILjava_nio_ByteBuffer_2 (JNIEnv *env, + jobject o __attribute__ ((__unused__)), + jint fd, + jobject bbuf) { +#ifdef HAVE_READ jint len; ssize_t result; struct JCL_buffer buf; + +/* NIODBG("fd: %d; bbuf: %p", fd, bbuf); */ if (JCL_init_buffer(env, &buf, bbuf) < 0) { @@ -292,14 +371,22 @@ Java_gnu_java_nio_VMChannel_read (JNIEnv *env, JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed"); return -1; } - + len = buf.limit - buf.position; + + if (len == 0) + { + JCL_release_buffer (env, &buf, bbuf, JNI_ABORT); + return 0; + } - result = read(fd, &(buf.ptr[buf.position + buf.offset]), len); - buf.count = result; + result = cpnio_read (fd, &(buf.ptr[buf.position + buf.offset]), len); if (result == 0) - result = -1; /* End Of File */ + { + result = -1; + buf.count = 0; + } else if (result == -1) { buf.count = 0; @@ -311,7 +398,13 @@ Java_gnu_java_nio_VMChannel_read (JNIEnv *env, JCL_ThrowException (env, NON_READABLE_CHANNEL_EXCEPTION, strerror(errno)); return -1; - } + } + else if (EINTR == errno) /* read interrupted */ + { + JCL_release_buffer(env, &buf, bbuf, JNI_ABORT); + JCL_ThrowException(env, INTERRUPTED_IO_EXCEPTION, strerror (errno)); + return -1; + } else { JCL_release_buffer(env, &buf, bbuf, JNI_ABORT); @@ -320,21 +413,31 @@ Java_gnu_java_nio_VMChannel_read (JNIEnv *env, } } else - - JCL_release_buffer(env, &buf, bbuf, JNI_COMMIT); + buf.count = result; + + JCL_release_buffer(env, &buf, bbuf, 0); return result; +#else + (void) fd; + (void) bbuf; + JCL_ThrowException (env, IO_EXCEPTION, "read not supported"); + return -1; +#endif /* HAVE_READ */ } JNIEXPORT jint JNICALL -Java_gnu_java_nio_VMChannel_write (JNIEnv *env, - jobject o __attribute__ ((__unused__)), - jint fd, - jobject bbuf) +Java_gnu_java_nio_VMChannel_write__ILjava_nio_ByteBuffer_2 (JNIEnv *env, + jobject o __attribute__ ((__unused__)), + jint fd, + jobject bbuf) { +#ifdef HAVE_WRITE jint len; ssize_t result; struct JCL_buffer buf; + +/* NIODBG("fd: %d; bbuf: %p", fd, bbuf); */ if (JCL_init_buffer(env, &buf, bbuf) < 0) { @@ -342,16 +445,24 @@ Java_gnu_java_nio_VMChannel_write (JNIEnv *env, JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed"); return -1; } - + len = buf.limit - buf.position; + + if (len == 0) + { + JCL_release_buffer (env, &buf, bbuf, JNI_ABORT); + return 0; + } - result = write(fd, &(buf.ptr[buf.position + buf.offset]), len); + result = cpnio_write (fd, &(buf.ptr[buf.position + buf.offset]), len); buf.count = result; - + if (result == -1) { if (errno == EAGAIN) /* Non-blocking */ + { result = 0; + } else { JCL_release_buffer(env, &buf, bbuf, JNI_ABORT); @@ -362,7 +473,13 @@ Java_gnu_java_nio_VMChannel_write (JNIEnv *env, JCL_release_buffer(env, &buf, bbuf, JNI_ABORT); - return result; + return result; +#else + (void) fd; + (void) bbuf; + JCL_ThrowException (env, IO_EXCEPTION, "write not supported"); + return -1; +#endif /* HAVE_WRITE */ } @@ -390,7 +507,10 @@ Java_gnu_java_nio_VMChannel_readScattering (JNIEnv *env, struct JCL_buffer bi_list[JCL_IOV_MAX]; ssize_t result; jint vec_len = length < JCL_IOV_MAX ? length : JCL_IOV_MAX; - jlong bytes_read = 0; + jlong bytes_read = 0; + +/* NIODBG("fd: %d; bbufs: %p; offset: %d; length: %d", */ +/* fd, bbufs, offset, length); */ /* Build the vector of buffers to read into */ for (i = 0; i < vec_len; i++) @@ -401,7 +521,9 @@ Java_gnu_java_nio_VMChannel_readScattering (JNIEnv *env, buf = &bi_list[i]; bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i); - JCL_init_buffer(env, buf, bbuf); + JCL_init_buffer(env, buf, bbuf); + +/* JCL_print_buffer (env, buf); */ buffers[i].iov_base = &(buf->ptr[buf->position + buf->offset]); buffers[i].iov_len = buf->limit - buf->position; @@ -409,7 +531,7 @@ Java_gnu_java_nio_VMChannel_readScattering (JNIEnv *env, } /* Work the scattering magic */ - result = readv(fd, buffers, vec_len); + result = cpnio_readv (fd, buffers, vec_len); bytes_read = (jlong) result; /* Handle the response */ @@ -442,6 +564,7 @@ Java_gnu_java_nio_VMChannel_readScattering (JNIEnv *env, return (jlong) result; } + /* * Implementation of a gathering write. Will use the appropriate * vector based read call (currently readv on Linux). @@ -468,6 +591,8 @@ Java_gnu_java_nio_VMChannel_writeGathering (JNIEnv *env, jint vec_len = length < JCL_IOV_MAX ? length : JCL_IOV_MAX; jlong bytes_written; +/* NIODBG("fd: %d; bbufs: %p; offset: %d; length: %d", */ +/* fd, bbufs, offset, length); */ /* Build the vector of buffers to read into */ for (i = 0; i < vec_len; i++) @@ -480,13 +605,15 @@ Java_gnu_java_nio_VMChannel_writeGathering (JNIEnv *env, JCL_init_buffer(env, buf, bbuf); +/* JCL_print_buffer(env, buf); */ + buffers[i].iov_base = &(buf->ptr[buf->position + buf->offset]); buffers[i].iov_len = buf->limit - buf->position; (*env)->DeleteLocalRef(env, bbuf); } /* Work the gathering magic */ - result = writev(fd, buffers, vec_len); + result = cpnio_writev (fd, buffers, vec_len); bytes_written = (jlong) result; if (result < 0) @@ -518,6 +645,1150 @@ Java_gnu_java_nio_VMChannel_writeGathering (JNIEnv *env, } +/* + * Class: gnu_java_nio_VMChannel + * Method: receive + * Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_receive (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd, jobject dst, jobject addrPort) +{ +#ifdef HAVE_RECVFROM + char *addrPortPtr = (*env)->GetDirectBufferAddress (env, addrPort); + struct JCL_buffer buf; +#ifdef HAVE_INET6 + struct sockaddr_in6 sock_storage; + struct sockaddr_in6 *sock6; + socklen_t slen = sizeof (struct sockaddr_in6); +#else + struct sockaddr_in sock_storage; + socklen_t slen = sizeof (struct sockaddr_in); +#endif /* HAVE_INET6 */ + struct sockaddr *sockaddr = (struct sockaddr *) &sock_storage; + struct sockaddr_in *sock4; + int ret; + jint result = -1; + + if (JCL_init_buffer (env, &buf, dst) == -1) + JCL_ThrowException (env, IO_EXCEPTION, "loading buffer failed"); + + ret = cpnio_recvfrom (fd, &(buf.ptr[buf.position + buf.offset]), + buf.limit - buf.position, MSG_WAITALL, + sockaddr, &slen); + + if (-1 == ret) + { + JCL_release_buffer (env, &buf, dst, JNI_ABORT); + if (EINTR == errno) + JCL_ThrowException (env, "java/io/InterruptedIOException", strerror (errno)); + else if (EAGAIN == errno) + { + /* If the socket is in blocking mode, our timeout expired. */ + int val = fcntl (fd, F_GETFL, 0); + if (val == -1) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + else if ((val & O_NONBLOCK) == 0) + JCL_ThrowException (env, "java/net/SocketTimeoutException", + "read timed out"); + } + else + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return 0; + } + + if (sockaddr->sa_family == AF_INET) + { + sock4 = (struct sockaddr_in *) sockaddr; + memcpy (addrPortPtr, &(sock4->sin_addr.s_addr), 4); + ;memcpy (addrPortPtr + 4, &(sock4->sin_port), 2); + result = 4; + } +#ifdef HAVE_INET6 + else if (sockaddr->sa_family == AF_INET6) + { + sock6 = (struct sockaddr_in6 *) sockaddr; + memcpy (addrPortPtr, &(sock6->sin6_addr.s6_addr), 16); + memcpy (addrPortPtr + 16, &(sock6->sin6_port), 2); + result = 16; + } +#endif /* HAVE_INET6 */ + else if (ret == 0) + { + result = 0; + } + else + { + JCL_ThrowException (env, "java/net/SocketException", + "unsupported address type returned"); + } + + buf.count += ret; + JCL_release_buffer (env, &buf, dst, 0); + return result; +#else + (void) fd; + (void) dst; + (void) addrPort; + JCL_ThrowException (env, IO_EXCEPTION, "recvfrom not supported"); +#endif /* HAVE_RECVFROM */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: send + * Signature: (Ljava/nio/ByteBuffer;[BI)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_send (JNIEnv *env, + jclass c __attribute__((unused)), + int fd, jobject src, jbyteArray addr, jint port) +{ +#ifdef HAVE_SENDTO + struct sockaddr_in sockaddr; + jbyte *elems; + struct JCL_buffer buf; + int ret; + +/* NIODBG("fd: %d; src: %p; addr: %p; port: %d", */ +/* fd, src, addr, port); */ + + if (JCL_init_buffer (env, &buf, src) == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, "loading buffer failed"); + return -1; + } + +/* JCL_print_buffer (env, &buf); */ + + elems = (*env)->GetByteArrayElements (env, addr, NULL); + + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = *((uint32_t *) elems); + sockaddr.sin_port = htons (port); + + do + { + ret = cpnio_sendto (fd, &(buf.ptr[buf.position + buf.offset]), + buf.limit - buf.position, + 0, (const struct sockaddr *) &sockaddr, + sizeof (struct sockaddr_in)); + } + while (-1 == ret && EINTR == errno); + + (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT); + + if (-1 == ret) + { + if (errno != EAGAIN) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + JCL_release_buffer (env, &buf, src, JNI_ABORT); + return 0; + } + + buf.count += ret; + JCL_release_buffer (env, &buf, src, JNI_ABORT); + return ret; +#else + (void) fd; + (void) src; + (void) addr; + (void) port; +#endif /* HAVE_SENDTO */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: send6 + * Signature: (Ljava/nio/ByteBuffer;[BI)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_send6 (JNIEnv *env, + jclass c __attribute__((unused)), + int fd, jobject src, jbyteArray addr, jint port) +{ +#if defined(HAVE_SENDTO) && defined(HAVE_INET6) + struct sockaddr_in6 sockaddr; + jbyte *elems; + struct JCL_buffer buf; + int ret; + +/* NIODBG("fd: %d; src: %p; addr: %p; port: %d", */ +/* fd, src, addr, port); */ + + if (JCL_init_buffer (env, &buf, src) == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, "loading buffer failed"); + return -1; + } + +/* JCL_print_buffer (env, &buf); */ + + elems = (*env)->GetByteArrayElements (env, addr, NULL); + + sockaddr.sin6_family = AF_INET6; + memcpy (&sockaddr.sin6_addr.s6_addr, elems, 16); + sockaddr.sin6_port = htons (port); + + do + { + ret = cpnio_sendto (fd, (const void *) (buf.ptr + buf.offset), + buf.limit - buf.position, + 0, (const struct sockaddr *) &sockaddr, + sizeof (struct sockaddr_in6)); + } + while (-1 == ret && EINTR == errno); + + (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT); + + if (-1 == ret) + { + if (errno != EAGAIN) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + JCL_release_buffer (env, &buf, src, JNI_ABORT); + return 0; + } + + buf.count += ret; + JCL_release_buffer (env, &buf, src, JNI_ABORT); + return ret; +#else + (void) fd; + (void) src; + (void) addr; + (void) port; + JCL_ThrowException (env, IO_EXCEPTION, "IPv6 sendto not supported"); + return -1; +#endif /* HAVE_SENDTO && HAVE_INET6 */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: read + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_read__I (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd) +{ +#ifdef HAVE_READ + char in; + int ret; + +/* NIODBG("fd: %d", fd); */ + + ret = cpnio_read (fd, &in, 1); + if (-1 == ret) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + if (0 == ret) + return -1; + + return (in & 0xFF); +#else + (void) fd; + JCL_ThrowException (env, IO_EXCEPTION, "read not supported"); +#endif /* HAVE_READ */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: write + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_gnu_java_nio_VMChannel_write__II (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd, jint data) +{ +#ifdef HAVE_WRITE + char out = (char) data; + int ret; + +/* NIODBG("fd: %d; data: %d", fd, data); */ + + ret = cpnio_write (fd, &out, 1); + + if (-1 == ret) + JCL_ThrowException(env, IO_EXCEPTION, strerror (errno)); +#else + (void) fd; + (void) data; + JCL_ThrowException (env, IO_EXCEPTION, "write not supported"); +#endif /* HAVE_WRITE */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: socket + * Signature: (Z)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_socket (JNIEnv *env, jclass clazz __attribute__((unused)), + jboolean stream) +{ +#ifdef HAVE_SOCKET + int ret; + + do + { + ret = cpnio_socket (AF_INET, stream ? SOCK_STREAM : SOCK_DGRAM, 0); + } + while (-1 == ret && EINTR == errno); + + if (ret == -1) + JCL_ThrowException (env, "java/net/SocketException", strerror (errno)); +/* NIODBG("created socket %d", ret); */ + + return ret; +#else + (void) stream; + JCL_ThrowException (env, IO_EXCEPTION, "socket not supported"); + return -1; +#endif /* HAVE_SOCKET */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: connect + * Signature: (I[BI)Z + */ +JNIEXPORT jboolean JNICALL +Java_gnu_java_nio_VMChannel_connect (JNIEnv *env, jclass clazz __attribute__((unused)), + jint fd, jbyteArray addr, jint port, jint timeout) +{ +#ifdef HAVE_CONNECT + struct sockaddr_in sockaddr; + struct timeval timeo; + int origflags = 0, flags; + jbyte *addr_elems; + int ret; + + if ((*env)->GetArrayLength (env, addr) != 4) + { + JCL_ThrowException (env, "java/io/IOException", "expecting 4-byte address"); + return JNI_FALSE; + } + + if (timeout > 0) + { + timeo.tv_sec = timeout / 1000; + timeo.tv_usec = (timeout % 1000) * 1000; + origflags = fcntl (fd, F_GETFL, 0); + if (origflags == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + /* Set nonblocking mode, if not already set. */ + if (!(origflags & O_NONBLOCK)) + { + flags = origflags | O_NONBLOCK; + if (fcntl (fd, F_SETFL, flags) == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + } + } + + addr_elems = (*env)->GetByteArrayElements (env, addr, NULL); + + memset (&sockaddr, 0, sizeof (struct sockaddr_in)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons (port); + sockaddr.sin_addr.s_addr = *((uint32_t *) addr_elems); + + + ret = cpnio_connect (fd, (struct sockaddr *) &sockaddr, + sizeof (struct sockaddr_in)); + + (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT); + + /* If a timeout was specified, select on the file descriptor with + the timeout. */ + if (timeout > 0 && ret == -1) + { + /* Reset the non-blocking flag, if needed. */ + if (!(origflags & O_NONBLOCK)) + { + if (fcntl (fd, F_SETFL, origflags) == -1) + { + /* oops */ + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + } + if (EINPROGRESS == errno) + { + fd_set wrfds; + FD_SET(fd, &wrfds); + ret = cpnio_select (fd + 1, NULL, &wrfds, NULL, &timeo); + if (ret == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + if (ret == 0) /* connect timed out */ + { + JCL_ThrowException (env, SOCKET_TIMEOUT_EXCEPTION, + "connect timed out"); + return JNI_FALSE; + } + return JNI_TRUE; /* Connected! */ + } + else if (ECONNREFUSED == errno) + { + JCL_ThrowException (env, "java/net/ConnectException", + strerror (errno)); + return JNI_FALSE; + } + else + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + } + + if (ret == -1) + { + if (EINPROGRESS == errno) + return JNI_FALSE; + else if (ECONNREFUSED == errno) + { + JCL_ThrowException (env, "java/net/ConnectException", + strerror (errno)); + return JNI_FALSE; + } + else + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + } + + return JNI_TRUE; +#else + (void) fd; + (void) addr; + (void) port; + (void) timeout; + JCL_ThrowException (env, IO_EXCEPTION, "connect not supported"); + return JNI_FALSE; +#endif /* HAVE_CONNECT */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: connect6 + * Signature: (I[BI)Z + */ +JNIEXPORT jboolean JNICALL +Java_gnu_java_nio_VMChannel_connect6 (JNIEnv *env, jclass clazz __attribute__((unused)), + jint fd, jbyteArray addr, jint port, int timeout) +{ +#if defined(HAVE_CONNECT) && defined(HAVE_INET6) + struct sockaddr_in6 sockaddr; + struct timeval timeo; + int flags, origflags = 0; + jbyte *addr_elems; + int ret; + + if (timeout > 0) + { + timeo.tv_sec = timeout / 1000; + timeo.tv_usec = (timeout % 1000) * 1000; + origflags = fcntl (fd, F_GETFL, 0); + if (origflags == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + /* Set nonblocking mode, if not already set. */ + if (!(origflags & O_NONBLOCK)) + { + flags = origflags | O_NONBLOCK; + if (fcntl (fd, F_SETFL, flags) == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + } + } + + addr_elems = (*env)->GetByteArrayElements (env, addr, NULL); + + memset (&sockaddr, 0, sizeof (struct sockaddr_in6)); + sockaddr.sin6_family = AF_INET6; + sockaddr.sin6_port = htons (port); + memcpy (&sockaddr.sin6_addr.s6_addr, addr_elems, 16); + + ret = cpnio_connect (fd, (struct sockaddr *) &sockaddr, + sizeof (struct sockaddr_in6)); + + (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT); + + /* If a timeout was specified, select on the file descriptor with + the timeout. */ + if (timeout > 0 && ret == -1) + { + /* Reset the non-blocking flag, if needed. */ + if (!(origflags & O_NONBLOCK)) + { + if (fcntl (fd, F_SETFL, origflags) == -1) + { + /* oops */ + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + } + if (EINPROGRESS == errno) + { + fd_set wrfds; + FD_SET(fd, &wrfds); + ret = cpnio_select (fd + 1, NULL, &wrfds, NULL, &timeo); + if (ret == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + if (ret == 0) /* connect timed out */ + { + JCL_ThrowException (env, SOCKET_TIMEOUT_EXCEPTION, + "connect timed out"); + return JNI_FALSE; + } + return JNI_TRUE; /* Connected! */ + } + else if (ECONNREFUSED == errno) + { + JCL_ThrowException (env, "java/net/ConnectException", + strerror (errno)); + return JNI_FALSE; + } + else + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + } + + if (ret == -1) + { + if (EAGAIN == errno) + return JNI_FALSE; + else if (ECONNREFUSED == errno) + { + JCL_ThrowException (env, "java/net/ConnectException", + strerror (errno)); + return JNI_FALSE; + } + else + { + JCL_ThrowException (env, "java/io/IOException", strerror (errno)); + return JNI_FALSE; + } + } + + return JNI_TRUE; +#else + (void) fd; + (void) addr; + (void) port; + (void) timeout; + JCL_ThrowException (env, IO_EXCEPTION, "IPv6 connect not supported"); + return JNI_FALSE; +#endif /* HAVE_CONNECT && HAVE_INET6 */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: getsockname + * Signature: (ILjava/nio/ByteBuffer;)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_getsockname (JNIEnv *env, jclass clazz __attribute__((unused)), + jint fd, jobject name) +{ +#ifdef HAVE_GETSOCKNAME +#ifdef HAVE_INET6 + struct sockaddr_in6 *addr6; + struct sockaddr_in6 sock_storage; + socklen_t socklen = sizeof (struct sockaddr_in6); +#else + struct sockaddr_in sock_storage; + socklen_t socklen = sizeof (struct sockaddr_in); +#endif /* HAVE_INET6 */ + + struct sockaddr *sockaddr = (struct sockaddr *) &sock_storage; + struct sockaddr_in *addr4; + int ret; + char *nameptr = (*env)->GetDirectBufferAddress (env, name); + + ret = getsockname (fd, sockaddr, &socklen); + if (ret == -1) + { + JCL_ThrowException (env, "java/net/SocketException", strerror (errno)); + return 0; + } + + if (sockaddr->sa_family == AF_INET) + { + addr4 = (struct sockaddr_in *) sockaddr; + memcpy (nameptr, &(addr4->sin_addr.s_addr), 4); + memcpy (nameptr + 4, &(addr4->sin_port), 2); + return 4; + } + +#ifdef HAVE_INET6 + /* IPv6 */ + if (sockaddr->sa_family == AF_INET6) + { + addr6 = (struct sockaddr_in6 *) sockaddr; + memcpy (nameptr, &(addr6->sin6_addr.s6_addr), 16); + memcpy (nameptr, &(addr6->sin6_port), 2); + return 16; + } +#endif /* HAVE_INET6 */ + JCL_ThrowException (env, IO_EXCEPTION, "unsupported address format"); + return -1; +#else + (void) fd; + (void) name; + JCL_ThrowException (env, IO_EXCEPTION, "getsockname not supported"); + return -1; +#endif /* HAVE_GETSOCKNAME */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: getpeername + * Signature: (ILjava/nio/ByteBuffer;)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_getpeername (JNIEnv *env, jclass clazz __attribute__((unused)), + jint fd, jobject name) +{ +#ifdef HAVE_GETPEERNAME +#ifdef HAVE_INET6 + struct sockaddr_in6 *addr6; + struct sockaddr_in6 sock_storage; + socklen_t socklen = sizeof (struct sockaddr_in6); +#else + struct sockaddr_in sock_storage; + socklen_t socklen = sizeof (struct sockaddr_in); +#endif /* HAVE_INET6 */ + + struct sockaddr *sockaddr = (struct sockaddr *) &sock_storage; + struct sockaddr_in *addr4; + int ret; + char *nameptr = (*env)->GetDirectBufferAddress (env, name); + + ret = getpeername (fd, sockaddr, &socklen); + if (ret == -1) + { + if (ENOTCONN != errno) + JCL_ThrowException (env, "java/net/SocketException", strerror (errno)); + return 0; + } + + if (sockaddr->sa_family == AF_INET) + { + addr4 = (struct sockaddr_in *) sockaddr; + memcpy (nameptr, &(addr4->sin_addr.s_addr), 4); + memcpy (nameptr + 4, &(addr4->sin_port), 2); + return 4; + } +#ifdef HAVE_INET6 + else if (sockaddr->sa_family == AF_INET6) + { + addr6 = (struct sockaddr_in6 *) sockaddr; + memcpy (nameptr, &(addr6->sin6_addr.s6_addr), 16); + memcpy (nameptr, &(addr6->sin6_port), 2); + return 16; + } +#endif /* HAVE_INET6 */ + + JCL_ThrowException (env, "java/net/SocketException", + "unsupported address type"); + return -1; +#else + (void) fd; + (void) name; + JCL_ThrowException (env, IO_EXCEPTION, "getpeername not supported"); + return -1; +#endif /* HAVE_GETPEERNAME */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: accept + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_accept (JNIEnv *env, + jclass clazz __attribute__((unused)), + jint fd) +{ +#ifdef HAVE_ACCEPT + int ret; + +#ifdef HAVE_INET6 + struct sockaddr_in6 addr; + socklen_t alen = sizeof (struct sockaddr_in6); +#else + struct sockaddr_in addr; + socklen_t alen = sizeof (struct sockaddr_in); +#endif /* HAVE_INET6 */ + + do + { + ret = cpnio_accept (fd, (struct sockaddr *) &addr, &alen); + } + while (ret == -1 && EINTR == errno); + + if (ret == -1) + { + if (EWOULDBLOCK != ret && EAGAIN != ret) + JCL_ThrowException (env, "java/net/SocketException", strerror (errno)); + } + + return ret; +#else + (void) fd; + JCL_ThrowException (env, IO_EXCEPTION, "accept not supported"); + return -1; +#endif /* HAVE_ACCEPT */ +} + + + +/* + * Class: gnu_java_nio_VMChannel + * Method: disconnect + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_gnu_java_nio_VMChannel_disconnect (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd) +{ + struct sockaddr sockaddr; + + sockaddr.sa_family = AF_UNSPEC; + if (connect (fd, &sockaddr, sizeof (struct sockaddr)) == -1) + { + /* The expected error for a successful disconnect is EAFNOSUPPORT. */ + if (errno != EAFNOSUPPORT) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + } +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: close + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_gnu_java_nio_VMChannel_close (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd) +{ + if (close (fd) == -1) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: available + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_available (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd) +{ + jint avail = 0; + +/* NIODBG("fd: %d", fd); */ + if (ioctl (fd, FIONREAD, &avail) == -1) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); +/* NIODBG("avail: %d", avail); */ + + return avail; +} + + +enum FileChannel_mode { + CPNIO_READ = 1, + CPNIO_WRITE = 2, + CPNIO_APPEND = 4, + CPNIO_EXCL = 8, + CPNIO_SYNC = 16, + CPNIO_DSYNC = 32 +}; + + +/* + * Class: gnu_java_nio_VMChannel + * Method: open + * Signature: (Ljava/lang/String;I)I + */ +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_open (JNIEnv *env, + jclass c __attribute__((unused)), + jstring path, jint mode) +{ + int nmode = 0; + int ret; + const char *npath; + mode_t mask = umask (0); + umask (mask); + + if ((mode & CPNIO_READ) && (mode & CPNIO_WRITE)) + nmode = O_RDWR; + else if (mode & CPNIO_WRITE) + nmode = O_WRONLY; + else + nmode = O_RDONLY; + + nmode = (nmode + | ((nmode == O_RDWR || nmode == O_WRONLY) ? O_CREAT : 0) + | ((mode & CPNIO_APPEND) ? O_APPEND : + ((nmode == O_RDWR || nmode == O_WRONLY) ? O_TRUNC : 0)) + | ((mode & CPNIO_EXCL) ? O_EXCL : 0) + | ((mode & CPNIO_SYNC) ? O_SYNC : 0)); + + npath = JCL_jstring_to_cstring (env, path); + +/* NIODBG("path: %s; mode: %x", npath, nmode); */ + + ret = open (npath, nmode, 0777 & ~mask); + +/* NIODBG("ret: %d\n", ret); */ + + JCL_free_cstring (env, path, npath); + + if (-1 == ret) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + + return ret; +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: position + * Signature: (I)J + */ +JNIEXPORT jlong JNICALL +Java_gnu_java_nio_VMChannel_position (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd) +{ +#ifdef HAVE_LSEEK + off_t ret; + + ret = lseek (fd, 0, SEEK_CUR); + + if (-1 == ret) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + + return (jlong) ret; +#else + JCL_ThrowException (env, IO_EXCEPTION, "position not supported"); + return -1; +#endif /* HAVE_LSEEK */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: seek + * Signature: (IJ)V + */ +JNIEXPORT void JNICALL +Java_gnu_java_nio_VMChannel_seek (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd, jlong pos) +{ +#ifdef HAVE_LSEEK + if (lseek (fd, (off_t) pos, SEEK_SET) == -1) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); +#else + JCL_ThrowException (env, IO_EXCEPTION, "seek not supported"); +#endif /* HAVE_LSEEK */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: truncate + * Signature: (IJ)V + */ +JNIEXPORT void JNICALL +Java_gnu_java_nio_VMChannel_truncate (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd, jlong len) +{ +#if defined(HAVE_FTRUNCATE) && defined(HAVE_LSEEK) + off_t pos = lseek (fd, 0, SEEK_CUR); + if (pos == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return; + } + if (ftruncate (fd, (off_t) len) == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return; + } + if (pos > len) + { + if (lseek (fd, len, SEEK_SET) == -1) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + } +#else + JCL_ThrowException (env, IO_EXCEPTION, "truncate not supported"); +#endif /* HAVE_FTRUNCATE && HAVE_LSEEK */ +} + + +/* + * Class: gnu_java_nio_VMChannel + * Method: lock + * Signature: (IJJZZ)Z + */ +JNIEXPORT jboolean JNICALL +Java_gnu_java_nio_VMChannel_lock (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd, jlong pos, jlong len, + jboolean shared, jboolean wait) +{ +#if HAVE_FCNTL + struct flock fl; + + fl.l_start = (off_t) pos; + fl.l_len = (off_t) len; + fl.l_pid = getpid (); + fl.l_type = (shared ? F_RDLCK : F_WRLCK); + fl.l_whence = SEEK_SET; + + if (cpnio_fcntl (fd, (wait ? F_SETLKW : F_SETLK), (long) &fl) == -1) + { + if (errno != EAGAIN) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + + return JNI_TRUE; +#else + JCL_ThrowException (env, IO_EXCEPTION, "lock not supported"); + return JNI_FALSE; +#endif /* HAVE_FCNTL */ +} + +/* + * Class: gnu_java_nio_VMChannel + * Method: unlock + * Signature: (IJJ)V + */ +JNIEXPORT void JNICALL +Java_gnu_java_nio_VMChannel_unlock (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd, jlong pos, jlong len) +{ +#if HAVE_FCNTL + struct flock fl; + + fl.l_start = (off_t) pos; + fl.l_len = (off_t) len; + fl.l_pid = getpid (); + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + + if (cpnio_fcntl (fd, F_SETLK, (long) &fl) == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + } +#else + JCL_ThrowException (env, IO_EXCEPTION, "unlock not supported"); +#endif /* HAVE_FCNTL */ +} + +/* + * Class: gnu_java_nio_VMChannel + * Method: size + * Signature: (I)J + */ +JNIEXPORT jlong JNICALL +Java_gnu_java_nio_VMChannel_size (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd) +{ +#ifdef HAVE_FSTAT + struct stat st; + + if (fstat (fd, &st) == -1) + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + + return (jlong) st.st_size; +#else + JCL_ThrowException (env, IO_EXCEPTION, "size not supported"); + return 0; +#endif +} + +/* + * Class: gnu_java_nio_VMChannel + * Method: map + * Signature: (ICJI)Lgnu/classpath/Pointer; + */ +JNIEXPORT jobject JNICALL +Java_gnu_java_nio_VMChannel_map (JNIEnv *env, + jclass clazz __attribute__((unused)), + jint fd, jchar mode, jlong position, jint size) +{ +#ifdef HAVE_MMAP + jclass MappedByteBufferImpl_class; + jmethodID MappedByteBufferImpl_init = NULL; + jobject Pointer_instance; + volatile jobject buffer; + long pagesize; + int prot, flags; + void *p; + void *address; + +/* NIODBG("fd: %d; mode: %x; position: %lld; size: %d", */ +/* fd, mode, position, size); */ + + /* FIXME: should we just assume we're on an OS modern enough to + have 'sysconf'? And not check for 'getpagesize'? */ +#if defined(HAVE_GETPAGESIZE) + pagesize = getpagesize (); +#elif defined(HAVE_SYSCONF) + pagesize = sysconf (_SC_PAGESIZE); +#else + JCL_ThrowException (env, IO_EXCEPTION, + "can't determine memory page size"); + return NULL; +#endif /* HAVE_GETPAGESIZE/HAVE_SYSCONF */ + + if ((*env)->ExceptionOccurred (env)) + { + return NULL; + } + + prot = PROT_READ; + if (mode == '+' || mode == 'c') + { + /* When writing we need to make sure the file is big enough, + otherwise the result of mmap is undefined. */ + struct stat st; + if (fstat (fd, &st) == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return NULL; + } + if (position + size > st.st_size) + { + if (ftruncate(fd, position + size) == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return NULL; + } + } + prot |= PROT_WRITE; + } + + flags = (mode == 'c' ? MAP_PRIVATE : MAP_SHARED); + p = mmap (NULL, (size_t) ALIGN_UP (size, pagesize), prot, flags, + fd, ALIGN_DOWN (position, pagesize)); + if (p == MAP_FAILED) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return NULL; + } + + /* Unalign the mapped value back up, since we aligned offset + down to a multiple of the page size. */ + address = (void *) ((char *) p + (position % pagesize)); + + Pointer_instance = JCL_NewRawDataObject(env, address); + + MappedByteBufferImpl_class = (*env)->FindClass (env, + "java/nio/MappedByteBufferImpl"); + if (MappedByteBufferImpl_class != NULL) + { + MappedByteBufferImpl_init = + (*env)->GetMethodID (env, MappedByteBufferImpl_class, + "<init>", "(Lgnu/classpath/Pointer;IZ)V"); + } + + if ((*env)->ExceptionOccurred (env)) + { + munmap (p, ALIGN_UP (size, pagesize)); + return NULL; + } + if (MappedByteBufferImpl_init == NULL) + { + JCL_ThrowException (env, "java/lang/InternalError", + "could not get MappedByteBufferImpl constructor"); + munmap (p, ALIGN_UP (size, pagesize)); + return NULL; + } + + buffer = (*env)->NewObject (env, MappedByteBufferImpl_class, + MappedByteBufferImpl_init, Pointer_instance, + (jint) size, mode == 'r'); + return buffer; +#else + (void) fd; + (void) mode; + (void) position; + (void) size; + JCL_ThrowException (env, IO_EXCEPTION, + "memory-mapped files not implemented"); + return 0; +#endif /* HAVE_MMAP */ +} + +/* + * Class: gnu_java_nio_VMChannel + * Method: flush + * Signature: (IZ)Z + */ +JNIEXPORT jboolean JNICALL +Java_gnu_java_nio_VMChannel_flush (JNIEnv *env, + jclass c __attribute__((unused)), + jint fd, jboolean metadata __attribute__((unused))) +{ +#ifdef HAVE_FSYNC + /* XXX blocking? */ + if (fsync (fd) == -1) + { + JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + return JNI_FALSE; + } + return JNI_TRUE; +#else + JCL_ThrowException (env, IO_EXCEPTION, "flush not implemented"); + return JNI_TRUE; +#endif /* HAVE_FSYNC */ +} #ifdef __cplusplus |