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 FileChannelImpl(FileDescriptor fd, String path, boolean readable, boolean writable, boolean append, Object parent)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() open(FileDescriptor fd, String path, boolean readable, boolean writable, Object parent)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 open(FileDescriptor fd, String path, boolean readable, boolean writable, boolean append, Object parent)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 ensureOpen()123 private void ensureOpen() throws IOException { 124 if (!isOpen()) 125 throw new ClosedChannelException(); 126 } 127 128 129 // -- Standard channel operations -- 130 implCloseChannel()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 finalize()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 read(ByteBuffer dst)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 read(ByteBuffer[] dsts, int offset, int length)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 write(ByteBuffer src)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 write(ByteBuffer[] srcs, int offset, int length)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 position()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 position(long newPosition)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 size()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 truncate(long newSize)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 force(boolean metaData)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 transferToDirectlyInternal(long position, int icount, WritableByteChannel target, FileDescriptor targetFD)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 transferToDirectly(long position, int icount, WritableByteChannel target)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 transferToTrustedChannel(long position, long count, WritableByteChannel target)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 transferToArbitraryChannel(long position, int icount, WritableByteChannel target)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 transferTo(long position, long count, WritableByteChannel target)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 transferFromFileChannel(FileChannelImpl src, long position, long count)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 transferFromArbitraryChannel(ReadableByteChannel src, long position, long count)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 transferFrom(ReadableByteChannel src, long position, long count)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 read(ByteBuffer dst, long position)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 readInternal(ByteBuffer dst, long position)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 write(ByteBuffer src, long position)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 writeInternal(ByteBuffer src, long position)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 Unmapper(long address, long size, int cap, FileDescriptor fd)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 run()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 unmap(MappedByteBuffer bb)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 map(MapMode mode, long position, long size)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. isSharedFileLockTable()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 fileLockTable()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 lock(long position, long size, boolean shared)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 tryLock(long position, long size, boolean shared)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 release(FileLockImpl fli)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 SimpleFileLockTable()1176 public SimpleFileLockTable() { 1177 } 1178 checkList(long position, long size)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 add(FileLock fl)1190 public void add(FileLock fl) throws OverlappingFileLockException { 1191 synchronized (lockList) { 1192 checkList(fl.position(), fl.size()); 1193 lockList.add(fl); 1194 } 1195 } 1196 remove(FileLock fl)1197 public void remove(FileLock fl) { 1198 synchronized (lockList) { 1199 lockList.remove(fl); 1200 } 1201 } 1202 removeAll()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 replace(FileLock fl1, FileLock fl2)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 map0(int prot, long position, long length)1222 private native long map0(int prot, long position, long length) 1223 throws IOException; 1224 1225 // Removes an existing mapping unmap0(long address, long length)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 transferTo0(FileDescriptor src, long position, long count, FileDescriptor dst)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 position0(FileDescriptor fd, long offset)1235 private native long position0(FileDescriptor fd, long offset); 1236 1237 // Caches fieldIDs initIDs()1238 private static native long initIDs(); 1239 1240 static { 1241 allocationGranularity = initIDs(); 1242 } 1243 1244 } 1245