FileChannelImpl.java revision 8b15dcc5890963edad4dfcf558cc16027c7985e5
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        if ((mode & O_APPEND) != 0) {
242            return size();
243        }
244        try {
245            return Libcore.os.lseek(fd, 0L, SEEK_CUR);
246        } catch (ErrnoException errnoException) {
247            throw errnoException.rethrowAsIOException();
248        }
249    }
250
251    public FileChannel position(long newPosition) throws IOException {
252        checkOpen();
253        if (newPosition < 0) {
254            throw new IllegalArgumentException("position: " + newPosition);
255        }
256        synchronized (repositioningLock) {
257            try {
258                Libcore.os.lseek(fd, newPosition, SEEK_SET);
259            } catch (ErrnoException errnoException) {
260                throw errnoException.rethrowAsIOException();
261            }
262        }
263        return this;
264    }
265
266    public int read(ByteBuffer buffer, long position) throws IOException {
267        FileChannelImpl.checkWritable(buffer);
268        if (position < 0) {
269            throw new IllegalArgumentException("position: " + position);
270        }
271        checkOpen();
272        checkReadable();
273        if (!buffer.hasRemaining()) {
274            return 0;
275        }
276        synchronized (repositioningLock) {
277            int bytesRead = 0;
278            long preReadPosition = position();
279            position(position);
280            try {
281                bytesRead = read(buffer);
282            } finally {
283                position(preReadPosition);
284            }
285            return bytesRead;
286        }
287    }
288
289    public int read(ByteBuffer buffer) throws IOException {
290        FileChannelImpl.checkWritable(buffer);
291        checkOpen();
292        checkReadable();
293        if (!buffer.hasRemaining()) {
294            return 0;
295        }
296        synchronized (repositioningLock) {
297            int bytesRead = 0;
298            boolean completed = false;
299            try {
300                begin();
301                try {
302                    bytesRead = Libcore.os.read(fd, buffer);
303                } catch (ErrnoException errnoException) {
304                    if (errnoException.errno == EAGAIN) {
305                        // We don't throw if we try to read from an empty non-blocking pipe.
306                        bytesRead = 0;
307                    } else {
308                        throw errnoException.rethrowAsIOException();
309                    }
310                }
311                completed = true;
312            } finally {
313                end(completed && bytesRead >= 0);
314            }
315            if (bytesRead > 0) {
316                buffer.position(buffer.position() + bytesRead);
317            }
318            return bytesRead;
319        }
320    }
321
322    private int transferIoVec(IoVec ioVec) throws IOException {
323        if (ioVec.init() == 0) {
324            return 0;
325        }
326        int bytesTransferred = 0;
327        boolean completed = false;
328        try {
329            begin();
330            synchronized (repositioningLock) {
331                bytesTransferred = ioVec.doTransfer(fd);
332            }
333            completed = true;
334        } finally {
335            end(completed);
336        }
337        ioVec.didTransfer(bytesTransferred);
338        return bytesTransferred;
339    }
340
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
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
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        ByteBuffer buffer = null;
371
372        try {
373            if (src instanceof FileChannel) {
374                FileChannel fileSrc = (FileChannel) src;
375                long size = fileSrc.size();
376                long filePosition = fileSrc.position();
377                count = Math.min(count, size - filePosition);
378                buffer = fileSrc.map(MapMode.READ_ONLY, filePosition, count);
379                fileSrc.position(filePosition + count);
380            } else {
381                buffer = ByteBuffer.allocateDirect((int) count);
382                src.read(buffer);
383                buffer.flip();
384            }
385            return write(buffer, position);
386        } finally {
387            NioUtils.freeDirectBuffer(buffer);
388        }
389    }
390
391    public long transferTo(long position, long count, WritableByteChannel target)
392            throws IOException {
393        checkOpen();
394        if (!target.isOpen()) {
395            throw new ClosedChannelException();
396        }
397        if (target instanceof FileChannelImpl) {
398            ((FileChannelImpl) target).checkWritable();
399        }
400        if (position < 0 || count < 0) {
401            throw new IllegalArgumentException("position=" + position + " count=" + count);
402        }
403
404        if (count == 0 || position >= size()) {
405            return 0;
406        }
407        count = Math.min(count, size() - position);
408
409        // Try sendfile(2) first...
410        boolean completed = false;
411        if (target instanceof SocketChannelImpl) {
412            FileDescriptor outFd = ((SocketChannelImpl) target).getFD();
413            try {
414                begin();
415                try {
416                    MutableLong offset = new MutableLong(position);
417                    long rc = Libcore.os.sendfile(outFd, fd, offset, count);
418                    completed = true;
419                    return rc;
420                } catch (ErrnoException errnoException) {
421                    // If the OS doesn't support what we asked for, we want to fall through and
422                    // try a different approach. If it does support it, but it failed, we're done.
423                    if (errnoException.errno != ENOSYS && errnoException.errno != EINVAL) {
424                        throw errnoException.rethrowAsIOException();
425                    }
426                }
427            } finally {
428                end(completed);
429            }
430        }
431        // ...fall back to write(2).
432        ByteBuffer buffer = null;
433        try {
434            buffer = map(MapMode.READ_ONLY, position, count);
435            return target.write(buffer);
436        } finally {
437            NioUtils.freeDirectBuffer(buffer);
438        }
439    }
440
441    public FileChannel truncate(long size) throws IOException {
442        checkOpen();
443        if (size < 0) {
444            throw new IllegalArgumentException("size: " + size);
445        }
446        checkWritable();
447        if (size < size()) {
448            try {
449                Libcore.os.ftruncate(fd, size);
450            } catch (ErrnoException errnoException) {
451                throw errnoException.rethrowAsIOException();
452            }
453        }
454        return this;
455    }
456
457    public int write(ByteBuffer buffer, long position) throws IOException {
458        if (buffer == null) {
459            throw new NullPointerException("buffer == null");
460        }
461        if (position < 0) {
462            throw new IllegalArgumentException("position: " + position);
463        }
464        checkOpen();
465        checkWritable();
466        if (!buffer.hasRemaining()) {
467            return 0;
468        }
469        int bytesWritten = 0;
470        synchronized (repositioningLock) {
471            long preWritePosition = position();
472            position(position);
473            try {
474                bytesWritten = writeImpl(buffer);
475            } finally {
476                position(preWritePosition);
477            }
478        }
479        return bytesWritten;
480    }
481
482    public int write(ByteBuffer buffer) throws IOException {
483        checkOpen();
484        checkWritable();
485        if ((mode & O_APPEND) != 0) {
486            position(size());
487        }
488        return writeImpl(buffer);
489    }
490
491    private int writeImpl(ByteBuffer buffer) throws IOException {
492        synchronized (repositioningLock) {
493            int bytesWritten = 0;
494            boolean completed = false;
495            try {
496                begin();
497                try {
498                    bytesWritten = Libcore.os.write(fd, buffer);
499                } catch (ErrnoException errnoException) {
500                    throw errnoException.rethrowAsIOException();
501                }
502                completed = true;
503            } finally {
504                end(completed);
505            }
506            if (bytesWritten > 0) {
507                buffer.position(buffer.position() + bytesWritten);
508            }
509            return bytesWritten;
510        }
511    }
512
513    public long write(ByteBuffer[] buffers, int offset, int length) throws IOException {
514        Arrays.checkOffsetAndCount(buffers.length, offset, length);
515        checkOpen();
516        checkWritable();
517        return transferIoVec(new IoVec(buffers, offset, length, IoVec.Direction.WRITEV));
518    }
519
520    static void checkWritable(ByteBuffer buffer) {
521        if (buffer.isReadOnly()) {
522            throw new IllegalArgumentException("read-only buffer");
523        }
524    }
525
526    /**
527     * @param copyingIn true if we're copying data into the buffers (typically
528     * because the caller is a file/network read operation), false if we're
529     * copying data out of the buffers (for a file/network write operation).
530     */
531    static int calculateTotalRemaining(ByteBuffer[] buffers, int offset, int length, boolean copyingIn) {
532        int count = 0;
533        for (int i = offset; i < offset + length; ++i) {
534            count += buffers[i].remaining();
535            if (copyingIn) {
536                checkWritable(buffers[i]);
537            }
538        }
539        return count;
540    }
541
542    public FileDescriptor getFD() {
543        return fd;
544    }
545
546    /**
547     * Add a new pending lock to the manager. Throws an exception if the lock
548     * would overlap an existing lock. Once the lock is acquired it remains in
549     * this set as an acquired lock.
550     */
551    private synchronized void addLock(FileLock lock) throws OverlappingFileLockException {
552        long lockEnd = lock.position() + lock.size();
553        for (FileLock existingLock : locks) {
554            if (existingLock.position() > lockEnd) {
555                // This, and all remaining locks, start beyond our end (so
556                // cannot overlap).
557                break;
558            }
559            if (existingLock.overlaps(lock.position(), lock.size())) {
560                throw new OverlappingFileLockException();
561            }
562        }
563        locks.add(lock);
564    }
565
566    /**
567     * Removes an acquired lock from the lock manager. If the lock did not exist
568     * in the lock manager the operation is a no-op.
569     */
570    private synchronized void removeLock(FileLock lock) {
571        locks.remove(lock);
572    }
573}
574