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