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