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