1 /* 2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.ch; 27 28 import java.io.FileDescriptor; 29 import java.io.IOException; 30 import java.nio.ByteBuffer; 31 import java.nio.MappedByteBuffer; 32 import java.nio.channels.ClosedByInterruptException; 33 import java.nio.channels.ClosedChannelException; 34 import java.nio.channels.FileChannel; 35 import java.nio.channels.FileLock; 36 import java.nio.channels.FileLockInterruptionException; 37 import java.nio.channels.NonReadableChannelException; 38 import java.nio.channels.NonWritableChannelException; 39 import java.nio.channels.OverlappingFileLockException; 40 import java.nio.channels.ReadableByteChannel; 41 import java.nio.channels.SelectableChannel; 42 import java.nio.channels.WritableByteChannel; 43 import java.util.ArrayList; 44 import java.util.List; 45 46 import jdk.internal.misc.JavaIOFileDescriptorAccess; 47 import jdk.internal.misc.JavaNioAccess; 48 import jdk.internal.misc.SharedSecrets; 49 import jdk.internal.ref.Cleaner; 50 import sun.security.action.GetPropertyAction; 51 52 import java.nio.file.FileStore; 53 import java.nio.file.Files; 54 import java.nio.file.Path; 55 import java.nio.file.Paths; 56 import java.nio.file.FileSystemException; 57 58 public class FileChannelImpl 59 extends FileChannel 60 { 61 // Memory allocation size for mapping buffers 62 private static final long allocationGranularity; 63 64 // Access to FileDispatcher internals 65 private static final JavaIOFileDescriptorAccess fdAccess = 66 SharedSecrets.getJavaIOFileDescriptorAccess(); 67 68 // Used to make native read and write calls 69 private final FileDispatcher nd; 70 71 // File descriptor 72 private final FileDescriptor fd; 73 74 // File access mode (immutable) 75 private final boolean writable; 76 private final boolean readable; 77 78 // Required to prevent finalization of creating stream (immutable) 79 private final Object parent; 80 81 // The path of the referenced file 82 // (null if the parent stream is created with a file descriptor) 83 private final String path; 84 85 // Thread-safe set of IDs of native threads, for signalling 86 private final NativeThreadSet threads = new NativeThreadSet(2); 87 88 // Lock for operations involving position and size 89 private final Object positionLock = new Object(); 90 91 // Positional-read is not interruptible 92 private volatile boolean uninterruptible; 93 94 //directIO 95 private volatile boolean direct; 96 97 //IO alignment for DirectIO 98 private volatile int alignment = -1; 99 100 private FileChannelImpl(FileDescriptor fd, String path, boolean readable, 101 boolean writable, boolean direct, Object parent) 102 { 103 this.fd = fd; 104 this.readable = readable; 105 this.writable = writable; 106 this.parent = parent; 107 this.path = path; 108 this.direct = direct; 109 if (direct) 110 setDirectIO(); 111 this.nd = new FileDispatcherImpl(); 112 } 113 114 private void setDirectIO() 115 { 116 if (alignment == -1) { 117 Path fp = Paths.get(path); 118 try { 119 setDirect0(fd); 120 FileStore fs = Files.getFileStore(fp); 121 this.alignment = fs.getBlockSize(); 122 } catch (IOException e) { 123 this.alignment = -1; 124 throw new UnsupportedOperationException("Error setting up DirectIO"); 125 } 126 } 127 } 128 129 // Used by FileInputStream.getChannel(), FileOutputStream.getChannel 130 // and RandomAccessFile.getChannel() 131 public static FileChannel open(FileDescriptor fd, String path, 132 boolean readable, boolean writable, 133 Object parent) 134 { 135 return new FileChannelImpl(fd, path, readable, writable, false, parent); 136 } 137 138 public static FileChannel open(FileDescriptor fd, String path, 139 boolean readable, boolean writable, 140 boolean direct, Object parent) 141 { 142 return new FileChannelImpl(fd, path, readable, writable, direct, parent); 143 } 144 145 private void ensureOpen() throws IOException { 146 if (!isOpen()) 147 throw new ClosedChannelException(); 148 } 149 150 public void setUninterruptible() { 151 uninterruptible = true; 152 } 153 154 // -- Standard channel operations -- 155 156 protected void implCloseChannel() throws IOException { 157 if (!fd.valid()) 158 return; // nothing to do 159 160 // Release and invalidate any locks that we still hold 161 if (fileLockTable != null) { 162 for (FileLock fl: fileLockTable.removeAll()) { 163 synchronized (fl) { 164 if (fl.isValid()) { 165 nd.release(fd, fl.position(), fl.size()); 166 ((FileLockImpl)fl).invalidate(); 167 } 168 } 169 } 170 } 171 172 // signal any threads blocked on this channel 173 threads.signalAndWait(); 174 175 if (parent != null) { 176 177 // Close the fd via the parent stream's close method. The parent 178 // will reinvoke our close method, which is defined in the 179 // superclass AbstractInterruptibleChannel, but the isOpen logic in 180 // that method will prevent this method from being reinvoked. 181 // 182 ((java.io.Closeable)parent).close(); 183 } else { 184 nd.close(fd); 185 } 186 187 } 188 189 public int read(ByteBuffer dst) throws IOException { 190 if (direct && ((dst.remaining() % alignment != 0) || 191 (position() % alignment != 0))) 192 throw new IllegalArgumentException("IO size or position is not aligned"); 193 ensureOpen(); 194 if (!readable) 195 throw new NonReadableChannelException(); 196 synchronized (positionLock) { 197 int n = 0; 198 int ti = -1; 199 try { 200 begin(); 201 ti = threads.add(); 202 if (!isOpen()) 203 return 0; 204 do { 205 n = IOUtil.read(fd, dst, -1, direct, alignment, nd); 206 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 207 return IOStatus.normalize(n); 208 } finally { 209 threads.remove(ti); 210 end(n > 0); 211 assert IOStatus.check(n); 212 } 213 } 214 } 215 216 public long read(ByteBuffer[] dsts, int offset, int length) 217 throws IOException 218 { 219 if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) 220 throw new IndexOutOfBoundsException(); 221 222 if (direct && (position() % alignment != 0)) 223 throw new IllegalArgumentException("Position is not aligned"); 224 225 ensureOpen(); 226 if (!readable) 227 throw new NonReadableChannelException(); 228 synchronized (positionLock) { 229 long n = 0; 230 int ti = -1; 231 try { 232 begin(); 233 ti = threads.add(); 234 if (!isOpen()) 235 return 0; 236 do { 237 n = IOUtil.read(fd, dsts, offset, length, direct, alignment, nd); 238 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 239 return IOStatus.normalize(n); 240 } finally { 241 threads.remove(ti); 242 end(n > 0); 243 assert IOStatus.check(n); 244 } 245 } 246 } 247 248 public int write(ByteBuffer src) throws IOException { 249 250 ensureOpen(); 251 if (!writable) 252 throw new NonWritableChannelException(); 253 if (direct && ((src.remaining() % alignment != 0) || 254 (position() % alignment != 0))) 255 throw new IllegalArgumentException("IO size or position is not aligned"); 256 synchronized (positionLock) { 257 int n = 0; 258 int ti = -1; 259 try { 260 begin(); 261 ti = threads.add(); 262 if (!isOpen()) 263 return 0; 264 do { 265 n = IOUtil.write(fd, src, -1, direct, alignment, nd); 266 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 267 return IOStatus.normalize(n); 268 } finally { 269 threads.remove(ti); 270 end(n > 0); 271 assert IOStatus.check(n); 272 } 273 } 274 } 275 276 public long write(ByteBuffer[] srcs, int offset, int length) 277 throws IOException 278 { 279 if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) 280 throw new IndexOutOfBoundsException(); 281 ensureOpen(); 282 if (!writable) 283 throw new NonWritableChannelException(); 284 285 if (direct && (position() % alignment != 0)) 286 throw new IllegalArgumentException("Position is not aligned"); 287 288 synchronized (positionLock) { 289 long n = 0; 290 int ti = -1; 291 try { 292 begin(); 293 ti = threads.add(); 294 if (!isOpen()) 295 return 0; 296 do { 297 n = IOUtil.write(fd, srcs, offset, length, direct, alignment, nd); 298 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 299 return IOStatus.normalize(n); 300 } finally { 301 threads.remove(ti); 302 end(n > 0); 303 assert IOStatus.check(n); 304 } 305 } 306 } 307 308 // -- Other operations -- 309 310 public long position() throws IOException { 311 ensureOpen(); 312 synchronized (positionLock) { 313 long p = -1; 314 int ti = -1; 315 try { 316 begin(); 317 ti = threads.add(); 318 if (!isOpen()) 319 return 0; 320 boolean append = fdAccess.getAppend(fd); 321 do { 322 // in append-mode then position is advanced to end before writing 323 p = (append) ? nd.size(fd) : position0(fd, -1); 324 } while ((p == IOStatus.INTERRUPTED) && isOpen()); 325 return IOStatus.normalize(p); 326 } finally { 327 threads.remove(ti); 328 end(p > -1); 329 assert IOStatus.check(p); 330 } 331 } 332 } 333 334 public FileChannel position(long newPosition) throws IOException { 335 ensureOpen(); 336 if (newPosition < 0) 337 throw new IllegalArgumentException(); 338 synchronized (positionLock) { 339 long p = -1; 340 int ti = -1; 341 try { 342 begin(); 343 ti = threads.add(); 344 if (!isOpen()) 345 return null; 346 do { 347 p = position0(fd, newPosition); 348 } while ((p == IOStatus.INTERRUPTED) && isOpen()); 349 return this; 350 } finally { 351 threads.remove(ti); 352 end(p > -1); 353 assert IOStatus.check(p); 354 } 355 } 356 } 357 358 public long size() throws IOException { 359 ensureOpen(); 360 synchronized (positionLock) { 361 long s = -1; 362 int ti = -1; 363 try { 364 begin(); 365 ti = threads.add(); 366 if (!isOpen()) 367 return -1; 368 do { 369 s = nd.size(fd); 370 } while ((s == IOStatus.INTERRUPTED) && isOpen()); 371 return IOStatus.normalize(s); 372 } finally { 373 threads.remove(ti); 374 end(s > -1); 375 assert IOStatus.check(s); 376 } 377 } 378 } 379 380 public FileChannel truncate(long newSize) throws IOException { 381 ensureOpen(); 382 if (newSize < 0) 383 throw new IllegalArgumentException("Negative size"); 384 if (!writable) 385 throw new NonWritableChannelException(); 386 synchronized (positionLock) { 387 int rv = -1; 388 long p = -1; 389 int ti = -1; 390 long rp = -1; 391 try { 392 begin(); 393 ti = threads.add(); 394 if (!isOpen()) 395 return null; 396 397 // get current size 398 long size; 399 do { 400 size = nd.size(fd); 401 } while ((size == IOStatus.INTERRUPTED) && isOpen()); 402 if (!isOpen()) 403 return null; 404 405 // get current position 406 do { 407 p = position0(fd, -1); 408 } while ((p == IOStatus.INTERRUPTED) && isOpen()); 409 if (!isOpen()) 410 return null; 411 assert p >= 0; 412 413 // truncate file if given size is less than the current size 414 if (newSize < size) { 415 do { 416 rv = nd.truncate(fd, newSize); 417 } while ((rv == IOStatus.INTERRUPTED) && isOpen()); 418 if (!isOpen()) 419 return null; 420 } 421 422 // if position is beyond new size then adjust it 423 if (p > newSize) 424 p = newSize; 425 do { 426 rp = position0(fd, p); 427 } while ((rp == IOStatus.INTERRUPTED) && isOpen()); 428 return this; 429 } finally { 430 threads.remove(ti); 431 end(rv > -1); 432 assert IOStatus.check(rv); 433 } 434 } 435 } 436 437 public void force(boolean metaData) throws IOException { 438 ensureOpen(); 439 int rv = -1; 440 int ti = -1; 441 try { 442 begin(); 443 ti = threads.add(); 444 if (!isOpen()) 445 return; 446 do { 447 rv = nd.force(fd, metaData); 448 } while ((rv == IOStatus.INTERRUPTED) && isOpen()); 449 } finally { 450 threads.remove(ti); 451 end(rv > -1); 452 assert IOStatus.check(rv); 453 } 454 } 455 456 // Assume at first that the underlying kernel supports sendfile(); 457 // set this to false if we find out later that it doesn't 458 // 459 private static volatile boolean transferSupported = true; 460 461 // Assume that the underlying kernel sendfile() will work if the target 462 // fd is a pipe; set this to false if we find out later that it doesn't 463 // 464 private static volatile boolean pipeSupported = true; 465 466 // Assume that the underlying kernel sendfile() will work if the target 467 // fd is a file; set this to false if we find out later that it doesn't 468 // 469 private static volatile boolean fileSupported = true; 470 471 private long transferToDirectlyInternal(long position, int icount, 472 WritableByteChannel target, 473 FileDescriptor targetFD) 474 throws IOException 475 { 476 assert !nd.transferToDirectlyNeedsPositionLock() || 477 Thread.holdsLock(positionLock); 478 479 long n = -1; 480 int ti = -1; 481 try { 482 begin(); 483 ti = threads.add(); 484 if (!isOpen()) 485 return -1; 486 do { 487 n = transferTo0(fd, position, icount, targetFD); 488 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 489 if (n == IOStatus.UNSUPPORTED_CASE) { 490 if (target instanceof SinkChannelImpl) 491 pipeSupported = false; 492 if (target instanceof FileChannelImpl) 493 fileSupported = false; 494 return IOStatus.UNSUPPORTED_CASE; 495 } 496 if (n == IOStatus.UNSUPPORTED) { 497 // Don't bother trying again 498 transferSupported = false; 499 return IOStatus.UNSUPPORTED; 500 } 501 return IOStatus.normalize(n); 502 } finally { 503 threads.remove(ti); 504 end (n > -1); 505 } 506 } 507 508 private long transferToDirectly(long position, int icount, 509 WritableByteChannel target) 510 throws IOException 511 { 512 if (!transferSupported) 513 return IOStatus.UNSUPPORTED; 514 515 FileDescriptor targetFD = null; 516 if (target instanceof FileChannelImpl) { 517 if (!fileSupported) 518 return IOStatus.UNSUPPORTED_CASE; 519 targetFD = ((FileChannelImpl)target).fd; 520 } else if (target instanceof SelChImpl) { 521 // Direct transfer to pipe causes EINVAL on some configurations 522 if ((target instanceof SinkChannelImpl) && !pipeSupported) 523 return IOStatus.UNSUPPORTED_CASE; 524 525 // Platform-specific restrictions. Now there is only one: 526 // Direct transfer to non-blocking channel could be forbidden 527 SelectableChannel sc = (SelectableChannel)target; 528 if (!nd.canTransferToDirectly(sc)) 529 return IOStatus.UNSUPPORTED_CASE; 530 531 targetFD = ((SelChImpl)target).getFD(); 532 } 533 534 if (targetFD == null) 535 return IOStatus.UNSUPPORTED; 536 int thisFDVal = IOUtil.fdVal(fd); 537 int targetFDVal = IOUtil.fdVal(targetFD); 538 if (thisFDVal == targetFDVal) // Not supported on some configurations 539 return IOStatus.UNSUPPORTED; 540 541 if (nd.transferToDirectlyNeedsPositionLock()) { 542 synchronized (positionLock) { 543 long pos = position(); 544 try { 545 return transferToDirectlyInternal(position, icount, 546 target, targetFD); 547 } finally { 548 position(pos); 549 } 550 } 551 } else { 552 return transferToDirectlyInternal(position, icount, target, targetFD); 553 } 554 } 555 556 // Maximum size to map when using a mapped buffer 557 private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L; 558 559 private long transferToTrustedChannel(long position, long count, 560 WritableByteChannel target) 561 throws IOException 562 { 563 boolean isSelChImpl = (target instanceof SelChImpl); 564 if (!((target instanceof FileChannelImpl) || isSelChImpl)) 565 return IOStatus.UNSUPPORTED; 566 567 // Trusted target: Use a mapped buffer 568 long remaining = count; 569 while (remaining > 0L) { 570 long size = Math.min(remaining, MAPPED_TRANSFER_SIZE); 571 try { 572 MappedByteBuffer dbb = map(MapMode.READ_ONLY, position, size); 573 try { 574 // ## Bug: Closing this channel will not terminate the write 575 int n = target.write(dbb); 576 assert n >= 0; 577 remaining -= n; 578 if (isSelChImpl) { 579 // one attempt to write to selectable channel 580 break; 581 } 582 assert n > 0; 583 position += n; 584 } finally { 585 unmap(dbb); 586 } 587 } catch (ClosedByInterruptException e) { 588 // target closed by interrupt as ClosedByInterruptException needs 589 // to be thrown after closing this channel. 590 assert !target.isOpen(); 591 try { 592 close(); 593 } catch (Throwable suppressed) { 594 e.addSuppressed(suppressed); 595 } 596 throw e; 597 } catch (IOException ioe) { 598 // Only throw exception if no bytes have been written 599 if (remaining == count) 600 throw ioe; 601 break; 602 } 603 } 604 return count - remaining; 605 } 606 607 private long transferToArbitraryChannel(long position, int icount, 608 WritableByteChannel target) 609 throws IOException 610 { 611 // Untrusted target: Use a newly-erased buffer 612 int c = Math.min(icount, TRANSFER_SIZE); 613 ByteBuffer bb = Util.getTemporaryDirectBuffer(c); 614 long tw = 0; // Total bytes written 615 long pos = position; 616 try { 617 Util.erase(bb); 618 while (tw < icount) { 619 bb.limit(Math.min((int)(icount - tw), TRANSFER_SIZE)); 620 int nr = read(bb, pos); 621 if (nr <= 0) 622 break; 623 bb.flip(); 624 // ## Bug: Will block writing target if this channel 625 // ## is asynchronously closed 626 int nw = target.write(bb); 627 tw += nw; 628 if (nw != nr) 629 break; 630 pos += nw; 631 bb.clear(); 632 } 633 return tw; 634 } catch (IOException x) { 635 if (tw > 0) 636 return tw; 637 throw x; 638 } finally { 639 Util.releaseTemporaryDirectBuffer(bb); 640 } 641 } 642 643 public long transferTo(long position, long count, 644 WritableByteChannel target) 645 throws IOException 646 { 647 ensureOpen(); 648 if (!target.isOpen()) 649 throw new ClosedChannelException(); 650 if (!readable) 651 throw new NonReadableChannelException(); 652 if (target instanceof FileChannelImpl && 653 !((FileChannelImpl)target).writable) 654 throw new NonWritableChannelException(); 655 if ((position < 0) || (count < 0)) 656 throw new IllegalArgumentException(); 657 long sz = size(); 658 if (position > sz) 659 return 0; 660 int icount = (int)Math.min(count, Integer.MAX_VALUE); 661 if ((sz - position) < icount) 662 icount = (int)(sz - position); 663 664 long n; 665 666 // Attempt a direct transfer, if the kernel supports it 667 if ((n = transferToDirectly(position, icount, target)) >= 0) 668 return n; 669 670 // Attempt a mapped transfer, but only to trusted channel types 671 if ((n = transferToTrustedChannel(position, icount, target)) >= 0) 672 return n; 673 674 // Slow path for untrusted targets 675 return transferToArbitraryChannel(position, icount, target); 676 } 677 678 private long transferFromFileChannel(FileChannelImpl src, 679 long position, long count) 680 throws IOException 681 { 682 if (!src.readable) 683 throw new NonReadableChannelException(); 684 synchronized (src.positionLock) { 685 long pos = src.position(); 686 long max = Math.min(count, src.size() - pos); 687 688 long remaining = max; 689 long p = pos; 690 while (remaining > 0L) { 691 long size = Math.min(remaining, MAPPED_TRANSFER_SIZE); 692 // ## Bug: Closing this channel will not terminate the write 693 MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, size); 694 try { 695 long n = write(bb, position); 696 assert n > 0; 697 p += n; 698 position += n; 699 remaining -= n; 700 } catch (IOException ioe) { 701 // Only throw exception if no bytes have been written 702 if (remaining == max) 703 throw ioe; 704 break; 705 } finally { 706 unmap(bb); 707 } 708 } 709 long nwritten = max - remaining; 710 src.position(pos + nwritten); 711 return nwritten; 712 } 713 } 714 715 private static final int TRANSFER_SIZE = 8192; 716 717 private long transferFromArbitraryChannel(ReadableByteChannel src, 718 long position, long count) 719 throws IOException 720 { 721 // Untrusted target: Use a newly-erased buffer 722 int c = (int)Math.min(count, TRANSFER_SIZE); 723 ByteBuffer bb = Util.getTemporaryDirectBuffer(c); 724 long tw = 0; // Total bytes written 725 long pos = position; 726 try { 727 Util.erase(bb); 728 while (tw < count) { 729 bb.limit((int)Math.min((count - tw), (long)TRANSFER_SIZE)); 730 // ## Bug: Will block reading src if this channel 731 // ## is asynchronously closed 732 int nr = src.read(bb); 733 if (nr <= 0) 734 break; 735 bb.flip(); 736 int nw = write(bb, pos); 737 tw += nw; 738 if (nw != nr) 739 break; 740 pos += nw; 741 bb.clear(); 742 } 743 return tw; 744 } catch (IOException x) { 745 if (tw > 0) 746 return tw; 747 throw x; 748 } finally { 749 Util.releaseTemporaryDirectBuffer(bb); 750 } 751 } 752 753 public long transferFrom(ReadableByteChannel src, 754 long position, long count) 755 throws IOException 756 { 757 ensureOpen(); 758 if (!src.isOpen()) 759 throw new ClosedChannelException(); 760 if (!writable) 761 throw new NonWritableChannelException(); 762 if ((position < 0) || (count < 0)) 763 throw new IllegalArgumentException(); 764 if (position > size()) 765 return 0; 766 if (src instanceof FileChannelImpl) 767 return transferFromFileChannel((FileChannelImpl)src, 768 position, count); 769 770 return transferFromArbitraryChannel(src, position, count); 771 } 772 773 public int read(ByteBuffer dst, long position) throws IOException { 774 if (dst == null) 775 throw new NullPointerException(); 776 if (position < 0) 777 throw new IllegalArgumentException("Negative position"); 778 if (!readable) 779 throw new NonReadableChannelException(); 780 if (direct && ((dst.remaining() % alignment != 0) || 781 (position % alignment != 0))) 782 throw new IllegalArgumentException("IO size or position is not aligned"); 783 784 ensureOpen(); 785 if (nd.needsPositionLock()) { 786 synchronized (positionLock) { 787 return readInternal(dst, position); 788 } 789 } else { 790 return readInternal(dst, position); 791 } 792 } 793 794 private int readInternal(ByteBuffer dst, long position) throws IOException { 795 assert !nd.needsPositionLock() || Thread.holdsLock(positionLock); 796 int n = 0; 797 int ti = -1; 798 799 boolean interruptible = !uninterruptible; 800 try { 801 if (interruptible) begin(); 802 ti = threads.add(); 803 if (!isOpen()) 804 return -1; 805 do { 806 n = IOUtil.read(fd, dst, position, direct, alignment, nd); 807 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 808 return IOStatus.normalize(n); 809 } finally { 810 threads.remove(ti); 811 if (interruptible) end(n > 0); 812 assert IOStatus.check(n); 813 } 814 } 815 816 public int write(ByteBuffer src, long position) throws IOException { 817 if (src == null) 818 throw new NullPointerException(); 819 if (position < 0) 820 throw new IllegalArgumentException("Negative position"); 821 if (!writable) 822 throw new NonWritableChannelException(); 823 if (direct && ((src.remaining() % alignment != 0) || 824 (position % alignment != 0))) 825 throw new IllegalArgumentException("IO size or position is not aligned"); 826 827 ensureOpen(); 828 if (nd.needsPositionLock()) { 829 synchronized (positionLock) { 830 return writeInternal(src, position); 831 } 832 } else { 833 return writeInternal(src, position); 834 } 835 } 836 837 private int writeInternal(ByteBuffer src, long position) throws IOException { 838 assert !nd.needsPositionLock() || Thread.holdsLock(positionLock); 839 int n = 0; 840 int ti = -1; 841 try { 842 begin(); 843 ti = threads.add(); 844 if (!isOpen()) 845 return -1; 846 do { 847 n = IOUtil.write(fd, src, position, direct, alignment, nd); 848 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 849 return IOStatus.normalize(n); 850 } finally { 851 threads.remove(ti); 852 end(n > 0); 853 assert IOStatus.check(n); 854 } 855 } 856 857 858 // -- Memory-mapped buffers -- 859 860 private static class Unmapper 861 implements Runnable 862 { 863 // may be required to close file 864 private static final NativeDispatcher nd = new FileDispatcherImpl(); 865 866 // keep track of mapped buffer usage 867 static volatile int count; 868 static volatile long totalSize; 869 static volatile long totalCapacity; 870 871 private volatile long address; 872 private final long size; 873 private final int cap; 874 private final FileDescriptor fd; 875 876 private Unmapper(long address, long size, int cap, 877 FileDescriptor fd) 878 { 879 assert (address != 0); 880 this.address = address; 881 this.size = size; 882 this.cap = cap; 883 this.fd = fd; 884 885 synchronized (Unmapper.class) { 886 count++; 887 totalSize += size; 888 totalCapacity += cap; 889 } 890 } 891 892 public void run() { 893 if (address == 0) 894 return; 895 unmap0(address, size); 896 address = 0; 897 898 // if this mapping has a valid file descriptor then we close it 899 if (fd.valid()) { 900 try { 901 nd.close(fd); 902 } catch (IOException ignore) { 903 // nothing we can do 904 } 905 } 906 907 synchronized (Unmapper.class) { 908 count--; 909 totalSize -= size; 910 totalCapacity -= cap; 911 } 912 } 913 } 914 915 private static void unmap(MappedByteBuffer bb) { 916 Cleaner cl = ((DirectBuffer)bb).cleaner(); 917 if (cl != null) 918 cl.clean(); 919 } 920 921 private static final int MAP_RO = 0; 922 private static final int MAP_RW = 1; 923 private static final int MAP_PV = 2; 924 925 public MappedByteBuffer map(MapMode mode, long position, long size) 926 throws IOException 927 { 928 ensureOpen(); 929 if (mode == null) 930 throw new NullPointerException("Mode is null"); 931 if (position < 0L) 932 throw new IllegalArgumentException("Negative position"); 933 if (size < 0L) 934 throw new IllegalArgumentException("Negative size"); 935 if (position + size < 0) 936 throw new IllegalArgumentException("Position + size overflow"); 937 if (size > Integer.MAX_VALUE) 938 throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE"); 939 940 int imode = -1; 941 if (mode == MapMode.READ_ONLY) 942 imode = MAP_RO; 943 else if (mode == MapMode.READ_WRITE) 944 imode = MAP_RW; 945 else if (mode == MapMode.PRIVATE) 946 imode = MAP_PV; 947 assert (imode >= 0); 948 if ((mode != MapMode.READ_ONLY) && !writable) 949 throw new NonWritableChannelException(); 950 if (!readable) 951 throw new NonReadableChannelException(); 952 953 long addr = -1; 954 int ti = -1; 955 try { 956 begin(); 957 ti = threads.add(); 958 if (!isOpen()) 959 return null; 960 961 long filesize; 962 do { 963 filesize = nd.size(fd); 964 } while ((filesize == IOStatus.INTERRUPTED) && isOpen()); 965 if (!isOpen()) 966 return null; 967 968 if (filesize < position + size) { // Extend file size 969 if (!writable) { 970 throw new IOException("Channel not open for writing " + 971 "- cannot extend file to required size"); 972 } 973 int rv; 974 do { 975 rv = nd.truncate(fd, position + size); 976 } while ((rv == IOStatus.INTERRUPTED) && isOpen()); 977 if (!isOpen()) 978 return null; 979 } 980 if (size == 0) { 981 addr = 0; 982 // a valid file descriptor is not required 983 FileDescriptor dummy = new FileDescriptor(); 984 if ((!writable) || (imode == MAP_RO)) 985 return Util.newMappedByteBufferR(0, 0, dummy, null); 986 else 987 return Util.newMappedByteBuffer(0, 0, dummy, null); 988 } 989 990 int pagePosition = (int)(position % allocationGranularity); 991 long mapPosition = position - pagePosition; 992 long mapSize = size + pagePosition; 993 try { 994 // If no exception was thrown from map0, the address is valid 995 addr = map0(imode, mapPosition, mapSize); 996 } catch (OutOfMemoryError x) { 997 // An OutOfMemoryError may indicate that we've exhausted memory 998 // so force gc and re-attempt map 999 System.gc(); 1000 try { 1001 Thread.sleep(100); 1002 } catch (InterruptedException y) { 1003 Thread.currentThread().interrupt(); 1004 } 1005 try { 1006 addr = map0(imode, mapPosition, mapSize); 1007 } catch (OutOfMemoryError y) { 1008 // After a second OOME, fail 1009 throw new IOException("Map failed", y); 1010 } 1011 } 1012 1013 // On Windows, and potentially other platforms, we need an open 1014 // file descriptor for some mapping operations. 1015 FileDescriptor mfd; 1016 try { 1017 mfd = nd.duplicateForMapping(fd); 1018 } catch (IOException ioe) { 1019 unmap0(addr, mapSize); 1020 throw ioe; 1021 } 1022 1023 assert (IOStatus.checkAll(addr)); 1024 assert (addr % allocationGranularity == 0); 1025 int isize = (int)size; 1026 Unmapper um = new Unmapper(addr, mapSize, isize, mfd); 1027 if ((!writable) || (imode == MAP_RO)) { 1028 return Util.newMappedByteBufferR(isize, 1029 addr + pagePosition, 1030 mfd, 1031 um); 1032 } else { 1033 return Util.newMappedByteBuffer(isize, 1034 addr + pagePosition, 1035 mfd, 1036 um); 1037 } 1038 } finally { 1039 threads.remove(ti); 1040 end(IOStatus.checkAll(addr)); 1041 } 1042 } 1043 1044 /** 1045 * Invoked by sun.management.ManagementFactoryHelper to create the management 1046 * interface for mapped buffers. 1047 */ 1048 public static JavaNioAccess.BufferPool getMappedBufferPool() { 1049 return new JavaNioAccess.BufferPool() { 1050 @Override 1051 public String getName() { 1052 return "mapped"; 1053 } 1054 @Override 1055 public long getCount() { 1056 return Unmapper.count; 1057 } 1058 @Override 1059 public long getTotalCapacity() { 1060 return Unmapper.totalCapacity; 1061 } 1062 @Override 1063 public long getMemoryUsed() { 1064 return Unmapper.totalSize; 1065 } 1066 }; 1067 } 1068 1069 // -- Locks -- 1070 1071 1072 1073 // keeps track of locks on this file 1074 private volatile FileLockTable fileLockTable; 1075 1076 // indicates if file locks are maintained system-wide (as per spec) 1077 private static boolean isSharedFileLockTable; 1078 1079 // indicates if the disableSystemWideOverlappingFileLockCheck property 1080 // has been checked 1081 private static volatile boolean propertyChecked; 1082 1083 // The lock list in J2SE 1.4/5.0 was local to each FileChannel instance so 1084 // the overlap check wasn't system wide when there were multiple channels to 1085 // the same file. This property is used to get 1.4/5.0 behavior if desired. 1086 private static boolean isSharedFileLockTable() { 1087 if (!propertyChecked) { 1088 synchronized (FileChannelImpl.class) { 1089 if (!propertyChecked) { 1090 String value = GetPropertyAction.privilegedGetProperty( 1091 "sun.nio.ch.disableSystemWideOverlappingFileLockCheck"); 1092 isSharedFileLockTable = ((value == null) || value.equals("false")); 1093 propertyChecked = true; 1094 } 1095 } 1096 } 1097 return isSharedFileLockTable; 1098 } 1099 1100 private FileLockTable fileLockTable() throws IOException { 1101 if (fileLockTable == null) { 1102 synchronized (this) { 1103 if (fileLockTable == null) { 1104 if (isSharedFileLockTable()) { 1105 int ti = threads.add(); 1106 try { 1107 ensureOpen(); 1108 fileLockTable = FileLockTable.newSharedFileLockTable(this, fd); 1109 } finally { 1110 threads.remove(ti); 1111 } 1112 } else { 1113 fileLockTable = new SimpleFileLockTable(); 1114 } 1115 } 1116 } 1117 } 1118 return fileLockTable; 1119 } 1120 1121 public FileLock lock(long position, long size, boolean shared) 1122 throws IOException 1123 { 1124 ensureOpen(); 1125 if (shared && !readable) 1126 throw new NonReadableChannelException(); 1127 if (!shared && !writable) 1128 throw new NonWritableChannelException(); 1129 FileLockImpl fli = new FileLockImpl(this, position, size, shared); 1130 FileLockTable flt = fileLockTable(); 1131 flt.add(fli); 1132 boolean completed = false; 1133 int ti = -1; 1134 try { 1135 begin(); 1136 ti = threads.add(); 1137 if (!isOpen()) 1138 return null; 1139 int n; 1140 do { 1141 n = nd.lock(fd, true, position, size, shared); 1142 } while ((n == FileDispatcher.INTERRUPTED) && isOpen()); 1143 if (isOpen()) { 1144 if (n == FileDispatcher.RET_EX_LOCK) { 1145 assert shared; 1146 FileLockImpl fli2 = new FileLockImpl(this, position, size, 1147 false); 1148 flt.replace(fli, fli2); 1149 fli = fli2; 1150 } 1151 completed = true; 1152 } 1153 } finally { 1154 if (!completed) 1155 flt.remove(fli); 1156 threads.remove(ti); 1157 try { 1158 end(completed); 1159 } catch (ClosedByInterruptException e) { 1160 throw new FileLockInterruptionException(); 1161 } 1162 } 1163 return fli; 1164 } 1165 1166 public FileLock tryLock(long position, long size, boolean shared) 1167 throws IOException 1168 { 1169 ensureOpen(); 1170 if (shared && !readable) 1171 throw new NonReadableChannelException(); 1172 if (!shared && !writable) 1173 throw new NonWritableChannelException(); 1174 FileLockImpl fli = new FileLockImpl(this, position, size, shared); 1175 FileLockTable flt = fileLockTable(); 1176 flt.add(fli); 1177 int result; 1178 1179 int ti = threads.add(); 1180 try { 1181 try { 1182 ensureOpen(); 1183 result = nd.lock(fd, false, position, size, shared); 1184 } catch (IOException e) { 1185 flt.remove(fli); 1186 throw e; 1187 } 1188 if (result == FileDispatcher.NO_LOCK) { 1189 flt.remove(fli); 1190 return null; 1191 } 1192 if (result == FileDispatcher.RET_EX_LOCK) { 1193 assert shared; 1194 FileLockImpl fli2 = new FileLockImpl(this, position, size, 1195 false); 1196 flt.replace(fli, fli2); 1197 return fli2; 1198 } 1199 return fli; 1200 } finally { 1201 threads.remove(ti); 1202 } 1203 } 1204 1205 void release(FileLockImpl fli) throws IOException { 1206 int ti = threads.add(); 1207 try { 1208 ensureOpen(); 1209 nd.release(fd, fli.position(), fli.size()); 1210 } finally { 1211 threads.remove(ti); 1212 } 1213 assert fileLockTable != null; 1214 fileLockTable.remove(fli); 1215 } 1216 1217 // -- File lock support -- 1218 1219 /** 1220 * A simple file lock table that maintains a list of FileLocks obtained by a 1221 * FileChannel. Use to get 1.4/5.0 behaviour. 1222 */ 1223 private static class SimpleFileLockTable extends FileLockTable { 1224 // synchronize on list for access 1225 private final List<FileLock> lockList = new ArrayList<FileLock>(2); 1226 1227 public SimpleFileLockTable() { 1228 } 1229 1230 private void checkList(long position, long size) 1231 throws OverlappingFileLockException 1232 { 1233 assert Thread.holdsLock(lockList); 1234 for (FileLock fl: lockList) { 1235 if (fl.overlaps(position, size)) { 1236 throw new OverlappingFileLockException(); 1237 } 1238 } 1239 } 1240 1241 public void add(FileLock fl) throws OverlappingFileLockException { 1242 synchronized (lockList) { 1243 checkList(fl.position(), fl.size()); 1244 lockList.add(fl); 1245 } 1246 } 1247 1248 public void remove(FileLock fl) { 1249 synchronized (lockList) { 1250 lockList.remove(fl); 1251 } 1252 } 1253 1254 public List<FileLock> removeAll() { 1255 synchronized(lockList) { 1256 List<FileLock> result = new ArrayList<FileLock>(lockList); 1257 lockList.clear(); 1258 return result; 1259 } 1260 } 1261 1262 public void replace(FileLock fl1, FileLock fl2) { 1263 synchronized (lockList) { 1264 lockList.remove(fl1); 1265 lockList.add(fl2); 1266 } 1267 } 1268 } 1269 1270 // -- Native methods -- 1271 1272 // Creates a new mapping 1273 private native long map0(int prot, long position, long length) 1274 throws IOException; 1275 1276 // Removes an existing mapping 1277 private static native int unmap0(long address, long length); 1278 1279 // Transfers from src to dst, or returns -2 if kernel can't do that 1280 private native long transferTo0(FileDescriptor src, long position, 1281 long count, FileDescriptor dst); 1282 1283 // Sets or reports this file's position 1284 // If offset is -1, the current position is returned 1285 // otherwise the position is set to offset 1286 private native long position0(FileDescriptor fd, long offset); 1287 1288 // Caches fieldIDs 1289 private static native long initIDs(); 1290 1291 private native int setDirect0(FileDescriptor fd) throws IOException; 1292 1293 static { 1294 IOUtil.load(); 1295 allocationGranularity = initIDs(); 1296 } 1297 1298 }