1 /* FileChannelImpl.java --
2    Copyright (C) 2002, 2004, 2005, 2006  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 
init()76   private static native void init();
77 
78   static
79   {
80     if (Configuration.INIT_LOAD_LIBRARY)
81       {
82         System.loadLibrary("javanio");
83       }
84 
init()85     init();
86   }
87 
88   /**
89    * This is the actual native file descriptor value
90    */
91   // System's notion of file descriptor.  It might seem redundant to
92   // initialize this given that it is reassigned in the constructors.
93   // However, this is necessary because if open() throws an exception
94   // we want to make sure this has the value -1.  This is the most
95   // efficient way to accomplish that.
96   private int fd = -1;
97 
98   private long pos;
99   private int mode;
100 
FileChannelImpl()101   public FileChannelImpl ()
102   {
103   }
104 
105   /* Open a file.  MODE is a combination of the above mode flags. */
106   /* This is a static factory method, so that VM implementors can decide
107    * substitute subclasses of FileChannelImpl. */
create(File file, int mode)108   public static FileChannelImpl create(File file, int mode)
109     throws FileNotFoundException
110   {
111     return new FileChannelImpl(file, mode);
112   }
113 
114   /* Open a file.  MODE is a combination of the above mode flags. */
FileChannelImpl(File file, int mode)115   private FileChannelImpl (File file, int mode) throws FileNotFoundException
116   {
117     final String path = file.getPath();
118     fd = open (path, mode);
119     this.mode = mode;
120 
121     // First open the file and then check if it is a a directory
122     // to avoid race condition.
123     if (file.isDirectory())
124       {
125 	try
126 	  {
127 	      close();
128 	  }
129 	catch (IOException e)
130 	  {
131 	      /* ignore it */
132 	  }
133 
134 	throw new FileNotFoundException(path + " is a directory");
135       }
136   }
137 
138   /* Used by init() (native code) */
FileChannelImpl(int fd, int mode)139   FileChannelImpl (int fd, int mode)
140   {
141     this.fd = fd;
142     this.mode = mode;
143   }
144 
145   public static FileChannelImpl in;
146   public static FileChannelImpl out;
147   public static FileChannelImpl err;
148 
open(String path, int mode)149   private native int open (String path, int mode) throws FileNotFoundException;
150 
available()151   public native int available () throws IOException;
implPosition()152   private native long implPosition () throws IOException;
seek(long newPosition)153   private native void seek (long newPosition) throws IOException;
implTruncate(long size)154   private native void implTruncate (long size) throws IOException;
155 
unlock(long pos, long len)156   public native void unlock (long pos, long len) throws IOException;
157 
size()158   public native long size () throws IOException;
159 
implCloseChannel()160   protected native void implCloseChannel() throws IOException;
161 
162   /**
163    * Makes sure the Channel is properly closed.
164    */
finalize()165   protected void finalize() throws IOException
166   {
167     this.close();
168   }
169 
read(ByteBuffer dst)170   public int read (ByteBuffer dst) throws IOException
171   {
172     int result;
173     byte[] buffer = new byte [dst.remaining ()];
174 
175     result = read (buffer, 0, buffer.length);
176 
177     if (result > 0)
178       dst.put (buffer, 0, result);
179 
180     return result;
181   }
182 
read(ByteBuffer dst, long position)183   public int read (ByteBuffer dst, long position)
184     throws IOException
185   {
186     if (position < 0)
187       throw new IllegalArgumentException ();
188     long oldPosition = implPosition ();
189     position (position);
190     int result = read(dst);
191     position (oldPosition);
192 
193     return result;
194   }
195 
read()196   public native int read ()
197     throws IOException;
198 
read(byte[] buffer, int offset, int length)199   public native int read (byte[] buffer, int offset, int length)
200     throws IOException;
201 
read(ByteBuffer[] dsts, int offset, int length)202   public long read (ByteBuffer[] dsts, int offset, int length)
203     throws IOException
204   {
205     long result = 0;
206 
207     for (int i = offset; i < offset + length; i++)
208       {
209         result += read (dsts [i]);
210       }
211 
212     return result;
213   }
214 
write(ByteBuffer src)215   public int write (ByteBuffer src) throws IOException
216   {
217     int len = src.remaining ();
218     if (src.hasArray())
219       {
220 	byte[] buffer = src.array();
221 	write(buffer, src.arrayOffset() + src.position(), len);
222 	src.position(src.position() + len);
223       }
224     else
225       {
226 	// Use a more efficient native method! FIXME!
227 	byte[] buffer = new byte [len];
228     	src.get (buffer, 0, len);
229 	write (buffer, 0, len);
230       }
231     return len;
232   }
233 
write(ByteBuffer src, long position)234   public int write (ByteBuffer src, long position)
235     throws IOException
236   {
237     if (position < 0)
238       throw new IllegalArgumentException ();
239 
240     if (!isOpen ())
241       throw new ClosedChannelException ();
242 
243     if ((mode & WRITE) == 0)
244        throw new NonWritableChannelException ();
245 
246     int result;
247     long oldPosition;
248 
249     oldPosition = implPosition ();
250     seek (position);
251     result = write(src);
252     seek (oldPosition);
253 
254     return result;
255   }
256 
write(byte[] buffer, int offset, int length)257   public native void write (byte[] buffer, int offset, int length)
258     throws IOException;
259 
write(int b)260   public native void write (int b) throws IOException;
261 
write(ByteBuffer[] srcs, int offset, int length)262   public long write(ByteBuffer[] srcs, int offset, int length)
263     throws IOException
264   {
265     long result = 0;
266 
267     for (int i = offset;i < offset + length;i++)
268       {
269         result += write (srcs[i]);
270       }
271 
272     return result;
273   }
274 
mapImpl(char mode, long position, int size)275   public native MappedByteBuffer mapImpl (char mode, long position, int size)
276     throws IOException;
277 
map(FileChannel.MapMode mode, long position, long size)278   public MappedByteBuffer map (FileChannel.MapMode mode,
279 			       long position, long size)
280     throws IOException
281   {
282     char nmode = 0;
283     if (mode == MapMode.READ_ONLY)
284       {
285 	nmode = 'r';
286 	if ((this.mode & READ) == 0)
287 	  throw new NonReadableChannelException();
288       }
289     else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE)
290       {
291 	nmode = mode == MapMode.READ_WRITE ? '+' : 'c';
292 	if ((this.mode & (READ|WRITE)) != (READ|WRITE))
293 	  throw new NonWritableChannelException();
294       }
295     else
296       throw new IllegalArgumentException ();
297 
298     if (position < 0 || size < 0 || size > Integer.MAX_VALUE)
299       throw new IllegalArgumentException ();
300     return mapImpl(nmode, position, (int) size);
301   }
302 
303   /**
304    * msync with the disk
305    */
force(boolean metaData)306   public void force (boolean metaData) throws IOException
307   {
308     if (!isOpen ())
309       throw new ClosedChannelException ();
310   }
311 
312   // like transferTo, but with a count of less than 2Gbytes
smallTransferTo(long position, int count, WritableByteChannel target)313   private int smallTransferTo (long position, int count,
314 			       WritableByteChannel target)
315     throws IOException
316   {
317     ByteBuffer buffer;
318     try
319       {
320 	// Try to use a mapped buffer if we can.  If this fails for
321 	// any reason we'll fall back to using a ByteBuffer.
322 	buffer = map (MapMode.READ_ONLY, position, count);
323       }
324     catch (IOException e)
325       {
326 	buffer = ByteBuffer.allocate (count);
327 	read (buffer, position);
328 	buffer.flip();
329       }
330 
331     return target.write (buffer);
332   }
333 
transferTo(long position, long count, WritableByteChannel target)334   public long transferTo (long position, long count,
335 			  WritableByteChannel target)
336     throws IOException
337   {
338     if (position < 0
339         || count < 0)
340       throw new IllegalArgumentException ();
341 
342     if (!isOpen ())
343       throw new ClosedChannelException ();
344 
345     if ((mode & READ) == 0)
346        throw new NonReadableChannelException ();
347 
348     final int pageSize = 65536;
349     long total = 0;
350 
351     while (count > 0)
352       {
353 	int transferred
354 	  = smallTransferTo (position, (int)Math.min (count, pageSize),
355 			     target);
356 	if (transferred < 0)
357 	  break;
358 	total += transferred;
359 	position += transferred;
360 	count -= transferred;
361       }
362 
363     return total;
364   }
365 
366   // like transferFrom, but with a count of less than 2Gbytes
smallTransferFrom(ReadableByteChannel src, long position, int count)367   private int smallTransferFrom (ReadableByteChannel src, long position,
368 				 int count)
369     throws IOException
370   {
371     ByteBuffer buffer = null;
372 
373     if (src instanceof FileChannel)
374       {
375 	try
376 	  {
377 	    // Try to use a mapped buffer if we can.  If this fails
378 	    // for any reason we'll fall back to using a ByteBuffer.
379 	    buffer = ((FileChannel)src).map (MapMode.READ_ONLY, position,
380 					     count);
381 	  }
382 	catch (IOException e)
383 	  {
384 	  }
385       }
386 
387     if (buffer == null)
388       {
389 	buffer = ByteBuffer.allocate ((int) count);
390 	src.read (buffer);
391 	buffer.flip();
392       }
393 
394     return write (buffer, position);
395   }
396 
transferFrom(ReadableByteChannel src, long position, long count)397   public long transferFrom (ReadableByteChannel src, long position,
398 			    long count)
399     throws IOException
400   {
401     if (position < 0
402         || count < 0)
403       throw new IllegalArgumentException ();
404 
405     if (!isOpen ())
406       throw new ClosedChannelException ();
407 
408     if ((mode & WRITE) == 0)
409        throw new NonWritableChannelException ();
410 
411     final int pageSize = 65536;
412     long total = 0;
413 
414     while (count > 0)
415       {
416 	int transferred = smallTransferFrom (src, position,
417 					     (int)Math.min (count, pageSize));
418 	if (transferred < 0)
419 	  break;
420 	total += transferred;
421 	position += transferred;
422 	count -= transferred;
423       }
424 
425     return total;
426   }
427 
tryLock(long position, long size, boolean shared)428   public FileLock tryLock (long position, long size, boolean shared)
429     throws IOException
430   {
431     if (position < 0
432         || size < 0)
433       throw new IllegalArgumentException ();
434 
435     if (!isOpen ())
436       throw new ClosedChannelException ();
437 
438     if (shared && (mode & READ) == 0)
439       throw new NonReadableChannelException ();
440 
441     if (!shared && (mode & WRITE) == 0)
442       throw new NonWritableChannelException ();
443 
444     boolean completed = false;
445 
446     try
447       {
448 	begin();
449 	boolean lockable = lock(position, size, shared, false);
450 	completed = true;
451 	return (lockable
452 		? new FileLockImpl(this, position, size, shared)
453 		: null);
454       }
455     finally
456       {
457 	end(completed);
458       }
459   }
460 
461   /** Try to acquire a lock at the given position and size.
462    * On success return true.
463    * If wait as specified, block until we can get it.
464    * Otherwise return false.
465    */
lock(long position, long size, boolean shared, boolean wait)466   private native boolean lock(long position, long size,
467 			      boolean shared, boolean wait) throws IOException;
468 
lock(long position, long size, boolean shared)469   public FileLock lock (long position, long size, boolean shared)
470     throws IOException
471   {
472     if (position < 0
473         || size < 0)
474       throw new IllegalArgumentException ();
475 
476     if (!isOpen ())
477       throw new ClosedChannelException ();
478 
479     boolean completed = false;
480 
481     try
482       {
483 	boolean lockable = lock(position, size, shared, true);
484 	completed = true;
485 	return (lockable
486 		? new FileLockImpl(this, position, size, shared)
487 		: null);
488       }
489     finally
490       {
491 	end(completed);
492       }
493   }
494 
position()495   public long position ()
496     throws IOException
497   {
498     if (!isOpen ())
499       throw new ClosedChannelException ();
500 
501     return implPosition ();
502   }
503 
position(long newPosition)504   public FileChannel position (long newPosition)
505     throws IOException
506   {
507     if (newPosition < 0)
508       throw new IllegalArgumentException ();
509 
510     if (!isOpen ())
511       throw new ClosedChannelException ();
512 
513     // FIXME note semantics if seeking beyond eof.
514     // We should seek lazily - only on a write.
515     seek (newPosition);
516     return this;
517   }
518 
truncate(long size)519   public FileChannel truncate (long size)
520     throws IOException
521   {
522     if (size < 0)
523       throw new IllegalArgumentException ();
524 
525     if (!isOpen ())
526       throw new ClosedChannelException ();
527 
528     if ((mode & WRITE) == 0)
529        throw new NonWritableChannelException ();
530 
531     if (size < size ())
532       implTruncate (size);
533 
534     return this;
535   }
536 
537   /**
538    * @return The native file descriptor.
539    */
getNativeFD()540   public int getNativeFD()
541   {
542     return fd;
543   }
544 }
545