FileChannelImpl.java revision fc549a0b0388987b26dea524894d75a63d14783b
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 org.apache.harmony.luni.platform.Platform;
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        boolean completed = false;
297        int bytesRead = 0;
298        synchronized (repositioningLock) {
299            if (buffer.isDirect()) {
300                try {
301                    begin();
302                    /*
303                     * if (bytesRead <= EOF) dealt by read completed = false;
304                     */
305                    int address = NioUtils.getDirectBufferAddress(buffer);
306                    bytesRead = (int) Platform.FILE_SYSTEM.readDirect(IoUtils.getFd(fd), address,
307                            buffer.position(), buffer.remaining());
308                    completed = true;
309                } finally {
310                    end(completed && bytesRead >= 0);
311                }
312            } else {
313                try {
314                    begin();
315                    /*
316                     * if (bytesRead <= EOF) dealt by read completed = false;
317                     */
318                    bytesRead = (int) Platform.FILE_SYSTEM.read(IoUtils.getFd(fd), buffer.array(),
319                            buffer.arrayOffset() + buffer.position(), buffer.remaining());
320                    completed = true;
321                } finally {
322                    end(completed && bytesRead >= 0);
323                }
324            }
325            if (bytesRead > 0) {
326                buffer.position(buffer.position() + bytesRead);
327            }
328        }
329        return bytesRead;
330    }
331
332    public long read(ByteBuffer[] buffers, int offset, int length) throws IOException {
333        Arrays.checkOffsetAndCount(buffers.length, offset, length);
334        checkOpen();
335        checkReadable();
336        int count = FileChannelImpl.calculateTotalRemaining(buffers, offset, length, true);
337        if (count == 0) {
338            return 0;
339        }
340        ByteBuffer[] directBuffers = new ByteBuffer[length];
341        int[] handles = new int[length];
342        int[] offsets = new int[length];
343        int[] lengths = new int[length];
344        for (int i = 0; i < length; i++) {
345            ByteBuffer buffer = buffers[i + offset];
346            if (!buffer.isDirect()) {
347                buffer = ByteBuffer.allocateDirect(buffer.remaining());
348                directBuffers[i] = buffer;
349                offsets[i] = 0;
350            } else {
351                offsets[i] = buffer.position();
352            }
353            handles[i] = NioUtils.getDirectBufferAddress(buffer);
354            lengths[i] = buffer.remaining();
355        }
356        long bytesRead = 0;
357        {
358            boolean completed = false;
359            try {
360                begin();
361                synchronized (repositioningLock) {
362                    bytesRead = Platform.FILE_SYSTEM.readv(IoUtils.getFd(fd), handles, offsets, lengths, length);
363
364                }
365                completed = true;
366                /*
367                 * if (bytesRead < EOF) //dealt by readv? completed = false;
368                 */
369            } finally {
370                end(completed);
371            }
372        }
373        int end = offset + length;
374        long bytesRemaining = bytesRead;
375        for (int i = offset; i < end && bytesRemaining > 0; i++) {
376            if (buffers[i].isDirect()) {
377                if (lengths[i] < bytesRemaining) {
378                    int pos = buffers[i].limit();
379                    buffers[i].position(pos);
380                    bytesRemaining -= lengths[i];
381                } else {
382                    int pos = (int) bytesRemaining;
383                    buffers[i].position(pos);
384                    break;
385                }
386            } else {
387                ByteBuffer buf = directBuffers[i - offset];
388                if (bytesRemaining < buf.remaining()) {
389                    // this is the last step.
390                    int pos = buf.position();
391                    buffers[i].put(buf);
392                    buffers[i].position(pos + (int) bytesRemaining);
393                    bytesRemaining = 0;
394                } else {
395                    bytesRemaining -= buf.remaining();
396                    buffers[i].put(buf);
397                }
398            }
399        }
400        return bytesRead;
401    }
402
403    public long size() throws IOException {
404        checkOpen();
405        try {
406            return Libcore.os.fstat(fd).st_size;
407        } catch (ErrnoException errnoException) {
408            throw errnoException.rethrowAsIOException();
409        }
410    }
411
412    public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
413        checkOpen();
414        if (!src.isOpen()) {
415            throw new ClosedChannelException();
416        }
417        checkWritable();
418        if (position < 0 || count < 0 || count > Integer.MAX_VALUE) {
419            throw new IllegalArgumentException("position=" + position + " count=" + count);
420        }
421        if (position > size()) {
422            return 0;
423        }
424
425        ByteBuffer buffer = null;
426
427        try {
428            if (src instanceof FileChannel) {
429                FileChannel fileSrc = (FileChannel) src;
430                long size = fileSrc.size();
431                long filePosition = fileSrc.position();
432                count = Math.min(count, size - filePosition);
433                buffer = fileSrc.map(MapMode.READ_ONLY, filePosition, count);
434                fileSrc.position(filePosition + count);
435            } else {
436                buffer = ByteBuffer.allocateDirect((int) count);
437                src.read(buffer);
438                buffer.flip();
439            }
440            return write(buffer, position);
441        } finally {
442            NioUtils.freeDirectBuffer(buffer);
443        }
444    }
445
446    public long transferTo(long position, long count, WritableByteChannel target)
447            throws IOException {
448        checkOpen();
449        if (!target.isOpen()) {
450            throw new ClosedChannelException();
451        }
452        if (target instanceof FileChannelImpl) {
453            ((FileChannelImpl) target).checkWritable();
454        }
455        if (position < 0 || count < 0) {
456            throw new IllegalArgumentException("position=" + position + " count=" + count);
457        }
458
459        if (count == 0 || position >= size()) {
460            return 0;
461        }
462        ByteBuffer buffer = null;
463        count = Math.min(count, size() - position);
464        if (target instanceof SocketChannelImpl) {
465            // only socket can be transfered by system call
466            return transferToSocket(fd, ((SocketChannelImpl) target).getFD(), position, count);
467        }
468
469        try {
470            buffer = map(MapMode.READ_ONLY, position, count);
471            return target.write(buffer);
472        } finally {
473            NioUtils.freeDirectBuffer(buffer);
474        }
475    }
476
477    private long transferToSocket(FileDescriptor src, FileDescriptor dst, long position, long count) throws IOException {
478        boolean completed = false;
479        try {
480            begin();
481            long ret = Platform.FILE_SYSTEM.transfer(IoUtils.getFd(src), fd, position, count);
482            completed = true;
483            return ret;
484        } finally {
485            end(completed);
486        }
487    }
488
489    public FileChannel truncate(long size) throws IOException {
490        checkOpen();
491        if (size < 0) {
492            throw new IllegalArgumentException("size: " + size);
493        }
494        checkWritable();
495        if (size < size()) {
496            try {
497                Libcore.os.ftruncate(fd, size);
498            } catch (ErrnoException errnoException) {
499                throw errnoException.rethrowAsIOException();
500            }
501        }
502        return this;
503    }
504
505    public int write(ByteBuffer buffer, long position) throws IOException {
506        if (buffer == null) {
507            throw new NullPointerException("buffer == null");
508        }
509        if (position < 0) {
510            throw new IllegalArgumentException("position: " + position);
511        }
512        checkOpen();
513        checkWritable();
514        if (!buffer.hasRemaining()) {
515            return 0;
516        }
517        int bytesWritten = 0;
518        synchronized (repositioningLock) {
519            long preWritePosition = position();
520            position(position);
521            try {
522                bytesWritten = writeImpl(buffer);
523            } finally {
524                position(preWritePosition);
525            }
526        }
527        return bytesWritten;
528    }
529
530    public int write(ByteBuffer buffer) throws IOException {
531        checkOpen();
532        checkWritable();
533        if ((mode & O_APPEND) != 0) {
534            position(size());
535        }
536        return writeImpl(buffer);
537    }
538
539    private int writeImpl(ByteBuffer buffer) throws IOException {
540        int bytesWritten;
541        boolean completed = false;
542        synchronized (repositioningLock) {
543            if (buffer.isDirect()) {
544                try {
545                    begin();
546                    int address = NioUtils.getDirectBufferAddress(buffer);
547                    bytesWritten = (int) Platform.FILE_SYSTEM.writeDirect(IoUtils.getFd(fd),
548                            address, buffer.position(), buffer.remaining());
549                    completed = true;
550                } finally {
551                    end(completed);
552                }
553            } else {
554                try {
555                    begin();
556                    bytesWritten = (int) Platform.FILE_SYSTEM.write(IoUtils.getFd(fd), buffer
557                            .array(), buffer.arrayOffset() + buffer.position(),
558                            buffer.remaining());
559                    completed = true;
560                } finally {
561                    end(completed);
562                }
563            }
564            if (bytesWritten > 0) {
565                buffer.position(buffer.position() + bytesWritten);
566            }
567        }
568        return bytesWritten;
569    }
570
571    public long write(ByteBuffer[] buffers, int offset, int length) throws IOException {
572        Arrays.checkOffsetAndCount(buffers.length, offset, length);
573        checkOpen();
574        checkWritable();
575        int count = FileChannelImpl.calculateTotalRemaining(buffers, offset, length, false);
576        if (count == 0) {
577            return 0;
578        }
579        int[] handles = new int[length];
580        int[] offsets = new int[length];
581        int[] lengths = new int[length];
582        // list of allocated direct ByteBuffers to prevent them from being GC-ed
583        ByteBuffer[] allocatedBufs = new ByteBuffer[length];
584
585        for (int i = 0; i < length; i++) {
586            ByteBuffer buffer = buffers[i + offset];
587            if (!buffer.isDirect()) {
588                ByteBuffer directBuffer = ByteBuffer.allocateDirect(buffer.remaining());
589                directBuffer.put(buffer);
590                directBuffer.flip();
591                buffer = directBuffer;
592                allocatedBufs[i] = directBuffer;
593                offsets[i] = 0;
594            } else {
595                offsets[i] = buffer.position();
596                allocatedBufs[i] = null;
597            }
598            handles[i] = NioUtils.getDirectBufferAddress(buffer);
599            lengths[i] = buffer.remaining();
600        }
601
602        long bytesWritten = 0;
603        boolean completed = false;
604        synchronized (repositioningLock) {
605            try {
606                begin();
607                bytesWritten = Platform.FILE_SYSTEM.writev(IoUtils.getFd(fd), handles, offsets, lengths, length);
608                completed = true;
609            } finally {
610                end(completed);
611                for (ByteBuffer buffer : allocatedBufs) {
612                    NioUtils.freeDirectBuffer(buffer);
613                }
614            }
615        }
616
617        long bytesRemaining = bytesWritten;
618        for (int i = offset; i < length + offset; i++) {
619            if (bytesRemaining > buffers[i].remaining()) {
620                int pos = buffers[i].limit();
621                buffers[i].position(pos);
622                bytesRemaining -= buffers[i].remaining();
623            } else {
624                int pos = buffers[i].position() + (int) bytesRemaining;
625                buffers[i].position(pos);
626                break;
627            }
628        }
629        return bytesWritten;
630    }
631
632    static void checkWritable(ByteBuffer buffer) {
633        if (buffer.isReadOnly()) {
634            throw new IllegalArgumentException("read-only buffer");
635        }
636    }
637
638    /**
639     * @param copyingIn true if we're copying data into the buffers (typically
640     * because the caller is a file/network read operation), false if we're
641     * copying data out of the buffers (for a file/network write operation).
642     */
643    static int calculateTotalRemaining(ByteBuffer[] buffers, int offset, int length, boolean copyingIn) {
644        int count = 0;
645        for (int i = offset; i < offset + length; ++i) {
646            count += buffers[i].remaining();
647            if (copyingIn) {
648                checkWritable(buffers[i]);
649            }
650        }
651        return count;
652    }
653
654    public FileDescriptor getFD() {
655        return fd;
656    }
657
658    /**
659     * Add a new pending lock to the manager. Throws an exception if the lock
660     * would overlap an existing lock. Once the lock is acquired it remains in
661     * this set as an acquired lock.
662     */
663    private synchronized void addLock(FileLock lock) throws OverlappingFileLockException {
664        long lockEnd = lock.position() + lock.size();
665        for (FileLock existingLock : locks) {
666            if (existingLock.position() > lockEnd) {
667                // This, and all remaining locks, start beyond our end (so
668                // cannot overlap).
669                break;
670            }
671            if (existingLock.overlaps(lock.position(), lock.size())) {
672                throw new OverlappingFileLockException();
673            }
674        }
675        locks.add(lock);
676    }
677
678    /**
679     * Removes an acquired lock from the lock manager. If the lock did not exist
680     * in the lock manager the operation is a no-op.
681     */
682    private synchronized void removeLock(FileLock lock) {
683        locks.remove(lock);
684    }
685}
686