Home | History | Annotate | Download | only in nio
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.nio;
     19 
     20 import android.system.ErrnoException;
     21 import android.system.StructFlock;
     22 import android.util.MutableLong;
     23 import java.io.Closeable;
     24 import java.io.FileDescriptor;
     25 import java.io.IOException;
     26 import java.nio.channels.ClosedByInterruptException;
     27 import java.nio.channels.ClosedChannelException;
     28 import java.nio.channels.FileChannel;
     29 import java.nio.channels.FileLock;
     30 import java.nio.channels.FileLockInterruptionException;
     31 import java.nio.channels.NonReadableChannelException;
     32 import java.nio.channels.NonWritableChannelException;
     33 import java.nio.channels.OverlappingFileLockException;
     34 import java.nio.channels.ReadableByteChannel;
     35 import java.nio.channels.WritableByteChannel;
     36 import java.util.Arrays;
     37 import java.util.Comparator;
     38 import java.util.SortedSet;
     39 import java.util.TreeSet;
     40 import libcore.io.Libcore;
     41 import static android.system.OsConstants.*;
     42 
     43 /**
     44  * Our concrete implementation of the abstract FileChannel class.
     45  */
     46 final class FileChannelImpl extends FileChannel {
     47     private static final Comparator<FileLock> LOCK_COMPARATOR = new Comparator<FileLock>() {
     48         public int compare(FileLock lock1, FileLock lock2) {
     49             long position1 = lock1.position();
     50             long position2 = lock2.position();
     51             return position1 > position2 ? 1 : (position1 < position2 ? -1 : 0);
     52         }
     53     };
     54 
     55     private final Closeable ioObject;
     56     private final FileDescriptor fd;
     57     private final int mode;
     58 
     59     // The set of acquired and pending locks.
     60     private final SortedSet<FileLock> locks = new TreeSet<FileLock>(LOCK_COMPARATOR);
     61 
     62     /**
     63      * Create a new file channel implementation class that wraps the given
     64      * fd and operates in the specified mode.
     65      */
     66     public FileChannelImpl(Closeable ioObject, FileDescriptor fd, int mode) {
     67         this.fd = fd;
     68         this.ioObject = ioObject;
     69         this.mode = mode;
     70     }
     71 
     72     private void checkOpen() throws ClosedChannelException {
     73         if (!isOpen()) {
     74             throw new ClosedChannelException();
     75         }
     76     }
     77 
     78     private void checkReadable() {
     79         if ((mode & O_ACCMODE) == O_WRONLY) {
     80             throw new NonReadableChannelException();
     81         }
     82     }
     83 
     84     private void checkWritable() {
     85         if ((mode & O_ACCMODE) == O_RDONLY) {
     86             throw new NonWritableChannelException();
     87         }
     88     }
     89 
     90     protected void implCloseChannel() throws IOException {
     91         ioObject.close();
     92     }
     93 
     94     private FileLock basicLock(long position, long size, boolean shared, boolean wait) throws IOException {
     95         int accessMode = (mode & O_ACCMODE);
     96         if (accessMode == O_RDONLY) {
     97             if (!shared) {
     98                 throw new NonWritableChannelException();
     99             }
    100         } else if (accessMode == O_WRONLY) {
    101             if (shared) {
    102                 throw new NonReadableChannelException();
    103             }
    104         }
    105 
    106         if (position < 0 || size < 0) {
    107             throw new IllegalArgumentException("position=" + position + " size=" + size);
    108         }
    109 
    110         FileLock pendingLock = new FileLockImpl(this, position, size, shared);
    111         addLock(pendingLock);
    112 
    113         StructFlock flock = new StructFlock();
    114         flock.l_type = (short) (shared ? F_RDLCK : F_WRLCK);
    115         flock.l_whence = (short) SEEK_SET;
    116         flock.l_start = position;
    117         flock.l_len = translateLockLength(size);
    118 
    119         boolean success = false;
    120         try {
    121             success = (Libcore.os.fcntlFlock(fd, wait ? F_SETLKW64 : F_SETLK64, flock) != -1);
    122         } catch (ErrnoException errnoException) {
    123             throw errnoException.rethrowAsIOException();
    124         } finally {
    125             if (!success) {
    126                 removeLock(pendingLock);
    127             }
    128         }
    129         return success ? pendingLock : null;
    130     }
    131 
    132     private static long translateLockLength(long byteCount) {
    133         // FileChannel uses Long.MAX_VALUE to mean "lock the whole file" where POSIX uses 0.
    134         return (byteCount == Long.MAX_VALUE) ? 0 : byteCount;
    135     }
    136 
    137     private static final class FileLockImpl extends FileLock {
    138         private boolean isReleased = false;
    139 
    140         public FileLockImpl(FileChannel channel, long position, long size, boolean shared) {
    141             super(channel, position, size, shared);
    142         }
    143 
    144         public boolean isValid() {
    145             return !isReleased && channel().isOpen();
    146         }
    147 
    148         public void release() throws IOException {
    149             if (!channel().isOpen()) {
    150                 throw new ClosedChannelException();
    151             }
    152             if (!isReleased) {
    153                 ((FileChannelImpl) channel()).release(this);
    154                 isReleased = true;
    155             }
    156         }
    157     }
    158 
    159     public final FileLock lock(long position, long size, boolean shared) throws IOException {
    160         checkOpen();
    161         FileLock resultLock = null;
    162         {
    163             boolean completed = false;
    164             try {
    165                 begin();
    166                 resultLock = basicLock(position, size, shared, true);
    167                 completed = true;
    168             } finally {
    169                 try {
    170                     end(completed);
    171                 } catch (ClosedByInterruptException e) {
    172                     throw new FileLockInterruptionException();
    173                 }
    174             }
    175         }
    176         return resultLock;
    177     }
    178 
    179     public final FileLock tryLock(long position, long size, boolean shared) throws IOException {
    180         checkOpen();
    181         return basicLock(position, size, shared, false);
    182     }
    183 
    184     /**
    185      * Non-API method to release a given lock on a file channel. Assumes that
    186      * the lock will mark itself invalid after successful unlocking.
    187      */
    188     public void release(FileLock lock) throws IOException {
    189         checkOpen();
    190 
    191         StructFlock flock = new StructFlock();
    192         flock.l_type = (short) F_UNLCK;
    193         flock.l_whence = (short) SEEK_SET;
    194         flock.l_start = lock.position();
    195         flock.l_len = translateLockLength(lock.size());
    196         try {
    197             Libcore.os.fcntlFlock(fd, F_SETLKW64, flock);
    198         } catch (ErrnoException errnoException) {
    199             throw errnoException.rethrowAsIOException();
    200         }
    201 
    202         removeLock(lock);
    203     }
    204 
    205     public void force(boolean metadata) throws IOException {
    206         checkOpen();
    207         if ((mode & O_ACCMODE) != O_RDONLY) {
    208             try {
    209                 if (metadata) {
    210                     Libcore.os.fsync(fd);
    211                 } else {
    212                     Libcore.os.fdatasync(fd);
    213                 }
    214             } catch (ErrnoException errnoException) {
    215                 throw errnoException.rethrowAsIOException();
    216             }
    217         }
    218     }
    219 
    220     public final MappedByteBuffer map(MapMode mapMode, long position, long size) throws IOException {
    221         checkOpen();
    222         if (mapMode == null) {
    223             throw new NullPointerException("mapMode == null");
    224         }
    225         if (position < 0 || size < 0 || size > Integer.MAX_VALUE) {
    226             throw new IllegalArgumentException("position=" + position + " size=" + size);
    227         }
    228         int accessMode = (mode & O_ACCMODE);
    229         if (accessMode == O_RDONLY) {
    230             if (mapMode != MapMode.READ_ONLY) {
    231                 throw new NonWritableChannelException();
    232             }
    233         } else if (accessMode == O_WRONLY) {
    234             throw new NonReadableChannelException();
    235         }
    236         if (position + size > size()) {
    237             // We can't defer to FileChannel.truncate because that will only make a file shorter,
    238             // and we only care about making our backing file longer here.
    239             try {
    240                 Libcore.os.ftruncate(fd, position + size);
    241             } catch (ErrnoException ftruncateException) {
    242                 // EINVAL can be thrown if we're dealing with non-regular
    243                 // files, for example, character devices such as /dev/zero.
    244                 // In those cases, we ignore the failed truncation and
    245                 // continue on.
    246                 try {
    247                     if (S_ISREG(Libcore.os.fstat(fd).st_mode) || ftruncateException.errno != EINVAL) {
    248                         throw ftruncateException.rethrowAsIOException();
    249                     }
    250                 } catch (ErrnoException fstatException) {
    251                     throw fstatException.rethrowAsIOException();
    252                 }
    253             }
    254         }
    255         long alignment = position - position % Libcore.os.sysconf(_SC_PAGE_SIZE);
    256         int offset = (int) (position - alignment);
    257         MemoryBlock block = MemoryBlock.mmap(fd, alignment, size + offset, mapMode);
    258         return new DirectByteBuffer(block, (int) size, offset, (mapMode == MapMode.READ_ONLY), mapMode);
    259     }
    260 
    261     public long position() throws IOException {
    262         checkOpen();
    263         try {
    264             return Libcore.os.lseek(fd, 0L, SEEK_CUR);
    265         } catch (ErrnoException errnoException) {
    266             throw errnoException.rethrowAsIOException();
    267         }
    268     }
    269 
    270     public FileChannel position(long newPosition) throws IOException {
    271         checkOpen();
    272         if (newPosition < 0) {
    273             throw new IllegalArgumentException("position: " + newPosition);
    274         }
    275         try {
    276             Libcore.os.lseek(fd, newPosition, SEEK_SET);
    277         } catch (ErrnoException errnoException) {
    278             throw errnoException.rethrowAsIOException();
    279         }
    280         return this;
    281     }
    282 
    283     public int read(ByteBuffer buffer, long position) throws IOException {
    284         if (position < 0) {
    285             throw new IllegalArgumentException("position: " + position);
    286         }
    287         return readImpl(buffer, position);
    288     }
    289 
    290     public int read(ByteBuffer buffer) throws IOException {
    291         return readImpl(buffer, -1);
    292     }
    293 
    294     private int readImpl(ByteBuffer buffer, long position) throws IOException {
    295         buffer.checkWritable();
    296         checkOpen();
    297         checkReadable();
    298         if (!buffer.hasRemaining()) {
    299             return 0;
    300         }
    301         int bytesRead = 0;
    302         boolean completed = false;
    303         try {
    304             begin();
    305             try {
    306                 if (position == -1) {
    307                     bytesRead = Libcore.os.read(fd, buffer);
    308                 } else {
    309                     bytesRead = Libcore.os.pread(fd, buffer, position);
    310                 }
    311                 if (bytesRead == 0) {
    312                     bytesRead = -1;
    313                 }
    314             } catch (ErrnoException errnoException) {
    315                 if (errnoException.errno == EAGAIN) {
    316                     // We don't throw if we try to read from an empty non-blocking pipe.
    317                     bytesRead = 0;
    318                 } else {
    319                     throw errnoException.rethrowAsIOException();
    320                 }
    321             }
    322             completed = true;
    323         } finally {
    324             end(completed && bytesRead >= 0);
    325         }
    326 
    327         return bytesRead;
    328     }
    329 
    330     private int transferIoVec(IoVec ioVec) throws IOException {
    331         if (ioVec.init() == 0) {
    332             return 0;
    333         }
    334         int bytesTransferred = 0;
    335         boolean completed = false;
    336         try {
    337             begin();
    338             bytesTransferred = ioVec.doTransfer(fd);
    339             completed = true;
    340         } finally {
    341             end(completed);
    342         }
    343         ioVec.didTransfer(bytesTransferred);
    344         return bytesTransferred;
    345     }
    346 
    347     public long read(ByteBuffer[] buffers, int offset, int length) throws IOException {
    348         Arrays.checkOffsetAndCount(buffers.length, offset, length);
    349         checkOpen();
    350         checkReadable();
    351         return transferIoVec(new IoVec(buffers, offset, length, IoVec.Direction.READV));
    352     }
    353 
    354     public long size() throws IOException {
    355         checkOpen();
    356         try {
    357             return Libcore.os.fstat(fd).st_size;
    358         } catch (ErrnoException errnoException) {
    359             throw errnoException.rethrowAsIOException();
    360         }
    361     }
    362 
    363     public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
    364         checkOpen();
    365         if (!src.isOpen()) {
    366             throw new ClosedChannelException();
    367         }
    368         checkWritable();
    369         if (position < 0 || count < 0 || count > Integer.MAX_VALUE) {
    370             throw new IllegalArgumentException("position=" + position + " count=" + count);
    371         }
    372         if (position > size()) {
    373             return 0;
    374         }
    375 
    376         // Although sendfile(2) originally supported writing to a regular file.
    377         // In Linux 2.6 and later, it only supports writing to sockets.
    378 
    379         // If our source is a regular file, mmap(2) rather than reading.
    380         // Callers should only be using transferFrom for large transfers,
    381         // so the mmap(2) overhead isn't a concern.
    382         if (src instanceof FileChannel) {
    383             FileChannel fileSrc = (FileChannel) src;
    384             long size = fileSrc.size();
    385             long filePosition = fileSrc.position();
    386             count = Math.min(count, size - filePosition);
    387             ByteBuffer buffer = fileSrc.map(MapMode.READ_ONLY, filePosition, count);
    388             try {
    389                 fileSrc.position(filePosition + count);
    390                 return write(buffer, position);
    391             } finally {
    392                 NioUtils.freeDirectBuffer(buffer);
    393             }
    394         }
    395 
    396         // For non-file channels, all we can do is read and write via userspace.
    397         ByteBuffer buffer = ByteBuffer.allocate((int) count);
    398         src.read(buffer);
    399         buffer.flip();
    400         return write(buffer, position);
    401     }
    402 
    403     public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
    404         checkOpen();
    405         if (!target.isOpen()) {
    406             throw new ClosedChannelException();
    407         }
    408         checkReadable();
    409         if (target instanceof FileChannelImpl) {
    410             ((FileChannelImpl) target).checkWritable();
    411         }
    412         if (position < 0 || count < 0) {
    413             throw new IllegalArgumentException("position=" + position + " count=" + count);
    414         }
    415 
    416         if (count == 0 || position >= size()) {
    417             return 0;
    418         }
    419         count = Math.min(count, size() - position);
    420 
    421         // Try sendfile(2) first...
    422         boolean completed = false;
    423         if (target instanceof SocketChannelImpl) {
    424             FileDescriptor outFd = ((SocketChannelImpl) target).getFD();
    425             try {
    426                 begin();
    427                 try {
    428                     MutableLong offset = new MutableLong(position);
    429                     long rc = Libcore.os.sendfile(outFd, fd, offset, count);
    430                     completed = true;
    431                     return rc;
    432                 } catch (ErrnoException errnoException) {
    433                     // If the OS doesn't support what we asked for, we want to fall through and
    434                     // try a different approach. If it does support it, but it failed, we're done.
    435                     if (errnoException.errno != ENOSYS && errnoException.errno != EINVAL) {
    436                         throw errnoException.rethrowAsIOException();
    437                     }
    438                 }
    439             } finally {
    440                 end(completed);
    441             }
    442         }
    443         // ...fall back to write(2).
    444         ByteBuffer buffer = null;
    445         try {
    446             buffer = map(MapMode.READ_ONLY, position, count);
    447             return target.write(buffer);
    448         } finally {
    449             NioUtils.freeDirectBuffer(buffer);
    450         }
    451     }
    452 
    453     public FileChannel truncate(long size) throws IOException {
    454         checkOpen();
    455         if (size < 0) {
    456             throw new IllegalArgumentException("size < 0: " + size);
    457         }
    458         checkWritable();
    459         if (size < size()) {
    460             try {
    461                 Libcore.os.ftruncate(fd, size);
    462             } catch (ErrnoException errnoException) {
    463                 throw errnoException.rethrowAsIOException();
    464             }
    465         }
    466         if (position() > size) {
    467             position(size);
    468         }
    469         return this;
    470     }
    471 
    472     public int write(ByteBuffer buffer, long position) throws IOException {
    473         if (position < 0) {
    474             throw new IllegalArgumentException("position < 0: " + position);
    475         }
    476         return writeImpl(buffer, position);
    477     }
    478 
    479     public int write(ByteBuffer buffer) throws IOException {
    480         return writeImpl(buffer, -1);
    481     }
    482 
    483     private int writeImpl(ByteBuffer buffer, long position) throws IOException {
    484         checkOpen();
    485         checkWritable();
    486         if (buffer == null) {
    487             throw new NullPointerException("buffer == null");
    488         }
    489         if (!buffer.hasRemaining()) {
    490             return 0;
    491         }
    492         int bytesWritten = 0;
    493         boolean completed = false;
    494         try {
    495             begin();
    496             try {
    497                 if (position == -1) {
    498                     bytesWritten = Libcore.os.write(fd, buffer);
    499                 } else {
    500                     bytesWritten = Libcore.os.pwrite(fd, buffer, position);
    501                 }
    502             } catch (ErrnoException errnoException) {
    503                 throw errnoException.rethrowAsIOException();
    504             }
    505             completed = true;
    506         } finally {
    507             end(completed);
    508         }
    509         return bytesWritten;
    510     }
    511 
    512     public long write(ByteBuffer[] buffers, int offset, int length) throws IOException {
    513         Arrays.checkOffsetAndCount(buffers.length, offset, length);
    514         checkOpen();
    515         checkWritable();
    516         return transferIoVec(new IoVec(buffers, offset, length, IoVec.Direction.WRITEV));
    517     }
    518 
    519     /**
    520      * @param copyingIn true if we're copying data into the buffers (typically
    521      * because the caller is a file/network read operation), false if we're
    522      * copying data out of the buffers (for a file/network write operation).
    523      */
    524     static int calculateTotalRemaining(ByteBuffer[] buffers, int offset, int length, boolean copyingIn) {
    525         int count = 0;
    526         for (int i = offset; i < offset + length; ++i) {
    527             count += buffers[i].remaining();
    528             if (copyingIn) {
    529                 buffers[i].checkWritable();
    530             }
    531         }
    532         return count;
    533     }
    534 
    535     public FileDescriptor getFD() {
    536         return fd;
    537     }
    538 
    539     /**
    540      * Add a new pending lock to the manager. Throws an exception if the lock
    541      * would overlap an existing lock. Once the lock is acquired it remains in
    542      * this set as an acquired lock.
    543      */
    544     private synchronized void addLock(FileLock lock) throws OverlappingFileLockException {
    545         long lockEnd = lock.position() + lock.size();
    546         for (FileLock existingLock : locks) {
    547             if (existingLock.position() > lockEnd) {
    548                 // This, and all remaining locks, start beyond our end (so
    549                 // cannot overlap).
    550                 break;
    551             }
    552             if (existingLock.overlaps(lock.position(), lock.size())) {
    553                 throw new OverlappingFileLockException();
    554             }
    555         }
    556         locks.add(lock);
    557     }
    558 
    559     /**
    560      * Removes an acquired lock from the lock manager. If the lock did not exist
    561      * in the lock manager the operation is a no-op.
    562      */
    563     private synchronized void removeLock(FileLock lock) {
    564         locks.remove(lock);
    565     }
    566 }
    567