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