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