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