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