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