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