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