mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			2036 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			2036 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			C
		
	
	
	
| /* gnu_java_nio_VMChannel.c -
 | |
|    Copyright (C) 2003, 2004, 2005, 2006, 2007  Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
| 
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | |
| 02110-1301 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| 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 <config-int.h>
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #ifdef HAVE_SYS_MMAN_H
 | |
| #include <sys/mman.h>
 | |
| #endif
 | |
| #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 <string.h>
 | |
| 
 | |
| #include <jni.h>
 | |
| #include <jcl.h>
 | |
| 
 | |
| #include "cpio.h"
 | |
| #include "gnu_java_nio_VMChannel.h"
 | |
| #include "javanio.h"
 | |
| 
 | |
| #ifdef HAVE_FCNTL_H
 | |
| #include <fcntl.h>
 | |
| #endif /* HAVE_FCNTL_H */
 | |
| 
 | |
| #if defined(HAVE_SYS_IOCTL_H)
 | |
| #define BSD_COMP /* Get FIONREAD on Solaris2 */
 | |
| #include <sys/ioctl.h>
 | |
| #endif
 | |
| #if defined(HAVE_SYS_FILIO_H) /* Get FIONREAD on Solaris 2.5 */
 | |
| #include <sys/filio.h>
 | |
| #endif
 | |
| 
 | |
| #define CONNECT_EXCEPTION "java/net/ConnectException"
 | |
| #define IO_EXCEPTION "java/io/IOException"
 | |
| #define SOCKET_EXCEPTION "java/net/SocketException"
 | |
| #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
 | |
|  */
 | |
| #define JCL_IOV_MAX 16
 | |
| 
 | |
| #ifdef __cplusplus
 | |
| extern "C"
 | |
| {
 | |
| #endif
 | |
| 
 | |
| enum JCL_buffer_type { DIRECT, HEAP, ARRAY, UNKNOWN };
 | |
| 
 | |
| struct JCL_buffer
 | |
| {
 | |
|   enum JCL_buffer_type type;
 | |
|   jbyte *ptr;
 | |
|   jint offset;
 | |
|   jint position;
 | |
|   jint limit;
 | |
|   jint count;
 | |
| };
 | |
| 
 | |
| jmethodID get_method_id(JNIEnv *, jclass, const char *, const char *);
 | |
| void JCL_print_buffer(JNIEnv *, struct JCL_buffer *);
 | |
| int JCL_init_buffer(JNIEnv *, struct JCL_buffer *, jobject);
 | |
| void JCL_release_buffer(JNIEnv *, struct JCL_buffer *, jobject, jint);
 | |
| void JCL_cleanup_buffers(JNIEnv *, struct JCL_buffer *, jint, jobjectArray, jint, jlong);
 | |
| int JCL_thread_interrupted(JNIEnv *);
 | |
| 
 | |
| static jfieldID address_fid;
 | |
| static jmethodID get_position_mid;
 | |
| static jmethodID set_position_mid;
 | |
| static jmethodID get_limit_mid;
 | |
| static jmethodID set_limit_mid;
 | |
| static jmethodID has_array_mid;
 | |
| static jmethodID array_mid;
 | |
| static jmethodID array_offset_mid;
 | |
| static jmethodID thread_interrupted_mid;
 | |
| static jclass vm_channel_class;
 | |
| 
 | |
| jmethodID
 | |
| 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);
 | |
|       return NULL;
 | |
|     }
 | |
|   
 | |
|   return mid;
 | |
| }
 | |
| 
 | |
| inline void
 | |
| JCL_print_buffer(JNIEnv *env __attribute__((__unused__)), struct JCL_buffer *buf)
 | |
| {
 | |
|   fprintf (stderr, "Buffer - type: %d, ptr: %p\n", buf->type, buf->ptr);
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| JCL_init_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf)
 | |
| {
 | |
|   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);
 | |
|   buf->offset = 0;
 | |
|   buf->count = 0;
 | |
|   buf->type = UNKNOWN;
 | |
|     
 | |
|   if (addr != NULL)
 | |
|     {
 | |
|       buf->ptr = (jbyte *) addr;
 | |
|       buf->type = DIRECT;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       jboolean has_array;
 | |
|       has_array = (*env)->CallBooleanMethod(env, bbuf, has_array_mid);
 | |
|       
 | |
|       if (has_array == JNI_TRUE)
 | |
|         {
 | |
|           jbyteArray arr;
 | |
|           buf->offset = (*env)->CallIntMethod(env, bbuf, array_offset_mid);
 | |
|           arr = (*env)->CallObjectMethod(env, bbuf, array_mid);
 | |
|           buf->ptr = (*env)->GetByteArrayElements(env, arr, 0);
 | |
|           buf->type = ARRAY;
 | |
|           (*env)->DeleteLocalRef(env, arr);
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           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);
 | |
|         }
 | |
|     }
 | |
|       
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| 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)
 | |
|     {
 | |
|       jobject bbufTemp;
 | |
|       bbufTemp = (*env)->CallObjectMethod(env, bbuf, set_position_mid, 
 | |
|                                           buf->position + buf->count);
 | |
|       (*env)->DeleteLocalRef(env, bbufTemp);
 | |
|     }
 | |
|     
 | |
|   switch (buf->type)
 | |
|     {
 | |
|     case DIRECT:
 | |
|     case HEAP:
 | |
|       break;
 | |
|     case ARRAY:
 | |
|       arr = (*env)->CallObjectMethod(env, bbuf, array_mid);
 | |
|       (*env)->ReleaseByteArrayElements(env, arr, buf->ptr, action);
 | |
|       (*env)->DeleteLocalRef(env, arr);
 | |
|       break;
 | |
|     case UNKNOWN:
 | |
|       /* TODO: Handle buffers that are not direct or array backed */
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| JCL_cleanup_buffers(JNIEnv *env, 
 | |
|                     struct JCL_buffer *bi_list, 
 | |
|                     jint vec_len, 
 | |
|                     jobjectArray bbufs, 
 | |
|                     jint offset,
 | |
|                     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++)
 | |
|     {
 | |
|       struct JCL_buffer* buf;
 | |
|       jobject bbuf;
 | |
|       
 | |
|       buf = &bi_list[i];
 | |
|       bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i);
 | |
| 
 | |
|       if (num_bytes > (buf->limit - buf->position))
 | |
|         buf->count = (buf->limit - buf->position);
 | |
|       else
 | |
|         buf->count = num_bytes;
 | |
|         
 | |
|       num_bytes -= buf->count;
 | |
|       
 | |
|       JCL_release_buffer(env, buf, bbuf, JNI_ABORT);
 | |
|       (*env)->DeleteLocalRef(env, bbuf);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| JCL_thread_interrupted(JNIEnv *env)
 | |
| {
 | |
|   return (int) (*env)->CallStaticBooleanMethod(env, vm_channel_class,
 | |
| 					       thread_interrupted_mid);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * 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)
 | |
| {
 | |
|   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)
 | |
|     {
 | |
|   	  JCL_ThrowException(env, "java/lang/InternalError", 
 | |
|   	  	"Unable to find internal field");
 | |
|       return;
 | |
|     }
 | |
|   
 | |
|   get_position_mid = get_method_id(env, bufferClass, "position", "()I");
 | |
|   set_position_mid = get_method_id(env, bufferClass, "position", 
 | |
|                                    "(I)Ljava/nio/Buffer;");
 | |
|   get_limit_mid = get_method_id(env, bufferClass, "limit", "()I");
 | |
|   set_limit_mid = get_method_id(env, bufferClass, "limit", 
 | |
|                                 "(I)Ljava/nio/Buffer;");
 | |
|   has_array_mid = get_method_id(env, byteBufferClass, "hasArray", "()Z");
 | |
|   array_mid = get_method_id(env, byteBufferClass, "array", "()[B");
 | |
|   array_offset_mid = get_method_id(env, byteBufferClass, "arrayOffset", "()I");
 | |
|   
 | |
|   vm_channel_class = clazz;
 | |
|   thread_interrupted_mid = (*env)->GetStaticMethodID(env, clazz,
 | |
|                                                   "isThreadInterrupted",
 | |
|                                                   "()Z");
 | |
| }
 | |
| 
 | |
| JNIEXPORT void JNICALL 
 | |
| Java_gnu_java_nio_VMChannel_setBlocking (JNIEnv *env, 
 | |
| 	jobject o __attribute__ ((__unused__)), 
 | |
| 	jint fd, 
 | |
| 	jboolean blocking)
 | |
| {
 | |
|   int opts;
 | |
|   
 | |
| /*   NIODBG("fd: %d; blocking: %d", fd, blocking); */
 | |
| 
 | |
|   opts = fcntl(fd, F_GETFL);
 | |
|   if (opts < 0)
 | |
|     {
 | |
|       JCL_ThrowException(env, IO_EXCEPTION, 
 | |
|         "Failed to get flags for file desriptor");
 | |
|       return;
 | |
|     }
 | |
|   
 | |
|   if (blocking == JNI_TRUE)
 | |
|     opts &= ~(O_NONBLOCK);
 | |
|   else
 | |
|     opts |= O_NONBLOCK;
 | |
|   
 | |
|   opts = fcntl(fd, F_SETFL, opts);
 | |
|   
 | |
|   if (opts < 0)
 | |
|     {
 | |
|       JCL_ThrowException(env, IO_EXCEPTION, 
 | |
|         "Failed to set flags for file desriptor");
 | |
|       return;
 | |
|     }  
 | |
| }
 | |
| 
 | |
| /* Return true if fd is in non-blocking mode. */
 | |
| static jboolean
 | |
| is_non_blocking_fd(jint fd)
 | |
| {
 | |
|   int opts;
 | |
|   opts = fcntl(fd, F_GETFL);
 | |
|   if (opts == -1)
 | |
|     {
 | |
|       /* Assume blocking on error. */
 | |
|       return 0;
 | |
|     }
 | |
|   return (opts & O_NONBLOCK) != 0;
 | |
| }
 | |
| 
 | |
| JNIEXPORT jint JNICALL 
 | |
| 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;
 | |
|   int tmp_errno;
 | |
| 
 | |
| /*   NIODBG("fd: %d; bbuf: %p", fd, bbuf); */
 | |
|   
 | |
|   if (JCL_init_buffer(env, &buf, bbuf) < 0)
 | |
|     {
 | |
|       /* TODO: Rethrown exception */
 | |
|       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;
 | |
|     }
 | |
|   
 | |
|   do 
 | |
|     {
 | |
|       result = cpnio_read (fd, &(buf.ptr[buf.position + buf.offset]), len);
 | |
|       tmp_errno = errno;
 | |
|     }
 | |
|   while (result == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
 | |
|   errno = tmp_errno;
 | |
|   
 | |
|   if (result == 0)
 | |
|     {
 | |
|       result = -1;
 | |
|       buf.count = 0;
 | |
|     }
 | |
|   else if (result == -1)
 | |
|     {
 | |
|       buf.count = 0;
 | |
|       if (errno == EAGAIN)
 | |
|         {
 | |
|           if (is_non_blocking_fd(fd))
 | |
|             {
 | |
|               /* Non-blocking */
 | |
|               result = 0;
 | |
|             }
 | |
|           else
 | |
|             {
 | |
|               /* Read timeout on a socket with SO_RCVTIMEO != 0. */
 | |
|               JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
 | |
|               JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "read timed out");
 | |
|               return -1;
 | |
|             }
 | |
|         }
 | |
|       else if (errno == EBADF) /* Bad fd */
 | |
|         {
 | |
|           JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
 | |
|           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);
 | |
|       	  JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
 | |
|       	  return -1;
 | |
|         }
 | |
|     }
 | |
|   else 
 | |
|     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__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;
 | |
|   int tmp_errno;
 | |
| 
 | |
| /*   NIODBG("fd: %d; bbuf: %p", fd, bbuf); */
 | |
|   
 | |
|   if (JCL_init_buffer(env, &buf, bbuf) < 0)
 | |
|     {
 | |
|       /* TODO: Rethrown exception */
 | |
|       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;
 | |
|     }
 | |
|   
 | |
|   do
 | |
|     {
 | |
|       result = cpnio_write (fd, &(buf.ptr[buf.position + buf.offset]), len);
 | |
|       tmp_errno = errno;
 | |
|     }
 | |
|   while (result == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
 | |
|   errno = tmp_errno;
 | |
| 
 | |
|   buf.count = result;
 | |
| 
 | |
|   if (result == -1)
 | |
|     {
 | |
|       if (errno == EAGAIN) /* Non-blocking */
 | |
|         {
 | |
|           result = 0;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
 | |
|           JCL_ThrowException(env, IO_EXCEPTION, strerror(errno));
 | |
|           return -1;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|   JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
 | |
|   
 | |
|   return result;
 | |
| #else
 | |
|   (void) fd;
 | |
|   (void) bbuf;
 | |
|   JCL_ThrowException (env, IO_EXCEPTION, "write not supported");
 | |
|   return -1;
 | |
| #endif /* HAVE_WRITE */
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Implementation of a scattering read.  Will use the appropriate
 | |
|  * vector based read call (currently readv on Linux).
 | |
|  * 
 | |
|  * This has a limit to the number of buffers that will be read.  It
 | |
|  * will not make muliple readv calls.  This is to ensure that operations 
 | |
|  * are atomic.  Currently it is limited to 16 buffers.  This is for 
 | |
|  * compatibiliy with Sun.
 | |
|  */
 | |
| JNIEXPORT jlong JNICALL 
 | |
| Java_gnu_java_nio_VMChannel_readScattering (JNIEnv *env, 
 | |
| 	jobject o __attribute__ ((__unused__)), 
 | |
| 	jint fd, 
 | |
| 	jobjectArray bbufs, 
 | |
| 	jint offset, 
 | |
| 	jint length)
 | |
| {
 | |
|   jint i;
 | |
| /*   jboolean is_error = JNI_FALSE; */
 | |
| /*   char *error_msg; */
 | |
|   struct iovec buffers[JCL_IOV_MAX];
 | |
|   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;
 | |
|   int tmp_errno;
 | |
| 
 | |
| /*   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++)
 | |
|     {
 | |
|       struct JCL_buffer* buf;
 | |
|       jobject bbuf;
 | |
|       
 | |
|       buf = &bi_list[i];
 | |
|       bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i);
 | |
|       
 | |
|       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 scattering magic */
 | |
|   do
 | |
|     {
 | |
|       result = cpnio_readv (fd, buffers, vec_len);
 | |
|       tmp_errno = errno;
 | |
|     }
 | |
|   while (result == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
 | |
|   errno = tmp_errno;
 | |
|   bytes_read = (jlong) result;
 | |
|   
 | |
|   /* Handle the response */
 | |
|   if (result < 0)
 | |
|     {
 | |
|       if (errno == EAGAIN)
 | |
|         {
 | |
|           if (is_non_blocking_fd(fd))
 | |
|             {
 | |
|               /* Non-blocking */
 | |
|               result = 0;
 | |
|             }
 | |
|           else
 | |
|             {
 | |
|               /* Read timeout on a socket with SO_RCVTIMEO != 0. */
 | |
|               JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
 | |
|               JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "read timed out");
 | |
|               return -1;
 | |
|             }
 | |
|         }
 | |
|       else if (errno == EBADF) /* Bad fd */
 | |
|         {
 | |
|           JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
 | |
|           JCL_ThrowException (env, NON_READABLE_CHANNEL_EXCEPTION, 
 | |
|                               strerror(errno));
 | |
|           return -1;
 | |
|         } 
 | |
|       else
 | |
|         {
 | |
|           JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
 | |
|           JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
 | |
|           return -1;
 | |
|         }
 | |
|       bytes_read = 0;
 | |
|     }
 | |
|   else if (result == 0) /* EOF */
 | |
|     {
 | |
|       result = -1;
 | |
|     }
 | |
|     
 | |
|   JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
 | |
|                   
 | |
|   return (jlong) result;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Implementation of a gathering write.  Will use the appropriate
 | |
|  * vector based read call (currently readv on Linux).
 | |
|  * 
 | |
|  * This has a limit to the number of buffers that will be read.  It
 | |
|  * will not make muliple readv calls.  This is to ensure that operations 
 | |
|  * are atomic.  Currently it is limited to 16 buffers.  This is for 
 | |
|  * compatibiliy with Sun.
 | |
|  */
 | |
| JNIEXPORT jlong JNICALL 
 | |
| Java_gnu_java_nio_VMChannel_writeGathering (JNIEnv *env, 
 | |
| 	jobject o __attribute__ ((__unused__)), 
 | |
| 	jint fd, 
 | |
| 	jobjectArray bbufs, 
 | |
| 	jint offset, 
 | |
| 	jint length)
 | |
| {
 | |
|   int i;
 | |
| /*   jboolean is_error = JNI_FALSE; */
 | |
| /*   char *error_msg; */
 | |
|   struct iovec buffers[JCL_IOV_MAX];
 | |
|   struct JCL_buffer bi_list[JCL_IOV_MAX];
 | |
|   ssize_t result;
 | |
|   jint vec_len = length < JCL_IOV_MAX ? length : JCL_IOV_MAX;
 | |
|   jlong bytes_written;
 | |
|   int tmp_errno;
 | |
|   
 | |
| /*   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++)
 | |
|     {
 | |
|       struct JCL_buffer* buf;
 | |
|       jobject bbuf;
 | |
|       
 | |
|       buf = &bi_list[i];
 | |
|       bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i);
 | |
|       
 | |
|       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 */
 | |
|   do
 | |
|     {
 | |
|       result = cpnio_writev (fd, buffers, vec_len);
 | |
|       tmp_errno = errno;
 | |
|     }
 | |
|   while (result == -1 && tmp_errno == EINTR && ! JCL_thread_interrupted(env));
 | |
|   errno = tmp_errno;
 | |
| 
 | |
|   bytes_written = (jlong) result;
 | |
| 
 | |
|   if (result < 0)
 | |
|     {
 | |
|       bytes_written = 0;
 | |
|       if (errno == EAGAIN) /* Non blocking */
 | |
|         result = 0;
 | |
|       else if (errno == EBADF) /* Bad fd */
 | |
|         {
 | |
|           JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, 
 | |
|                               bytes_written);
 | |
|           JCL_ThrowException (env, NON_WRITABLE_CHANNEL_EXCEPTION, 
 | |
|                               strerror(errno));
 | |
|           return -1;
 | |
|         } 
 | |
|       else
 | |
|         {
 | |
|           JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset,
 | |
|                               bytes_written);
 | |
|           JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
 | |
|           return -1;
 | |
|         }
 | |
|     }
 | |
|   else if (result == 0) /* EOF??  Does this happen on a write */
 | |
|     result = -1;
 | |
|     
 | |
|   JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_written);    
 | |
|   return (jlong) result;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * 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");
 | |
| 
 | |
| #ifndef HAVE_MSG_WAITALL
 | |
| #define MSG_WAITALL       0
 | |
| #endif
 | |
| 
 | |
|   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;
 | |
|   int tmp_errno;
 | |
| 
 | |
| /*   NIODBG("fd: %d", fd); */
 | |
| 
 | |
|   do
 | |
|     {
 | |
|       ret = cpnio_read (fd, &in, 1);
 | |
|       tmp_errno = errno;
 | |
|     }
 | |
|   while (ret == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
 | |
|   errno = tmp_errno;
 | |
| 
 | |
|   if (-1 == ret)
 | |
|     {
 | |
|       if (errno == EAGAIN && !is_non_blocking_fd(fd))
 | |
|         {
 | |
|           /* Read timeout on a socket with SO_RCVTIMEO != 0. */
 | |
|           JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "read timed out");
 | |
|         }
 | |
|       else
 | |
|         JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
 | |
|       return -1;
 | |
|     }
 | |
|   
 | |
|   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;
 | |
|   int tmp_errno;
 | |
| 
 | |
| /*   NIODBG("fd: %d; data: %d", fd, data); */
 | |
| 
 | |
|   do
 | |
|     {
 | |
|       ret = cpnio_write (fd, &out, 1);
 | |
|       tmp_errno = errno;
 | |
|     }
 | |
|   while (ret == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
 | |
|   errno = tmp_errno;
 | |
| 
 | |
|   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;
 | |
|   int tmpErrno;
 | |
| 
 | |
|   if ((*env)->GetArrayLength (env, addr) != 4)
 | |
|     {
 | |
|       JCL_ThrowException (env, SOCKET_EXCEPTION,
 | |
|                           "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, SOCKET_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, SOCKET_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);
 | |
| 
 | |
| 
 | |
|   do
 | |
|     {
 | |
|       ret = cpnio_connect (fd, (struct sockaddr *) &sockaddr,
 | |
|                            sizeof (struct sockaddr_in));
 | |
|       tmpErrno = errno;
 | |
|     }
 | |
|   while (ret == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
 | |
|   errno = tmpErrno;
 | |
| 
 | |
|   (*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, SOCKET_EXCEPTION, strerror (errno));
 | |
|               return JNI_FALSE;
 | |
|             }
 | |
|         }
 | |
|       if (EINPROGRESS == errno)
 | |
|         {
 | |
|           fd_set wrfds;
 | |
|           FD_ZERO(&wrfds);
 | |
|           FD_SET(fd, &wrfds);
 | |
|           ret = cpnio_select (fd + 1, NULL, &wrfds, NULL, &timeo);
 | |
|           if (ret == -1)
 | |
|             {
 | |
|               JCL_ThrowException (env, SOCKET_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, CONNECT_EXCEPTION,
 | |
|                               strerror (errno));
 | |
|           return JNI_FALSE;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
 | |
|           return JNI_FALSE;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   if (ret == -1)
 | |
|     {
 | |
|       if (EINPROGRESS == errno)
 | |
|         return JNI_FALSE;
 | |
|       else if (ECONNREFUSED == errno)
 | |
|         {
 | |
|           JCL_ThrowException (env, CONNECT_EXCEPTION,
 | |
|                               strerror (errno));
 | |
|           return JNI_FALSE;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
 | |
|           return JNI_FALSE;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   return JNI_TRUE;
 | |
| #else
 | |
|   (void) fd;
 | |
|   (void) addr;
 | |
|   (void) port;
 | |
|   (void) timeout;
 | |
|   JCL_ThrowException (env, SOCKET_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, SOCKET_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, SOCKET_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, SOCKET_EXCEPTION, strerror (errno));
 | |
|               return JNI_FALSE;
 | |
|             }
 | |
|         }
 | |
|       if (EINPROGRESS == errno)
 | |
|         {
 | |
|           fd_set wrfds;
 | |
|           FD_ZERO(&wrfds);
 | |
|           FD_SET(fd, &wrfds);
 | |
|           ret = cpnio_select (fd + 1, NULL, &wrfds, NULL, &timeo);
 | |
|           if (ret == -1)
 | |
|             {
 | |
|               JCL_ThrowException (env, SOCKET_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, CONNECT_EXCEPTION,
 | |
|                               strerror (errno));
 | |
|           return JNI_FALSE;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
 | |
|           return JNI_FALSE;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   if (ret == -1)
 | |
|     {
 | |
|       if (EAGAIN == errno)
 | |
|         return JNI_FALSE;
 | |
|       else if (ECONNREFUSED == errno)
 | |
|         {
 | |
|           JCL_ThrowException (env, CONNECT_EXCEPTION,
 | |
|                               strerror (errno));
 | |
|           return JNI_FALSE;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
 | |
|           return JNI_FALSE;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   return JNI_TRUE;
 | |
| #else
 | |
|   (void) fd;
 | |
|   (void) addr;
 | |
|   (void) port;
 | |
|   (void) timeout;
 | |
|   JCL_ThrowException (env, SOCKET_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 + 16, &(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 + 16, &(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 c __attribute__((unused)),
 | |
|                                     jint fd)
 | |
| {
 | |
| #ifdef HAVE_ACCEPT
 | |
|   int ret;
 | |
|   int tmp_errno = 0;
 | |
| 
 | |
| #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);
 | |
|       tmp_errno = errno;
 | |
|       
 | |
|       if (ret == -1)
 | |
|         switch (tmp_errno)
 | |
|         {
 | |
|           case EINTR:
 | |
|             /* Check if interrupted by Thread.interrupt(). If not then some
 | |
|              * other unrelated signal interrupted the system function and
 | |
|              * we should start over again.
 | |
|              */
 | |
|             if (JCL_thread_interrupted(env))
 | |
|               {
 | |
|                 JCL_ThrowException (env, "java/net/SocketException", strerror (tmp_errno));
 | |
|                 return -1;
 | |
|               }
 | |
|             break;
 | |
| #if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN
 | |
|           case EWOULDBLOCK:
 | |
| #endif
 | |
|           case EAGAIN:
 | |
|             if (!is_non_blocking_fd(fd))
 | |
|               {
 | |
|                 JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "Accept timed out");
 | |
|               }
 | |
|             /* Socket in non-blocking mode and no pending connection. */
 | |
|             return -1;
 | |
|           default:
 | |
|             JCL_ThrowException (env, "java/net/SocketException", strerror (tmp_errno));
 | |
|             return -1;
 | |
|         }
 | |
|       else
 | |
|         break;
 | |
|     }
 | |
|   while (1);
 | |
| 
 | |
|   cpio_closeOnExec(ret);
 | |
| 
 | |
|   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)
 | |
| {
 | |
| #if defined (FIONREAD)
 | |
| 
 | |
|   jint avail = 0;
 | |
| 
 | |
| #if defined(ENOTTY) && defined(HAVE_FSTAT)
 | |
|   struct stat statBuffer;
 | |
|   off_t n;
 | |
| #endif
 | |
| 
 | |
| /*   NIODBG("fd: %d", fd); */
 | |
|   if (ioctl (fd, FIONREAD, &avail) == -1)
 | |
|     {
 | |
| #if defined(ENOTTY) && defined(HAVE_FSTAT)
 | |
|       if (errno == ENOTTY)
 | |
|         {
 | |
|           if ((fstat (fd, &statBuffer) == 0) && S_ISREG (statBuffer.st_mode))
 | |
|             {
 | |
|               n = lseek (fd, 0, SEEK_CUR);
 | |
|               if (n != -1)
 | |
|                 {
 | |
|                   avail = statBuffer.st_size - n;
 | |
|                   return avail;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| #endif
 | |
|       JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
 | |
|     }
 | |
| /*   NIODBG("avail: %d", avail); */
 | |
| 
 | |
|   return avail;
 | |
| 
 | |
| #elif defined(HAVE_FSTAT)
 | |
| 
 | |
|   jint avail = 0;
 | |
| 
 | |
|   struct stat statBuffer;
 | |
|   off_t n;
 | |
| 
 | |
|   if ((fstat (fd, &statBuffer) == 0) && S_ISREG (statBuffer.st_mode))
 | |
|     {
 | |
|       n = lseek (fd, 0, SEEK_CUR);
 | |
|       if (n != -1) 
 | |
|         { 
 | |
| 	  avail = statBuffer.st_size - n;
 | |
| 	  return avail;
 | |
|         } 
 | |
|     }
 | |
|   JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
 | |
| 
 | |
| #elif defined(HAVE_SELECT)
 | |
| 
 | |
|   jint avail = 0;
 | |
|   fd_set filedescriptset;
 | |
|   struct timeval tv;
 | |
| 
 | |
|   FD_ZERO (&filedescriptset);
 | |
|   FD_SET (fd,&filedescriptset);
 | |
|   memset (&tv, 0, sizeof(tv));
 | |
| 
 | |
|   switch (select (fd+1, &filedescriptset, NULL, NULL, &tv))
 | |
|     {
 | |
|       case -1:
 | |
|         break;
 | |
|       case  0:
 | |
|         avail = 0;
 | |
| 	return avail;
 | |
|       default:
 | |
|         avail = 1;
 | |
| 	return avail;
 | |
|     }
 | |
|   JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
 | |
| 
 | |
| #else
 | |
| 
 | |
|   JCL_ThrowException (env, IO_EXCEPTION, "No native method for available");
 | |
| 
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| 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;
 | |
| 
 | |
|   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_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, 0666);
 | |
| 
 | |
| /*   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;
 | |
|   /* Long.MAX_VALUE means lock everything possible starting at pos. */
 | |
|   if (len == 9223372036854775807LL)
 | |
|     fl.l_len = 0;
 | |
|   else
 | |
|     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
 | |
| }
 | |
| #endif
 |