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