natFileDescriptorPosix.cc 9.55 KB
Newer Older
Tom Tromey committed
1 2
// natFileDescriptor.cc - Native part of FileDescriptor class.

3
/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Free Software Foundation
Tom Tromey committed
4 5 6 7 8 9 10 11 12

   This file is part of libgcj.

This software is copyrighted work licensed under the terms of the
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
details.  */

#include <config.h>

Tom Tromey committed
13 14
#include "posix.h"

Tom Tromey committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/param.h>

#ifdef HAVE_SYS_IOCTL_H
#define BSD_COMP /* Get FIONREAD on Solaris2. */
#include <sys/ioctl.h>
#endif

// Pick up FIONREAD on Solaris 2.5.
#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif

Tom Tromey committed
31
#include <gcj/cni.h>
Tom Tromey committed
32 33 34 35 36 37 38 39
#include <jvm.h>
#include <java/io/FileDescriptor.h>
#include <java/io/SyncFailedException.h>
#include <java/io/IOException.h>
#include <java/io/InterruptedIOException.h>
#include <java/io/EOFException.h>
#include <java/lang/ArrayIndexOutOfBoundsException.h>
#include <java/lang/NullPointerException.h>
40
#include <java/lang/System.h>
Tom Tromey committed
41 42 43 44 45 46
#include <java/lang/String.h>
#include <java/lang/Thread.h>
#include <java/io/FileNotFoundException.h>

#define NO_FSYNC_MESSAGE "sync unsupported"

47
void
48
java::io::FileDescriptor::init (void)
49
{
50 51 52
  in = new java::io::FileDescriptor(0);
  out = new java::io::FileDescriptor(1);
  err = new java::io::FileDescriptor(2);
53 54
}

Tom Tromey committed
55 56 57 58
jboolean
java::io::FileDescriptor::valid (void)
{
  struct stat sb;
59
  return fd >= 0 && ::fstat (fd, &sb) == 0;
Tom Tromey committed
60 61 62 63 64 65 66 67 68
}

void
java::io::FileDescriptor::sync (void)
{
  // Some files don't support fsync.  We don't bother reporting these
  // as errors.
#ifdef HAVE_FSYNC
  if (::fsync (fd) && errno != EROFS && errno != EINVAL)
69
    throw new SyncFailedException (JvNewStringLatin1 (strerror (errno)));
Tom Tromey committed
70
#else
71
  throw new SyncFailedException (JvNewStringLatin1 (NO_FSYNC_MESSAGE));
Tom Tromey committed
72 73 74 75 76 77
#endif
}

jint
java::io::FileDescriptor::open (jstring path, jint jflags)
{
78
  char *buf = (char *) _Jv_AllocBytes (_Jv_GetStringUTFLength (path) + 1);
Tom Tromey committed
79 80 81 82 83 84 85 86
  jsize total = JvGetStringUTFRegion (path, 0, path->length(), buf);
  buf[total] = '\0';
  int flags = 0;
#ifdef O_BINARY
  flags |= O_BINARY;
#endif

  JvAssert ((jflags & READ) || (jflags & WRITE));
87
  int mode = 0666;
Tom Tromey committed
88
  if ((jflags & READ) && (jflags & WRITE))
89
    flags |= O_RDWR | O_CREAT;
Tom Tromey committed
90 91 92 93 94 95 96 97 98
  else if ((jflags & READ))
    flags |= O_RDONLY;
  else
    {
      flags |= O_WRONLY | O_CREAT;
      if ((jflags & APPEND))
	flags |= O_APPEND;
      else
	flags |= O_TRUNC;
99 100 101 102 103 104 105

      if ((jflags & EXCL))
	{
	  flags |= O_EXCL;
	  // In this case we are making a temp file.
	  mode = 0600;
	}
Tom Tromey committed
106 107
    }

108 109 110 111 112 113
  if ((jflags & SYNC))
    flags |= O_SYNC;

  if ((jflags & DSYNC))
    flags |= O_DSYNC;

114
  int fd = ::open (buf, flags, mode);
115 116 117 118 119 120 121
  if (fd == -1 && errno == EMFILE)
    {
      // Because finalize () calls close () we might be able to continue.
      java::lang::System::gc ();
      java::lang::System::runFinalization ();
      fd = ::open (buf, flags, mode);
    }
Tom Tromey committed
122 123 124
  if (fd == -1)
    {
      char msg[MAXPATHLEN + 200];
125 126 127
      // We choose the formatting here for JDK compatibility, believe
      // it or not.
      sprintf (msg, "%s (%s)", buf, strerror (errno));
128
      throw new FileNotFoundException (JvNewStringLatin1 (msg));
Tom Tromey committed
129
    }
130 131 132

  _Jv_platform_close_on_exec (fd);

Tom Tromey committed
133 134 135 136 137 138 139
  return fd;
}

void
java::io::FileDescriptor::write (jint b)
{
  jbyte d = (jbyte) b;
140 141
  int r = 0;
  while (r != 1)
Tom Tromey committed
142
    {
143
      r = ::write (fd, &d, 1);
144 145 146 147 148 149 150 151 152
      if (r == -1)
        {
	  if (java::lang::Thread::interrupted())
	    {
	      InterruptedIOException *iioe
		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
	      iioe->bytesTransferred = r == -1 ? 0 : r;
	      throw iioe;
	    }	    
153 154
	  if (errno != EINTR)
	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
155
	}
Tom Tromey committed
156
    }
157
  position++;
Tom Tromey committed
158 159 160 161 162 163
}

void
java::io::FileDescriptor::write (jbyteArray b, jint offset, jint len)
{
  if (! b)
164
    throw new java::lang::NullPointerException;
Tom Tromey committed
165
  if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
166
    throw new java::lang::ArrayIndexOutOfBoundsException;
Tom Tromey committed
167
  jbyte *bytes = elements (b) + offset;
168 169 170

  int written = 0;
  while (len > 0)
Tom Tromey committed
171
    {
172
      int r = ::write (fd, bytes, len);
173 174 175 176 177 178 179 180 181
      if (r == -1)
        {
	  if (java::lang::Thread::interrupted())
	    {
	      InterruptedIOException *iioe
		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
	      iioe->bytesTransferred = written;
	      throw iioe;
	    }
182 183
	  if (errno != EINTR)
	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
184 185
	}

186
      written += r;
187 188
      len -= r;
      bytes += r;
189
      position += r;
Tom Tromey committed
190 191 192 193 194 195 196 197 198
    }
}

void
java::io::FileDescriptor::close (void)
{
  jint save = fd;
  fd = -1;
  if (::close (save))
199
    throw new IOException (JvNewStringLatin1 (strerror (errno)));
Tom Tromey committed
200 201
}

202 203 204 205 206
void
java::io::FileDescriptor::setLength (jlong pos)
{
  struct stat sb;

207
#ifdef HAVE_FTRUNCATE
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
  if (::fstat (fd, &sb))
    throw new IOException (JvNewStringLatin1 (strerror (errno)));

  if ((jlong) sb.st_size == pos) 
    return;

  // If the file is too short, we extend it.  We can't rely on
  // ftruncate() extending the file.  So we lseek() to 1 byte less
  // than we want, and then we write a single byte at the end.
  if ((jlong) sb.st_size < pos)
    {
      if (::lseek (fd, (off_t) (pos - 1), SEEK_SET) == -1)
	throw new IOException (JvNewStringLatin1 (strerror (errno)));
      char out = '\0';
      int r = ::write (fd, &out, 1);
223
      if (r <= 0 || ::lseek (fd, position, SEEK_SET) == -1)
224 225
	throw new IOException (JvNewStringLatin1 (strerror (errno)));
    }
226 227 228 229 230 231
  else
    {
      if (::ftruncate (fd, (off_t) pos))
	throw new IOException (JvNewStringLatin1 (strerror (errno)));
      position = pos;
    }
232 233 234
#else /* HAVE_FTRUNCATE */
  throw new IOException (JvNewStringLatin1 ("FileDescriptor.setLength not implemented"));
#endif /* HAVE_FTRUNCATE */
235 236
}

Tom Tromey committed
237
jint
238
java::io::FileDescriptor::seek (jlong pos, jint whence, jboolean eof_trunc)
Tom Tromey committed
239 240 241
{
  JvAssert (whence == SET || whence == CUR);

242
  if (eof_trunc)
243
    {
244
      jlong len = getLength ();
245 246 247 248 249 250 251 252 253 254 255 256 257 258
      if (whence == SET)
	{
	  if (pos > len)
	    pos = len;
	}
      else
	{
	  jlong here = getFilePointer ();
	  if (here + pos > len)
	    {
	      pos = len;
	      whence = SET;
	    }
	}
259
    }
Tom Tromey committed
260 261 262

  off_t r = ::lseek (fd, (off_t) pos, whence == SET ? SEEK_SET : SEEK_CUR);
  if (r == -1)
263
    throw new IOException (JvNewStringLatin1 (strerror (errno)));
264
  position = r;
Tom Tromey committed
265 266 267 268
  return r;
}

jlong
269
java::io::FileDescriptor::getLength (void)
Tom Tromey committed
270 271 272
{
  struct stat sb;
  if (::fstat (fd, &sb))
273
    throw new IOException (JvNewStringLatin1 (strerror (errno)));
Tom Tromey committed
274 275 276 277 278 279
  return sb.st_size;
}

jlong
java::io::FileDescriptor::getFilePointer (void)
{
280
  return position;
Tom Tromey committed
281 282 283 284 285 286
}

jint
java::io::FileDescriptor::read (void)
{
  jbyte b;
287 288
  int r;
  do
Tom Tromey committed
289
    {
290 291 292 293
      r = ::read (fd, &b, 1);
      if (r == 0)
	return -1;
      if (r == -1)
294
	{
295 296 297 298 299 300 301 302 303
	  if (java::lang::Thread::interrupted())
	    {
	      InterruptedIOException *iioe
		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
	      iioe->bytesTransferred = r == -1 ? 0 : r;
	      throw iioe;
	    }
	  if (errno != EINTR)
	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
304
	}
Tom Tromey committed
305
    }
306
  while (r != 1);
307
  position++;
Tom Tromey committed
308 309 310 311 312 313 314
  return b & 0xFF;
}

jint
java::io::FileDescriptor::read (jbyteArray buffer, jint offset, jint count)
{
  if (! buffer)
315
    throw new java::lang::NullPointerException;
Tom Tromey committed
316 317
  jsize bsize = JvGetArrayLength (buffer);
  if (offset < 0 || count < 0 || offset + count > bsize)
318
    throw new java::lang::ArrayIndexOutOfBoundsException;
319 320 321 322 323

  // Must return 0 if an attempt is made to read 0 bytes.
  if (count == 0)
    return 0;

Tom Tromey committed
324
  jbyte *bytes = elements (buffer) + offset;
325 326 327 328 329 330 331
  int r;
  do
    {
      r = ::read (fd, bytes, count);
      if (r == 0)
	return -1;
      if (r == -1)
332
	{
333 334 335 336 337 338 339 340 341
	  if (java::lang::Thread::interrupted())
	    {
	      InterruptedIOException *iioe
		= new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
	      iioe->bytesTransferred = r == -1 ? 0 : r;
	      throw iioe;
	    }
	  if (errno != EINTR)
	    throw new IOException (JvNewStringLatin1 (strerror (errno)));
342
	}
Tom Tromey committed
343
    }
344
  while (r <= 0);
345
  position += r;
Tom Tromey committed
346 347 348 349 350 351
  return r;
}

jint
java::io::FileDescriptor::available (void)
{
352 353 354 355 356
#if defined (FIONREAD) || defined (HAVE_SELECT) || defined (HAVE_FSTAT)
  long num = 0;
  int r = 0;
  bool num_set = false;

Tom Tromey committed
357
#if defined (FIONREAD)
358 359 360 361 362 363 364 365 366
  r = ::ioctl (fd, FIONREAD, &num);
  if (r == -1 && errno == ENOTTY)
    {
      // If the ioctl doesn't work, we don't care.
      r = 0;
      num = 0;
    }
  else
    num_set = true;
Tom Tromey committed
367 368
#elif defined (HAVE_SELECT)
  if (fd < 0)
369 370 371 372 373 374 375 376 377
    {
      errno = EBADF;
      r = -1;
    }
#endif

  if (r == -1)
    {
    posix_error:
378
      throw new IOException (JvNewStringLatin1 (strerror (errno)));
379 380 381 382 383 384 385 386 387 388 389 390
    }

  // If we didn't get anything, and we have fstat, then see if see if
  // we're reading a regular file.  On many systems, FIONREAD does not
  // work on regular files; select() likewise returns a useless
  // result.  This is run incorrectly when FIONREAD does work on
  // regular files and we are at the end of the file.  However, this
  // case probably isn't very important.
#if defined (HAVE_FSTAT)
  if (! num_set)
    {
      struct stat sb;
391
      off_t where = 0;
392 393
      if (fstat (fd, &sb) != -1
	  && S_ISREG (sb.st_mode)
394
	  && (where = lseek (fd, 0, SEEK_CUR)) != (off_t) -1)
395 396 397 398 399 400 401 402 403
	{
	  num = (long) (sb.st_size - where);
	  num_set = true;
	}
    }
#endif /* HAVE_FSTAT */

#if defined (HAVE_SELECT)
  if (! num_set)
Tom Tromey committed
404 405 406 407 408 409 410
    {
      fd_set rd;
      FD_ZERO (&rd);
      FD_SET (fd, &rd);
      struct timeval tv;
      tv.tv_sec = 0;
      tv.tv_usec = 0;
Tom Tromey committed
411
      r = _Jv_select (fd + 1, &rd, NULL, NULL, &tv);
412 413 414
      if (r == -1)
	goto posix_error;
      num = r == 0 ? 0 : 1;
Tom Tromey committed
415
    }
416 417 418
#endif /* HAVE_SELECT */

  return (jint) num;
Tom Tromey committed
419
#else
420
  return 0;
Tom Tromey committed
421 422
#endif
}