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