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