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