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