1 /* FileChannelImpl.java --
2    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package gnu.java.nio.channels;
40 
41 import gnu.classpath.Configuration;
42 import gnu.java.nio.FileLockImpl;
43 
44 import java.io.File;
45 import java.io.FileNotFoundException;
46 import java.io.IOException;
47 import java.nio.ByteBuffer;
48 import java.nio.MappedByteBuffer;
49 import java.nio.channels.ClosedChannelException;
50 import java.nio.channels.FileChannel;
51 import java.nio.channels.FileLock;
52 import java.nio.channels.NonReadableChannelException;
53 import java.nio.channels.NonWritableChannelException;
54 import java.nio.channels.ReadableByteChannel;
55 import java.nio.channels.WritableByteChannel;
56 
57 /**
58  * This file is not user visible !
59  * But alas, Java does not have a concept of friendly packages
60  * so this class is public.
61  * Instances of this class are created by invoking getChannel
62  * Upon a Input/Output/RandomAccessFile object.
63  */
64 public final class FileChannelImpl extends FileChannel
65 {
66   // These are mode values for open().
67   public static final int READ   = 1;
68   public static final int WRITE  = 2;
69   public static final int APPEND = 4;
70 
71   // EXCL is used only when making a temp file.
72   public static final int EXCL   = 8;
73   public static final int SYNC   = 16;
74   public static final int DSYNC  = 32;
75 
76   public static FileChannelImpl in;
77   public static FileChannelImpl out;
78   public static FileChannelImpl err;
79 
init()80   private static native void init();
81 
82   static
83   {
84     if (Configuration.INIT_LOAD_LIBRARY)
85       {
86         System.loadLibrary("javanio");
87       }
88 
init()89     init();
90 
91     in  = new FileChannelImpl(0, READ);
92     out = new FileChannelImpl(1, WRITE);
93     err = new FileChannelImpl(2, WRITE);
94   }
95 
96   /**
97    * This is the actual native file descriptor value
98    */
99   // System's notion of file descriptor.  It might seem redundant to
100   // initialize this given that it is reassigned in the constructors.
101   // However, this is necessary because if open() throws an exception
102   // we want to make sure this has the value -1.  This is the most
103   // efficient way to accomplish that.
104   private int fd = -1;
105 
106   private int mode;
107 
108   final String description;
109 
110   /* Open a file.  MODE is a combination of the above mode flags. */
111   /* This is a static factory method, so that VM implementors can decide
112    * substitute subclasses of FileChannelImpl. */
create(File file, int mode)113   public static FileChannelImpl create(File file, int mode)
114     throws FileNotFoundException
115   {
116     return new FileChannelImpl(file, mode);
117   }
118 
FileChannelImpl(File file, int mode)119   private FileChannelImpl(File file, int mode)
120     throws FileNotFoundException
121   {
122     String path = file.getPath();
123     description = path;
124     fd = open (path, mode);
125     this.mode = mode;
126 
127     // First open the file and then check if it is a a directory
128     // to avoid race condition.
129     if (file.isDirectory())
130       {
131 	try
132 	  {
133 	      close();
134 	  }
135 	catch (IOException e)
136 	  {
137 	      /* ignore it */
138 	  }
139 
140 	throw new FileNotFoundException(description + " is a directory");
141       }
142   }
143 
144   /**
145    * Constructor for default channels in, out and err.
146    *
147    * Used by init() (native code).
148    *
149    * @param fd the file descriptor (0, 1, 2 for stdin, stdout, stderr).
150    *
151    * @param mode READ or WRITE
152    */
FileChannelImpl(int fd, int mode)153   FileChannelImpl (int fd, int mode)
154   {
155     this.fd = fd;
156     this.mode = mode;
157     this.description = "descriptor(" + fd + ")";
158   }
159 
open(String path, int mode)160   private native int open (String path, int mode) throws FileNotFoundException;
161 
available()162   public native int available () throws IOException;
implPosition()163   private native long implPosition () throws IOException;
seek(long newPosition)164   private native void seek (long newPosition) throws IOException;
implTruncate(long size)165   private native void implTruncate (long size) throws IOException;
166 
unlock(long pos, long len)167   public native void unlock (long pos, long len) throws IOException;
168 
size()169   public native long size () throws IOException;
170 
implCloseChannel()171   protected native void implCloseChannel() throws IOException;
172 
173   /**
174    * Makes sure the Channel is properly closed.
175    */
finalize()176   protected void finalize() throws IOException
177   {
178     if (fd != -1)
179       close();
180   }
181 
read(ByteBuffer dst)182   public int read (ByteBuffer dst) throws IOException
183   {
184     int result;
185     byte[] buffer = new byte [dst.remaining ()];
186 
187     result = read (buffer, 0, buffer.length);
188 
189     if (result > 0)
190       dst.put (buffer, 0, result);
191 
192     return result;
193   }
194 
read(ByteBuffer dst, long position)195   public int read (ByteBuffer dst, long position)
196     throws IOException
197   {
198     if (position < 0)
199       throw new IllegalArgumentException ("position: " + position);
200     long oldPosition = implPosition ();
201     position (position);
202     int result = read(dst);
203     position (oldPosition);
204 
205     return result;
206   }
207 
read()208   public native int read ()
209     throws IOException;
210 
read(byte[] buffer, int offset, int length)211   public native int read (byte[] buffer, int offset, int length)
212     throws IOException;
213 
read(ByteBuffer[] dsts, int offset, int length)214   public long read (ByteBuffer[] dsts, int offset, int length)
215     throws IOException
216   {
217     long result = 0;
218 
219     for (int i = offset; i < offset + length; i++)
220       {
221         result += read (dsts [i]);
222       }
223 
224     return result;
225   }
226 
write(ByteBuffer src)227   public int write (ByteBuffer src) throws IOException
228   {
229     int len = src.remaining ();
230     if (src.hasArray())
231       {
232 	byte[] buffer = src.array();
233 	write(buffer, src.arrayOffset() + src.position(), len);
234 	src.position(src.position() + len);
235       }
236     else
237       {
238 	// Use a more efficient native method! FIXME!
239 	byte[] buffer = new byte [len];
240     	src.get (buffer, 0, len);
241 	write (buffer, 0, len);
242       }
243     return len;
244   }
245 
write(ByteBuffer src, long position)246   public int write (ByteBuffer src, long position)
247     throws IOException
248   {
249     if (position < 0)
250       throw new IllegalArgumentException ("position: " + position);
251 
252     if (!isOpen ())
253       throw new ClosedChannelException ();
254 
255     if ((mode & WRITE) == 0)
256        throw new NonWritableChannelException ();
257 
258     int result;
259     long oldPosition;
260 
261     oldPosition = implPosition ();
262     seek (position);
263     result = write(src);
264     seek (oldPosition);
265 
266     return result;
267   }
268 
write(byte[] buffer, int offset, int length)269   public native void write (byte[] buffer, int offset, int length)
270     throws IOException;
271 
write(int b)272   public native void write (int b) throws IOException;
273 
write(ByteBuffer[] srcs, int offset, int length)274   public long write(ByteBuffer[] srcs, int offset, int length)
275     throws IOException
276   {
277     long result = 0;
278 
279     for (int i = offset;i < offset + length;i++)
280       {
281         result += write (srcs[i]);
282       }
283 
284     return result;
285   }
286 
mapImpl(char mode, long position, int size)287   public native MappedByteBuffer mapImpl (char mode, long position, int size)
288     throws IOException;
289 
map(FileChannel.MapMode mode, long position, long size)290   public MappedByteBuffer map (FileChannel.MapMode mode,
291 			       long position, long size)
292     throws IOException
293   {
294     char nmode = 0;
295     if (mode == MapMode.READ_ONLY)
296       {
297 	nmode = 'r';
298 	if ((this.mode & READ) == 0)
299 	  throw new NonReadableChannelException();
300       }
301     else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE)
302       {
303 	nmode = mode == MapMode.READ_WRITE ? '+' : 'c';
304 	if ((this.mode & (READ|WRITE)) != (READ|WRITE))
305 	  throw new NonWritableChannelException();
306       }
307     else
308       throw new IllegalArgumentException ("mode: " + mode);
309 
310     if (position < 0 || size < 0 || size > Integer.MAX_VALUE)
311       throw new IllegalArgumentException ("position: " + position
312 					  + ", size: " + size);
313     return mapImpl(nmode, position, (int) size);
314   }
315 
316   /**
317    * msync with the disk
318    */
force(boolean metaData)319   public void force (boolean metaData) throws IOException
320   {
321     if (!isOpen ())
322       throw new ClosedChannelException ();
323 
324     force ();
325   }
326 
force()327   private native void force ();
328 
329   // like transferTo, but with a count of less than 2Gbytes
smallTransferTo(long position, int count, WritableByteChannel target)330   private int smallTransferTo (long position, int count,
331 			       WritableByteChannel target)
332     throws IOException
333   {
334     ByteBuffer buffer;
335     try
336       {
337 	// Try to use a mapped buffer if we can.  If this fails for
338 	// any reason we'll fall back to using a ByteBuffer.
339 	buffer = map (MapMode.READ_ONLY, position, count);
340       }
341     catch (IOException e)
342       {
343 	buffer = ByteBuffer.allocate (count);
344 	read (buffer, position);
345 	buffer.flip();
346       }
347 
348     return target.write (buffer);
349   }
350 
transferTo(long position, long count, WritableByteChannel target)351   public long transferTo (long position, long count,
352 			  WritableByteChannel target)
353     throws IOException
354   {
355     if (position < 0
356         || count < 0)
357       throw new IllegalArgumentException ("position: " + position
358 					  + ", count: " + count);
359 
360     if (!isOpen ())
361       throw new ClosedChannelException ();
362 
363     if ((mode & READ) == 0)
364        throw new NonReadableChannelException ();
365 
366     final int pageSize = 65536;
367     long total = 0;
368 
369     while (count > 0)
370       {
371 	int transferred
372 	  = smallTransferTo (position, (int)Math.min (count, pageSize),
373 			     target);
374 	if (transferred < 0)
375 	  break;
376 	total += transferred;
377 	position += transferred;
378 	count -= transferred;
379       }
380 
381     return total;
382   }
383 
384   // like transferFrom, but with a count of less than 2Gbytes
smallTransferFrom(ReadableByteChannel src, long position, int count)385   private int smallTransferFrom (ReadableByteChannel src, long position,
386 				 int count)
387     throws IOException
388   {
389     ByteBuffer buffer = null;
390 
391     if (src instanceof FileChannel)
392       {
393 	try
394 	  {
395 	    // Try to use a mapped buffer if we can.  If this fails
396 	    // for any reason we'll fall back to using a ByteBuffer.
397 	    buffer = ((FileChannel)src).map (MapMode.READ_ONLY, position,
398 					     count);
399 	  }
400 	catch (IOException e)
401 	  {
402 	  }
403       }
404 
405     if (buffer == null)
406       {
407 	buffer = ByteBuffer.allocate ((int) count);
408 	src.read (buffer);
409 	buffer.flip();
410       }
411 
412     return write (buffer, position);
413   }
414 
transferFrom(ReadableByteChannel src, long position, long count)415   public long transferFrom (ReadableByteChannel src, long position,
416 			    long count)
417     throws IOException
418   {
419     if (position < 0
420         || count < 0)
421       throw new IllegalArgumentException ("position: " + position
422 					  + ", count: " + count);
423 
424     if (!isOpen ())
425       throw new ClosedChannelException ();
426 
427     if ((mode & WRITE) == 0)
428        throw new NonWritableChannelException ();
429 
430     final int pageSize = 65536;
431     long total = 0;
432 
433     while (count > 0)
434       {
435 	int transferred = smallTransferFrom (src, position,
436 					     (int)Math.min (count, pageSize));
437 	if (transferred < 0)
438 	  break;
439 	total += transferred;
440 	position += transferred;
441 	count -= transferred;
442       }
443 
444     return total;
445   }
446 
447   // Shared sanity checks between lock and tryLock methods.
lockCheck(long position, long size, boolean shared)448   private void lockCheck(long position, long size, boolean shared)
449     throws IOException
450   {
451     if (position < 0
452         || size < 0)
453       throw new IllegalArgumentException ("position: " + position
454 					  + ", size: " + size);
455 
456     if (!isOpen ())
457       throw new ClosedChannelException();
458 
459     if (shared && ((mode & READ) == 0))
460       throw new NonReadableChannelException();
461 
462     if (!shared && ((mode & WRITE) == 0))
463       throw new NonWritableChannelException();
464   }
465 
tryLock(long position, long size, boolean shared)466   public FileLock tryLock (long position, long size, boolean shared)
467     throws IOException
468   {
469     lockCheck(position, size, shared);
470 
471     boolean completed = false;
472     try
473       {
474 	begin();
475 	boolean lockable = lock(position, size, shared, false);
476 	completed = true;
477 	return (lockable
478 		? new FileLockImpl(this, position, size, shared)
479 		: null);
480       }
481     finally
482       {
483 	end(completed);
484       }
485   }
486 
487   /** Try to acquire a lock at the given position and size.
488    * On success return true.
489    * If wait as specified, block until we can get it.
490    * Otherwise return false.
491    */
lock(long position, long size, boolean shared, boolean wait)492   private native boolean lock(long position, long size,
493 			      boolean shared, boolean wait) throws IOException;
494 
lock(long position, long size, boolean shared)495   public FileLock lock (long position, long size, boolean shared)
496     throws IOException
497   {
498     lockCheck(position, size, shared);
499 
500     boolean completed = false;
501     try
502       {
503 	boolean lockable = lock(position, size, shared, true);
504 	completed = true;
505 	return (lockable
506 		? new FileLockImpl(this, position, size, shared)
507 		: null);
508       }
509     finally
510       {
511 	end(completed);
512       }
513   }
514 
position()515   public long position ()
516     throws IOException
517   {
518     if (!isOpen ())
519       throw new ClosedChannelException ();
520 
521     return implPosition ();
522   }
523 
position(long newPosition)524   public FileChannel position (long newPosition)
525     throws IOException
526   {
527     if (newPosition < 0)
528       throw new IllegalArgumentException ("newPostition: " + newPosition);
529 
530     if (!isOpen ())
531       throw new ClosedChannelException ();
532 
533     // FIXME note semantics if seeking beyond eof.
534     // We should seek lazily - only on a write.
535     seek (newPosition);
536     return this;
537   }
538 
truncate(long size)539   public FileChannel truncate (long size)
540     throws IOException
541   {
542     if (size < 0)
543       throw new IllegalArgumentException ("size: " + size);
544 
545     if (!isOpen ())
546       throw new ClosedChannelException ();
547 
548     if ((mode & WRITE) == 0)
549        throw new NonWritableChannelException ();
550 
551     if (size < size ())
552       implTruncate (size);
553 
554     return this;
555   }
556 
toString()557   public String toString()
558   {
559     return (this.getClass()
560 	    + "[fd=" + fd
561 	    + ",mode=" + mode + ","
562 	    + description + "]");
563   }
564 }
565