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