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      */
FileChannelImpl(Closeable ioObject, FileDescriptor fd, int mode)66     public FileChannelImpl(Closeable ioObject, FileDescriptor fd, int mode) {
67         this.fd = fd;
68         this.ioObject = ioObject;
69         this.mode = mode;
70     }
71 
checkOpen()72     private void checkOpen() throws ClosedChannelException {
73         if (!isOpen()) {
74             throw new ClosedChannelException();
75         }
76     }
77 
checkReadable()78     private void checkReadable() {
79         if ((mode & O_ACCMODE) == O_WRONLY) {
80             throw new NonReadableChannelException();
81         }
82     }
83 
checkWritable()84     private void checkWritable() {
85         if ((mode & O_ACCMODE) == O_RDONLY) {
86             throw new NonWritableChannelException();
87         }
88     }
89 
implCloseChannel()90     protected void implCloseChannel() throws IOException {
91         ioObject.close();
92     }
93 
basicLock(long position, long size, boolean shared, boolean wait)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 
translateLockLength(long byteCount)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 
FileLockImpl(FileChannel channel, long position, long size, boolean shared)140         public FileLockImpl(FileChannel channel, long position, long size, boolean shared) {
141             super(channel, position, size, shared);
142         }
143 
isValid()144         public boolean isValid() {
145             return !isReleased && channel().isOpen();
146         }
147 
release()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 
lock(long position, long size, boolean shared)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 
tryLock(long position, long size, boolean shared)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      */
release(FileLock lock)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 
force(boolean metadata)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 
map(MapMode mapMode, long position, long size)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 
position()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 
position(long newPosition)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 
read(ByteBuffer buffer, long position)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 
read(ByteBuffer buffer)290     public int read(ByteBuffer buffer) throws IOException {
291         return readImpl(buffer, -1);
292     }
293 
readImpl(ByteBuffer buffer, long position)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 
transferIoVec(IoVec ioVec)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 
read(ByteBuffer[] buffers, int offset, int length)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 
size()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 
transferFrom(ReadableByteChannel src, long position, long count)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 
transferTo(long position, long count, WritableByteChannel target)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 
truncate(long size)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 
write(ByteBuffer buffer, long position)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 
write(ByteBuffer buffer)479     public int write(ByteBuffer buffer) throws IOException {
480         return writeImpl(buffer, -1);
481     }
482 
writeImpl(ByteBuffer buffer, long position)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 
write(ByteBuffer[] buffers, int offset, int length)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      */
calculateTotalRemaining(ByteBuffer[] buffers, int offset, int length, boolean copyingIn)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 
getFD()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      */
addLock(FileLock lock)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      */
removeLock(FileLock lock)563     private synchronized void removeLock(FileLock lock) {
564         locks.remove(lock);
565     }
566 }
567