1 // natFileDescriptor.cc - Native part of FileDescriptor class.
2 
3 /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Free Software Foundation
4 
5    This file is part of libgcj.
6 
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
9 details.  */
10 
11 #include <config.h>
12 
13 #include "posix.h"
14 
15 #include <errno.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 #include <sys/param.h>
20 
21 #ifdef HAVE_SYS_IOCTL_H
22 #define BSD_COMP /* Get FIONREAD on Solaris2. */
23 #include <sys/ioctl.h>
24 #endif
25 
26 // Pick up FIONREAD on Solaris 2.5.
27 #ifdef HAVE_SYS_FILIO_H
28 #include <sys/filio.h>
29 #endif
30 
31 #include <gcj/cni.h>
32 #include <jvm.h>
33 #include <java/io/FileDescriptor.h>
34 #include <java/io/SyncFailedException.h>
35 #include <java/io/IOException.h>
36 #include <java/io/InterruptedIOException.h>
37 #include <java/io/EOFException.h>
38 #include <java/lang/ArrayIndexOutOfBoundsException.h>
39 #include <java/lang/NullPointerException.h>
40 #include <java/lang/System.h>
41 #include <java/lang/String.h>
42 #include <java/lang/Thread.h>
43 #include <java/io/FileNotFoundException.h>
44 
45 #define NO_FSYNC_MESSAGE "sync unsupported"
46 
47 void
init(void)48 java::io::FileDescriptor::init (void)
49 {
50   in = new java::io::FileDescriptor(0);
51   out = new java::io::FileDescriptor(1);
52   err = new java::io::FileDescriptor(2);
53 }
54 
55 jboolean
valid(void)56 java::io::FileDescriptor::valid (void)
57 {
58   struct stat sb;
59   return fd >= 0 && ::fstat (fd, &sb) == 0;
60 }
61 
62 void
sync(void)63 java::io::FileDescriptor::sync (void)
64 {
65   // Some files don't support fsync.  We don't bother reporting these
66   // as errors.
67 #ifdef HAVE_FSYNC
68   if (::fsync (fd) && errno != EROFS && errno != EINVAL)
69     throw new SyncFailedException (JvNewStringLatin1 (strerror (errno)));
70 #else
71   throw new SyncFailedException (JvNewStringLatin1 (NO_FSYNC_MESSAGE));
72 #endif
73 }
74 
75 jint
open(jstring path,jint jflags)76 java::io::FileDescriptor::open (jstring path, jint jflags)
77 {
78   char *buf = (char *) _Jv_AllocBytes (_Jv_GetStringUTFLength (path) + 1);
79   jsize total = JvGetStringUTFRegion (path, 0, path->length(), buf);
80   buf[total] = '\0';
81   int flags = 0;
82 #ifdef O_BINARY
83   flags |= O_BINARY;
84 #endif
85 
86   JvAssert ((jflags & READ) || (jflags & WRITE));
87   int mode = 0666;
88   if ((jflags & READ) && (jflags & WRITE))
89     flags |= O_RDWR | O_CREAT;
90   else if ((jflags & READ))
91     flags |= O_RDONLY;
92   else
93     {
94       flags |= O_WRONLY | O_CREAT;
95       if ((jflags & APPEND))
96 	flags |= O_APPEND;
97       else
98 	flags |= O_TRUNC;
99 
100       if ((jflags & EXCL))
101 	{
102 	  flags |= O_EXCL;
103 	  // In this case we are making a temp file.
104 	  mode = 0600;
105 	}
106     }
107 
108   if ((jflags & SYNC))
109     flags |= O_SYNC;
110 
111   if ((jflags & DSYNC))
112     flags |= O_DSYNC;
113 
114   int fd = ::open (buf, flags, mode);
115   if (fd == -1 && errno == EMFILE)
116     {
117       // Because finalize () calls close () we might be able to continue.
118       java::lang::System::gc ();
119       java::lang::System::runFinalization ();
120       fd = ::open (buf, flags, mode);
121     }
122   if (fd == -1)
123     {
124       char msg[MAXPATHLEN + 200];
125       // We choose the formatting here for JDK compatibility, believe
126       // it or not.
127       sprintf (msg, "%s (%s)", buf, strerror (errno));
128       throw new FileNotFoundException (JvNewStringLatin1 (msg));
129     }
130 
131   _Jv_platform_close_on_exec (fd);
132 
133   return fd;
134 }
135 
136 void
write(jint b)137 java::io::FileDescriptor::write (jint b)
138 {
139   jbyte d = (jbyte) b;
140   int r = 0;
141   while (r != 1)
142     {
143       r = ::write (fd, &d, 1);
144       if (r == -1)
145         {
146 	  if (java::lang::Thread::interrupted())
147 	    {
148 	      InterruptedIOException *iioe
149 		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
150 	      iioe->bytesTransferred = r == -1 ? 0 : r;
151 	      throw iioe;
152 	    }
153 	  if (errno != EINTR)
154 	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
155 	}
156     }
157   position++;
158 }
159 
160 void
write(jbyteArray b,jint offset,jint len)161 java::io::FileDescriptor::write (jbyteArray b, jint offset, jint len)
162 {
163   if (! b)
164     throw new java::lang::NullPointerException;
165   if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
166     throw new java::lang::ArrayIndexOutOfBoundsException;
167   jbyte *bytes = elements (b) + offset;
168 
169   int written = 0;
170   while (len > 0)
171     {
172       int r = ::write (fd, bytes, len);
173       if (r == -1)
174         {
175 	  if (java::lang::Thread::interrupted())
176 	    {
177 	      InterruptedIOException *iioe
178 		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
179 	      iioe->bytesTransferred = written;
180 	      throw iioe;
181 	    }
182 	  if (errno != EINTR)
183 	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
184 	}
185 
186       written += r;
187       len -= r;
188       bytes += r;
189       position += r;
190     }
191 }
192 
193 void
close(void)194 java::io::FileDescriptor::close (void)
195 {
196   jint save = fd;
197   fd = -1;
198   if (::close (save))
199     throw new IOException (JvNewStringLatin1 (strerror (errno)));
200 }
201 
202 void
setLength(jlong pos)203 java::io::FileDescriptor::setLength (jlong pos)
204 {
205   struct stat sb;
206 
207 #ifdef HAVE_FTRUNCATE
208   if (::fstat (fd, &sb))
209     throw new IOException (JvNewStringLatin1 (strerror (errno)));
210 
211   if ((jlong) sb.st_size == pos)
212     return;
213 
214   // If the file is too short, we extend it.  We can't rely on
215   // ftruncate() extending the file.  So we lseek() to 1 byte less
216   // than we want, and then we write a single byte at the end.
217   if ((jlong) sb.st_size < pos)
218     {
219       if (::lseek (fd, (off_t) (pos - 1), SEEK_SET) == -1)
220 	throw new IOException (JvNewStringLatin1 (strerror (errno)));
221       char out = '\0';
222       int r = ::write (fd, &out, 1);
223       if (r <= 0 || ::lseek (fd, position, SEEK_SET) == -1)
224 	throw new IOException (JvNewStringLatin1 (strerror (errno)));
225     }
226   else
227     {
228       if (::ftruncate (fd, (off_t) pos))
229 	throw new IOException (JvNewStringLatin1 (strerror (errno)));
230       position = pos;
231     }
232 #else /* HAVE_FTRUNCATE */
233   throw new IOException (JvNewStringLatin1 ("FileDescriptor.setLength not implemented"));
234 #endif /* HAVE_FTRUNCATE */
235 }
236 
237 jint
seek(jlong pos,jint whence,jboolean eof_trunc)238 java::io::FileDescriptor::seek (jlong pos, jint whence, jboolean eof_trunc)
239 {
240   JvAssert (whence == SET || whence == CUR);
241 
242   if (eof_trunc)
243     {
244       jlong len = getLength ();
245       if (whence == SET)
246 	{
247 	  if (pos > len)
248 	    pos = len;
249 	}
250       else
251 	{
252 	  jlong here = getFilePointer ();
253 	  if (here + pos > len)
254 	    {
255 	      pos = len;
256 	      whence = SET;
257 	    }
258 	}
259     }
260 
261   off_t r = ::lseek (fd, (off_t) pos, whence == SET ? SEEK_SET : SEEK_CUR);
262   if (r == -1)
263     throw new IOException (JvNewStringLatin1 (strerror (errno)));
264   position = r;
265   return r;
266 }
267 
268 jlong
getLength(void)269 java::io::FileDescriptor::getLength (void)
270 {
271   struct stat sb;
272   if (::fstat (fd, &sb))
273     throw new IOException (JvNewStringLatin1 (strerror (errno)));
274   return sb.st_size;
275 }
276 
277 jlong
getFilePointer(void)278 java::io::FileDescriptor::getFilePointer (void)
279 {
280   return position;
281 }
282 
283 jint
read(void)284 java::io::FileDescriptor::read (void)
285 {
286   jbyte b;
287   int r;
288   do
289     {
290       r = ::read (fd, &b, 1);
291       if (r == 0)
292 	return -1;
293       if (r == -1)
294 	{
295 	  if (java::lang::Thread::interrupted())
296 	    {
297 	      InterruptedIOException *iioe
298 		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
299 	      iioe->bytesTransferred = r == -1 ? 0 : r;
300 	      throw iioe;
301 	    }
302 	  if (errno != EINTR)
303 	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
304 	}
305     }
306   while (r != 1);
307   position++;
308   return b & 0xFF;
309 }
310 
311 jint
read(jbyteArray buffer,jint offset,jint count)312 java::io::FileDescriptor::read (jbyteArray buffer, jint offset, jint count)
313 {
314   if (! buffer)
315     throw new java::lang::NullPointerException;
316   jsize bsize = JvGetArrayLength (buffer);
317   if (offset < 0 || count < 0 || offset + count > bsize)
318     throw new java::lang::ArrayIndexOutOfBoundsException;
319 
320   // Must return 0 if an attempt is made to read 0 bytes.
321   if (count == 0)
322     return 0;
323 
324   jbyte *bytes = elements (buffer) + offset;
325   int r;
326   do
327     {
328       r = ::read (fd, bytes, count);
329       if (r == 0)
330 	return -1;
331       if (r == -1)
332 	{
333 	  if (java::lang::Thread::interrupted())
334 	    {
335 	      InterruptedIOException *iioe
336 		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
337 	      iioe->bytesTransferred = r == -1 ? 0 : r;
338 	      throw iioe;
339 	    }
340 	  if (errno != EINTR)
341 	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
342 	}
343     }
344   while (r <= 0);
345   position += r;
346   return r;
347 }
348 
349 jint
available(void)350 java::io::FileDescriptor::available (void)
351 {
352 #if defined (FIONREAD) || defined (HAVE_SELECT) || defined (HAVE_FSTAT)
353   long num = 0;
354   int r = 0;
355   bool num_set = false;
356 
357 #if defined (FIONREAD)
358   r = ::ioctl (fd, FIONREAD, &num);
359   if (r == -1 && errno == ENOTTY)
360     {
361       // If the ioctl doesn't work, we don't care.
362       r = 0;
363       num = 0;
364     }
365   else
366     num_set = true;
367 #elif defined (HAVE_SELECT)
368   if (fd < 0)
369     {
370       errno = EBADF;
371       r = -1;
372     }
373 #endif
374 
375   if (r == -1)
376     {
377     posix_error:
378       throw new IOException (JvNewStringLatin1 (strerror (errno)));
379     }
380 
381   // If we didn't get anything, and we have fstat, then see if see if
382   // we're reading a regular file.  On many systems, FIONREAD does not
383   // work on regular files; select() likewise returns a useless
384   // result.  This is run incorrectly when FIONREAD does work on
385   // regular files and we are at the end of the file.  However, this
386   // case probably isn't very important.
387 #if defined (HAVE_FSTAT)
388   if (! num_set)
389     {
390       struct stat sb;
391       off_t where = 0;
392       if (fstat (fd, &sb) != -1
393 	  && S_ISREG (sb.st_mode)
394 	  && (where = lseek (fd, 0, SEEK_CUR)) != (off_t) -1)
395 	{
396 	  num = (long) (sb.st_size - where);
397 	  num_set = true;
398 	}
399     }
400 #endif /* HAVE_FSTAT */
401 
402 #if defined (HAVE_SELECT)
403   if (! num_set)
404     {
405       fd_set rd;
406       FD_ZERO (&rd);
407       FD_SET (fd, &rd);
408       struct timeval tv;
409       tv.tv_sec = 0;
410       tv.tv_usec = 0;
411       r = _Jv_select (fd + 1, &rd, NULL, NULL, &tv);
412       if (r == -1)
413 	goto posix_error;
414       num = r == 0 ? 0 : 1;
415     }
416 #endif /* HAVE_SELECT */
417 
418   return (jint) num;
419 #else
420   return 0;
421 #endif
422 }
423