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