• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
FileChannelImpl(Object stream, FileDescriptor fd, int mode)65     public FileChannelImpl(Object stream, FileDescriptor fd, int mode) {
66         this.fd = fd;
67         this.stream = stream;
68         this.mode = mode;
69     }
70 
checkOpen()71     private void checkOpen() throws ClosedChannelException {
72         if (!isOpen()) {
73             throw new ClosedChannelException();
74         }
75     }
76 
checkReadable()77     private void checkReadable() {
78         if ((mode & O_ACCMODE) == O_WRONLY) {
79             throw new NonReadableChannelException();
80         }
81     }
82 
checkWritable()83     private void checkWritable() {
84         if ((mode & O_ACCMODE) == O_RDONLY) {
85             throw new NonWritableChannelException();
86         }
87     }
88 
implCloseChannel()89     protected void implCloseChannel() throws IOException {
90         if (stream instanceof Closeable) {
91             ((Closeable) stream).close();
92         }
93     }
94 
basicLock(long position, long size, boolean shared, boolean wait)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 
translateLockLength(long byteCount)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 
FileLockImpl(FileChannel channel, long position, long size, boolean shared)141         public FileLockImpl(FileChannel channel, long position, long size, boolean shared) {
142             super(channel, position, size, shared);
143         }
144 
isValid()145         public boolean isValid() {
146             return !isReleased && channel().isOpen();
147         }
148 
release()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 
lock(long position, long size, boolean shared)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 
tryLock(long position, long size, boolean shared)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      */
release(FileLock lock)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 
force(boolean metadata)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 
map(MapMode mapMode, long position, long size)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 
position()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 
position(long newPosition)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 
read(ByteBuffer buffer, long position)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 
read(ByteBuffer buffer)277     public int read(ByteBuffer buffer) throws IOException {
278         return readImpl(buffer, -1);
279     }
280 
readImpl(ByteBuffer buffer, long position)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 
transferIoVec(IoVec ioVec)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 
read(ByteBuffer[] buffers, int offset, int length)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 
size()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 
transferFrom(ReadableByteChannel src, long position, long count)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 
transferTo(long position, long count, WritableByteChannel target)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 
truncate(long size)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 
write(ByteBuffer buffer, long position)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 
write(ByteBuffer buffer)465     public int write(ByteBuffer buffer) throws IOException {
466         return writeImpl(buffer, -1);
467     }
468 
writeImpl(ByteBuffer buffer, long position)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 
write(ByteBuffer[] buffers, int offset, int length)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      */
calculateTotalRemaining(ByteBuffer[] buffers, int offset, int length, boolean copyingIn)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 
getFD()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      */
addLock(FileLock lock)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      */
removeLock(FileLock lock)552     private synchronized void removeLock(FileLock lock) {
553         locks.remove(lock);
554     }
555 }
556