1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package sun.nio.ch;
28
29import android.system.ErrnoException;
30
31import java.io.FileDescriptor;
32import java.io.IOException;
33import java.nio.ByteBuffer;
34import java.nio.DirectByteBuffer;
35import java.nio.MappedByteBuffer;
36import java.nio.channels.ClosedByInterruptException;
37import java.nio.channels.ClosedChannelException;
38import java.nio.channels.FileChannel;
39import java.nio.channels.FileLock;
40import java.nio.channels.FileLockInterruptionException;
41import java.nio.channels.NonReadableChannelException;
42import java.nio.channels.NonWritableChannelException;
43import java.nio.channels.OverlappingFileLockException;
44import java.nio.channels.ReadableByteChannel;
45import java.nio.channels.SelectableChannel;
46import java.nio.channels.WritableByteChannel;
47import java.security.AccessController;
48import java.util.ArrayList;
49import java.util.List;
50import libcore.io.Libcore;
51
52import dalvik.system.BlockGuard;
53import dalvik.system.CloseGuard;
54import sun.misc.Cleaner;
55import sun.security.action.GetPropertyAction;
56
57public class FileChannelImpl
58    extends FileChannel
59{
60    // Memory allocation size for mapping buffers
61    private static final long allocationGranularity;
62
63    // Used to make native read and write calls
64    private final FileDispatcher nd;
65
66    // File descriptor
67    // Android-changed: make public.
68    public final FileDescriptor fd;
69
70    // File access mode (immutable)
71    private final boolean writable;
72    private final boolean readable;
73    private final boolean append;
74
75    // Required to prevent finalization of creating stream (immutable)
76    private final Object parent;
77
78    // The path of the referenced file
79    // (null if the parent stream is created with a file descriptor)
80    private final String path;
81
82    // Thread-safe set of IDs of native threads, for signalling
83    private final NativeThreadSet threads = new NativeThreadSet(2);
84
85    // Lock for operations involving position and size
86    private final Object positionLock = new Object();
87
88    // Android-changed: Add CloseGuard support.
89    private final CloseGuard guard = CloseGuard.get();
90
91    private FileChannelImpl(FileDescriptor fd, String path, boolean readable,
92                            boolean writable, boolean append, Object parent)
93    {
94        this.fd = fd;
95        this.readable = readable;
96        this.writable = writable;
97        this.append = append;
98        this.parent = parent;
99        this.path = path;
100        this.nd = new FileDispatcherImpl(append);
101        // Android-changed: Add CloseGuard support.
102        if (fd != null && fd.valid()) {
103            guard.open("close");
104        }
105    }
106
107    // Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()
108    public static FileChannel open(FileDescriptor fd, String path,
109                                   boolean readable, boolean writable,
110                                   Object parent)
111    {
112        return new FileChannelImpl(fd, path, readable, writable, false, parent);
113    }
114
115    // Used by FileOutputStream.getChannel
116    public static FileChannel open(FileDescriptor fd, String path,
117                                   boolean readable, boolean writable,
118                                   boolean append, Object parent)
119    {
120        return new FileChannelImpl(fd, path, readable, writable, append, parent);
121    }
122
123    private void ensureOpen() throws IOException {
124        if (!isOpen())
125            throw new ClosedChannelException();
126    }
127
128
129    // -- Standard channel operations --
130
131    protected void implCloseChannel() throws IOException {
132        // Android-changed: Add CloseGuard support.
133        guard.close();
134        // Release and invalidate any locks that we still hold
135        if (fileLockTable != null) {
136            for (FileLock fl: fileLockTable.removeAll()) {
137                synchronized (fl) {
138                    if (fl.isValid()) {
139                        nd.release(fd, fl.position(), fl.size());
140                        ((FileLockImpl)fl).invalidate();
141                    }
142                }
143            }
144        }
145
146        // signal any threads blocked on this channel
147        threads.signalAndWait();
148
149        if (parent != null) {
150
151            // Close the fd via the parent stream's close method.  The parent
152            // will reinvoke our close method, which is defined in the
153            // superclass AbstractInterruptibleChannel, but the isOpen logic in
154            // that method will prevent this method from being reinvoked.
155            //
156            ((java.io.Closeable)parent).close();
157        } else {
158            nd.close(fd);
159        }
160
161    }
162
163    protected void finalize() throws Throwable {
164        try {
165            if (guard != null) {
166                guard.warnIfOpen();
167            }
168            close();
169        } finally {
170            super.finalize();
171        }
172    }
173
174    public int read(ByteBuffer dst) throws IOException {
175        ensureOpen();
176        if (!readable)
177            throw new NonReadableChannelException();
178        synchronized (positionLock) {
179            int n = 0;
180            int ti = -1;
181            try {
182                begin();
183                ti = threads.add();
184                if (!isOpen())
185                    return 0;
186                do {
187                    n = IOUtil.read(fd, dst, -1, nd);
188                } while ((n == IOStatus.INTERRUPTED) && isOpen());
189                return IOStatus.normalize(n);
190            } finally {
191                threads.remove(ti);
192                end(n > 0);
193                assert IOStatus.check(n);
194            }
195        }
196    }
197
198    public long read(ByteBuffer[] dsts, int offset, int length)
199        throws IOException
200    {
201        if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
202            throw new IndexOutOfBoundsException();
203        ensureOpen();
204        if (!readable)
205            throw new NonReadableChannelException();
206        synchronized (positionLock) {
207            long n = 0;
208            int ti = -1;
209            try {
210                begin();
211                ti = threads.add();
212                if (!isOpen())
213                    return 0;
214                do {
215                    n = IOUtil.read(fd, dsts, offset, length, nd);
216                } while ((n == IOStatus.INTERRUPTED) && isOpen());
217                return IOStatus.normalize(n);
218            } finally {
219                threads.remove(ti);
220                end(n > 0);
221                assert IOStatus.check(n);
222            }
223        }
224    }
225
226    public int write(ByteBuffer src) throws IOException {
227        ensureOpen();
228        if (!writable)
229            throw new NonWritableChannelException();
230        synchronized (positionLock) {
231            int n = 0;
232            int ti = -1;
233            try {
234                begin();
235                ti = threads.add();
236                if (!isOpen())
237                    return 0;
238                do {
239                    n = IOUtil.write(fd, src, -1, nd);
240                } while ((n == IOStatus.INTERRUPTED) && isOpen());
241                return IOStatus.normalize(n);
242            } finally {
243                threads.remove(ti);
244                end(n > 0);
245                assert IOStatus.check(n);
246            }
247        }
248    }
249
250    public long write(ByteBuffer[] srcs, int offset, int length)
251        throws IOException
252    {
253        if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
254            throw new IndexOutOfBoundsException();
255        ensureOpen();
256        if (!writable)
257            throw new NonWritableChannelException();
258        synchronized (positionLock) {
259            long n = 0;
260            int ti = -1;
261            try {
262                begin();
263                ti = threads.add();
264                if (!isOpen())
265                    return 0;
266                do {
267                    n = IOUtil.write(fd, srcs, offset, length, nd);
268                } while ((n == IOStatus.INTERRUPTED) && isOpen());
269                return IOStatus.normalize(n);
270            } finally {
271                threads.remove(ti);
272                end(n > 0);
273                assert IOStatus.check(n);
274            }
275        }
276    }
277
278    // -- Other operations --
279
280    public long position() throws IOException {
281        ensureOpen();
282        synchronized (positionLock) {
283            long p = -1;
284            int ti = -1;
285            try {
286                begin();
287                ti = threads.add();
288                if (!isOpen())
289                    return 0;
290                if (append) {
291                    BlockGuard.getThreadPolicy().onWriteToDisk();
292                }
293                do {
294                    // in append-mode then position is advanced to end before writing
295                    p = (append) ? nd.size(fd) : position0(fd, -1);
296                } while ((p == IOStatus.INTERRUPTED) && isOpen());
297                return IOStatus.normalize(p);
298            } finally {
299                threads.remove(ti);
300                end(p > -1);
301                assert IOStatus.check(p);
302            }
303        }
304    }
305
306    public FileChannel position(long newPosition) throws IOException {
307        ensureOpen();
308        if (newPosition < 0)
309            throw new IllegalArgumentException();
310        synchronized (positionLock) {
311            long p = -1;
312            int ti = -1;
313            try {
314                begin();
315                ti = threads.add();
316                if (!isOpen())
317                    return null;
318                BlockGuard.getThreadPolicy().onReadFromDisk();
319                do {
320                    p  = position0(fd, newPosition);
321                } while ((p == IOStatus.INTERRUPTED) && isOpen());
322                return this;
323            } finally {
324                threads.remove(ti);
325                end(p > -1);
326                assert IOStatus.check(p);
327            }
328        }
329    }
330
331    public long size() throws IOException {
332        ensureOpen();
333        synchronized (positionLock) {
334            long s = -1;
335            int ti = -1;
336            try {
337                begin();
338                ti = threads.add();
339                if (!isOpen())
340                    return -1;
341                do {
342                    s = nd.size(fd);
343                } while ((s == IOStatus.INTERRUPTED) && isOpen());
344                return IOStatus.normalize(s);
345            } finally {
346                threads.remove(ti);
347                end(s > -1);
348                assert IOStatus.check(s);
349            }
350        }
351    }
352
353    public FileChannel truncate(long newSize) throws IOException {
354        ensureOpen();
355        if (newSize < 0)
356            throw new IllegalArgumentException("Negative size");
357        if (!writable)
358            throw new NonWritableChannelException();
359        synchronized (positionLock) {
360            int rv = -1;
361            long p = -1;
362            int ti = -1;
363            try {
364                begin();
365                ti = threads.add();
366                if (!isOpen())
367                    return null;
368
369                // get current size
370                long size;
371                do {
372                    size = nd.size(fd);
373                } while ((size == IOStatus.INTERRUPTED) && isOpen());
374                if (!isOpen())
375                    return null;
376
377                // get current position
378                do {
379                    p = position0(fd, -1);
380                } while ((p == IOStatus.INTERRUPTED) && isOpen());
381                if (!isOpen())
382                    return null;
383                assert p >= 0;
384
385                // truncate file if given size is less than the current size
386                if (newSize < size) {
387                    do {
388                        rv = nd.truncate(fd, newSize);
389                    } while ((rv == IOStatus.INTERRUPTED) && isOpen());
390                    if (!isOpen())
391                        return null;
392                }
393
394                // if position is beyond new size then adjust it
395                if (p > newSize)
396                    p = newSize;
397                do {
398                    rv = (int)position0(fd, p);
399                } while ((rv == IOStatus.INTERRUPTED) && isOpen());
400                return this;
401            } finally {
402                threads.remove(ti);
403                end(rv > -1);
404                assert IOStatus.check(rv);
405            }
406        }
407    }
408
409    public void force(boolean metaData) throws IOException {
410        ensureOpen();
411        int rv = -1;
412        int ti = -1;
413        try {
414            begin();
415            ti = threads.add();
416            if (!isOpen())
417                return;
418            do {
419                rv = nd.force(fd, metaData);
420            } while ((rv == IOStatus.INTERRUPTED) && isOpen());
421        } finally {
422            threads.remove(ti);
423            end(rv > -1);
424            assert IOStatus.check(rv);
425        }
426    }
427
428    // Assume at first that the underlying kernel supports sendfile();
429    // set this to false if we find out later that it doesn't
430    //
431    private static volatile boolean transferSupported = true;
432
433    // Assume that the underlying kernel sendfile() will work if the target
434    // fd is a pipe; set this to false if we find out later that it doesn't
435    //
436    private static volatile boolean pipeSupported = true;
437
438    // Assume that the underlying kernel sendfile() will work if the target
439    // fd is a file; set this to false if we find out later that it doesn't
440    //
441    private static volatile boolean fileSupported = true;
442
443    private long transferToDirectlyInternal(long position, int icount,
444                                            WritableByteChannel target,
445                                            FileDescriptor targetFD)
446        throws IOException
447    {
448        assert !nd.transferToDirectlyNeedsPositionLock() ||
449               Thread.holdsLock(positionLock);
450
451        long n = -1;
452        int ti = -1;
453        try {
454            begin();
455            ti = threads.add();
456            if (!isOpen())
457                return -1;
458            BlockGuard.getThreadPolicy().onWriteToDisk();
459            do {
460                n = transferTo0(fd, position, icount, targetFD);
461            } while ((n == IOStatus.INTERRUPTED) && isOpen());
462            if (n == IOStatus.UNSUPPORTED_CASE) {
463                if (target instanceof SinkChannelImpl)
464                    pipeSupported = false;
465                if (target instanceof FileChannelImpl)
466                    fileSupported = false;
467                return IOStatus.UNSUPPORTED_CASE;
468            }
469            if (n == IOStatus.UNSUPPORTED) {
470                // Don't bother trying again
471                transferSupported = false;
472                return IOStatus.UNSUPPORTED;
473            }
474            return IOStatus.normalize(n);
475        } finally {
476            threads.remove(ti);
477            end (n > -1);
478        }
479    }
480
481    private long transferToDirectly(long position, int icount,
482                                    WritableByteChannel target)
483        throws IOException
484    {
485        if (!transferSupported)
486            return IOStatus.UNSUPPORTED;
487
488        FileDescriptor targetFD = null;
489        if (target instanceof FileChannelImpl) {
490            if (!fileSupported)
491                return IOStatus.UNSUPPORTED_CASE;
492            targetFD = ((FileChannelImpl)target).fd;
493        } else if (target instanceof SelChImpl) {
494            // Direct transfer to pipe causes EINVAL on some configurations
495            if ((target instanceof SinkChannelImpl) && !pipeSupported)
496                return IOStatus.UNSUPPORTED_CASE;
497
498            // Platform-specific restrictions. Now there is only one:
499            // Direct transfer to non-blocking channel could be forbidden
500            SelectableChannel sc = (SelectableChannel)target;
501            if (!nd.canTransferToDirectly(sc))
502                return IOStatus.UNSUPPORTED_CASE;
503
504            targetFD = ((SelChImpl)target).getFD();
505        }
506
507        if (targetFD == null)
508            return IOStatus.UNSUPPORTED;
509        int thisFDVal = IOUtil.fdVal(fd);
510        int targetFDVal = IOUtil.fdVal(targetFD);
511        if (thisFDVal == targetFDVal) // Not supported on some configurations
512            return IOStatus.UNSUPPORTED;
513
514        if (nd.transferToDirectlyNeedsPositionLock()) {
515            synchronized (positionLock) {
516                long pos = position();
517                try {
518                    return transferToDirectlyInternal(position, icount,
519                                                      target, targetFD);
520                } finally {
521                    position(pos);
522                }
523            }
524        } else {
525            return transferToDirectlyInternal(position, icount, target, targetFD);
526        }
527    }
528
529    // Maximum size to map when using a mapped buffer
530    private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L;
531
532    private long transferToTrustedChannel(long position, long count,
533                                          WritableByteChannel target)
534        throws IOException
535    {
536        boolean isSelChImpl = (target instanceof SelChImpl);
537        if (!((target instanceof FileChannelImpl) || isSelChImpl))
538            return IOStatus.UNSUPPORTED;
539
540        // Trusted target: Use a mapped buffer
541        long remaining = count;
542        while (remaining > 0L) {
543            long size = Math.min(remaining, MAPPED_TRANSFER_SIZE);
544            try {
545                MappedByteBuffer dbb = map(MapMode.READ_ONLY, position, size);
546                try {
547                    // ## Bug: Closing this channel will not terminate the write
548                    int n = target.write(dbb);
549                    assert n >= 0;
550                    remaining -= n;
551                    if (isSelChImpl) {
552                        // one attempt to write to selectable channel
553                        break;
554                    }
555                    assert n > 0;
556                    position += n;
557                } finally {
558                    unmap(dbb);
559                }
560            } catch (ClosedByInterruptException e) {
561                // target closed by interrupt as ClosedByInterruptException needs
562                // to be thrown after closing this channel.
563                assert !target.isOpen();
564                try {
565                    close();
566                } catch (Throwable suppressed) {
567                    e.addSuppressed(suppressed);
568                }
569                throw e;
570            } catch (IOException ioe) {
571                // Only throw exception if no bytes have been written
572                if (remaining == count)
573                    throw ioe;
574                break;
575            }
576        }
577        return count - remaining;
578    }
579
580    private long transferToArbitraryChannel(long position, int icount,
581                                            WritableByteChannel target)
582        throws IOException
583    {
584        // Untrusted target: Use a newly-erased buffer
585        int c = Math.min(icount, TRANSFER_SIZE);
586        ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
587        long tw = 0;                    // Total bytes written
588        long pos = position;
589        try {
590            Util.erase(bb);
591            while (tw < icount) {
592                bb.limit(Math.min((int)(icount - tw), TRANSFER_SIZE));
593                int nr = read(bb, pos);
594                if (nr <= 0)
595                    break;
596                bb.flip();
597                // ## Bug: Will block writing target if this channel
598                // ##      is asynchronously closed
599                int nw = target.write(bb);
600                tw += nw;
601                if (nw != nr)
602                    break;
603                pos += nw;
604                bb.clear();
605            }
606            return tw;
607        } catch (IOException x) {
608            if (tw > 0)
609                return tw;
610            throw x;
611        } finally {
612            Util.releaseTemporaryDirectBuffer(bb);
613        }
614    }
615
616    public long transferTo(long position, long count,
617                           WritableByteChannel target)
618        throws IOException
619    {
620        ensureOpen();
621        if (!target.isOpen())
622            throw new ClosedChannelException();
623        if (!readable)
624            throw new NonReadableChannelException();
625        if (target instanceof FileChannelImpl &&
626            !((FileChannelImpl)target).writable)
627            throw new NonWritableChannelException();
628        if ((position < 0) || (count < 0))
629            throw new IllegalArgumentException();
630        long sz = size();
631        if (position > sz)
632            return 0;
633        int icount = (int)Math.min(count, Integer.MAX_VALUE);
634        if ((sz - position) < icount)
635            icount = (int)(sz - position);
636
637        long n;
638
639        // Attempt a direct transfer, if the kernel supports it
640        if ((n = transferToDirectly(position, icount, target)) >= 0)
641            return n;
642
643        // Attempt a mapped transfer, but only to trusted channel types
644        if ((n = transferToTrustedChannel(position, icount, target)) >= 0)
645            return n;
646
647        // Slow path for untrusted targets
648        return transferToArbitraryChannel(position, icount, target);
649    }
650
651    private long transferFromFileChannel(FileChannelImpl src,
652                                         long position, long count)
653        throws IOException
654    {
655        if (!src.readable)
656            throw new NonReadableChannelException();
657        synchronized (src.positionLock) {
658            long pos = src.position();
659            long max = Math.min(count, src.size() - pos);
660
661            long remaining = max;
662            long p = pos;
663            while (remaining > 0L) {
664                long size = Math.min(remaining, MAPPED_TRANSFER_SIZE);
665                // ## Bug: Closing this channel will not terminate the write
666                MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, size);
667                try {
668                    long n = write(bb, position);
669                    assert n > 0;
670                    p += n;
671                    position += n;
672                    remaining -= n;
673                } catch (IOException ioe) {
674                    // Only throw exception if no bytes have been written
675                    if (remaining == max)
676                        throw ioe;
677                    break;
678                } finally {
679                    unmap(bb);
680                }
681            }
682            long nwritten = max - remaining;
683            src.position(pos + nwritten);
684            return nwritten;
685        }
686    }
687
688    private static final int TRANSFER_SIZE = 8192;
689
690    private long transferFromArbitraryChannel(ReadableByteChannel src,
691                                              long position, long count)
692        throws IOException
693    {
694        // Untrusted target: Use a newly-erased buffer
695        int c = (int)Math.min(count, TRANSFER_SIZE);
696        ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
697        long tw = 0;                    // Total bytes written
698        long pos = position;
699        try {
700            Util.erase(bb);
701            while (tw < count) {
702                bb.limit((int)Math.min((count - tw), (long)TRANSFER_SIZE));
703                // ## Bug: Will block reading src if this channel
704                // ##      is asynchronously closed
705                int nr = src.read(bb);
706                if (nr <= 0)
707                    break;
708                bb.flip();
709                int nw = write(bb, pos);
710                tw += nw;
711                if (nw != nr)
712                    break;
713                pos += nw;
714                bb.clear();
715            }
716            return tw;
717        } catch (IOException x) {
718            if (tw > 0)
719                return tw;
720            throw x;
721        } finally {
722            Util.releaseTemporaryDirectBuffer(bb);
723        }
724    }
725
726    public long transferFrom(ReadableByteChannel src,
727                             long position, long count)
728        throws IOException
729    {
730        ensureOpen();
731        if (!src.isOpen())
732            throw new ClosedChannelException();
733        if (!writable)
734            throw new NonWritableChannelException();
735        if ((position < 0) || (count < 0))
736            throw new IllegalArgumentException();
737        if (position > size())
738            return 0;
739        if (src instanceof FileChannelImpl)
740           return transferFromFileChannel((FileChannelImpl)src,
741                                          position, count);
742
743        return transferFromArbitraryChannel(src, position, count);
744    }
745
746    public int read(ByteBuffer dst, long position) throws IOException {
747        if (dst == null)
748            throw new NullPointerException();
749        if (position < 0)
750            throw new IllegalArgumentException("Negative position");
751        if (!readable)
752            throw new NonReadableChannelException();
753        ensureOpen();
754        if (nd.needsPositionLock()) {
755            synchronized (positionLock) {
756                return readInternal(dst, position);
757            }
758        } else {
759            return readInternal(dst, position);
760        }
761    }
762
763    private int readInternal(ByteBuffer dst, long position) throws IOException {
764        assert !nd.needsPositionLock() || Thread.holdsLock(positionLock);
765        int n = 0;
766        int ti = -1;
767        try {
768            begin();
769            ti = threads.add();
770            if (!isOpen())
771                return -1;
772            do {
773                n = IOUtil.read(fd, dst, position, nd);
774            } while ((n == IOStatus.INTERRUPTED) && isOpen());
775            return IOStatus.normalize(n);
776        } finally {
777            threads.remove(ti);
778            end(n > 0);
779            assert IOStatus.check(n);
780        }
781    }
782
783    public int write(ByteBuffer src, long position) throws IOException {
784        if (src == null)
785            throw new NullPointerException();
786        if (position < 0)
787            throw new IllegalArgumentException("Negative position");
788        if (!writable)
789            throw new NonWritableChannelException();
790        ensureOpen();
791        if (nd.needsPositionLock()) {
792            synchronized (positionLock) {
793                return writeInternal(src, position);
794            }
795        } else {
796            return writeInternal(src, position);
797        }
798    }
799
800    private int writeInternal(ByteBuffer src, long position) throws IOException {
801        assert !nd.needsPositionLock() || Thread.holdsLock(positionLock);
802        int n = 0;
803        int ti = -1;
804        try {
805            begin();
806            ti = threads.add();
807            if (!isOpen())
808                return -1;
809            do {
810                n = IOUtil.write(fd, src, position, nd);
811            } while ((n == IOStatus.INTERRUPTED) && isOpen());
812            return IOStatus.normalize(n);
813        } finally {
814            threads.remove(ti);
815            end(n > 0);
816            assert IOStatus.check(n);
817        }
818    }
819
820
821    // -- Memory-mapped buffers --
822
823    private static class Unmapper
824        implements Runnable
825    {
826        // may be required to close file
827        private static final NativeDispatcher nd = new FileDispatcherImpl();
828
829        // keep track of mapped buffer usage
830        static volatile int count;
831        static volatile long totalSize;
832        static volatile long totalCapacity;
833
834        private volatile long address;
835        private final long size;
836        private final int cap;
837        private final FileDescriptor fd;
838
839        private Unmapper(long address, long size, int cap,
840                         FileDescriptor fd)
841        {
842            assert (address != 0);
843            this.address = address;
844            this.size = size;
845            this.cap = cap;
846            this.fd = fd;
847
848            synchronized (Unmapper.class) {
849                count++;
850                totalSize += size;
851                totalCapacity += cap;
852            }
853        }
854
855        public void run() {
856            if (address == 0)
857                return;
858            unmap0(address, size);
859            address = 0;
860
861            // if this mapping has a valid file descriptor then we close it
862            if (fd.valid()) {
863                try {
864                    nd.close(fd);
865                } catch (IOException ignore) {
866                    // nothing we can do
867                }
868            }
869
870            synchronized (Unmapper.class) {
871                count--;
872                totalSize -= size;
873                totalCapacity -= cap;
874            }
875        }
876    }
877
878    private static void unmap(MappedByteBuffer bb) {
879        Cleaner cl = ((DirectBuffer)bb).cleaner();
880        if (cl != null)
881            cl.clean();
882    }
883
884    private static final int MAP_RO = 0;
885    private static final int MAP_RW = 1;
886    private static final int MAP_PV = 2;
887
888    public MappedByteBuffer map(MapMode mode, long position, long size)
889        throws IOException
890    {
891        ensureOpen();
892        if (mode == null)
893            throw new NullPointerException("Mode is null");
894        if (position < 0L)
895            throw new IllegalArgumentException("Negative position");
896        if (size < 0L)
897            throw new IllegalArgumentException("Negative size");
898        if (position + size < 0)
899            throw new IllegalArgumentException("Position + size overflow");
900        if (size > Integer.MAX_VALUE)
901            throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
902
903        int imode = -1;
904        if (mode == MapMode.READ_ONLY)
905            imode = MAP_RO;
906        else if (mode == MapMode.READ_WRITE)
907            imode = MAP_RW;
908        else if (mode == MapMode.PRIVATE)
909            imode = MAP_PV;
910        assert (imode >= 0);
911        if ((mode != MapMode.READ_ONLY) && !writable)
912            throw new NonWritableChannelException();
913        if (!readable)
914            throw new NonReadableChannelException();
915
916        long addr = -1;
917        int ti = -1;
918        try {
919            begin();
920            ti = threads.add();
921            if (!isOpen())
922                return null;
923
924            long filesize;
925            do {
926                filesize = nd.size(fd);
927            } while ((filesize == IOStatus.INTERRUPTED) && isOpen());
928            if (!isOpen())
929                return null;
930
931            if (filesize < position + size) { // Extend file size
932                // BEGIN Android-changed
933                /*
934                if (!writable) {
935                    throw new IOException("Channel not open for writing " +
936                        "- cannot extend file to required size");
937                }
938                */
939                // END Android-changed
940                int rv = 0;
941                do {
942                    // BEGIN Android-changed
943                    //int rv = nd.truncate(fd, position + size);
944                    try {
945                        rv = nd.truncate(fd, position + size);
946                    } catch (IOException r) {
947                        try {
948                            // If we're dealing with non-regular files, for example,
949                            // character devices such as /dev/zero. In those
950                            // cases, we ignore the failed truncation and continue
951                            // on.
952                            if (android.system.OsConstants.S_ISREG(Libcore.os.fstat(fd).st_mode)) {
953                                throw r;
954                            }
955                        } catch (ErrnoException e) {
956                            e.rethrowAsIOException();
957                        }
958                        break;
959                    }
960                    // END Android-changed
961                } while ((rv == IOStatus.INTERRUPTED) && isOpen());
962                if (!isOpen())
963                    return null;
964            }
965            if (size == 0) {
966                addr = 0;
967                // a valid file descriptor is not required
968                FileDescriptor dummy = new FileDescriptor();
969                return new DirectByteBuffer(0, 0, dummy, null,
970                        (!writable) || (imode == MAP_RO) /* readOnly */);
971            }
972
973            int pagePosition = (int)(position % allocationGranularity);
974            long mapPosition = position - pagePosition;
975            long mapSize = size + pagePosition;
976            try {
977                // If no exception was thrown from map0, the address is valid
978                BlockGuard.getThreadPolicy().onReadFromDisk();
979                addr = map0(imode, mapPosition, mapSize);
980            } catch (OutOfMemoryError x) {
981                // An OutOfMemoryError may indicate that we've exhausted memory
982                // so force gc and re-attempt map
983                System.gc();
984                try {
985                    Thread.sleep(100);
986                } catch (InterruptedException y) {
987                    Thread.currentThread().interrupt();
988                }
989                try {
990                    addr = map0(imode, mapPosition, mapSize);
991                } catch (OutOfMemoryError y) {
992                    // After a second OOME, fail
993                    throw new IOException("Map failed", y);
994                }
995            }
996
997            // On Windows, and potentially other platforms, we need an open
998            // file descriptor for some mapping operations.
999            FileDescriptor mfd;
1000            try {
1001                mfd = nd.duplicateForMapping(fd);
1002            } catch (IOException ioe) {
1003                unmap0(addr, mapSize);
1004                throw ioe;
1005            }
1006
1007            assert (IOStatus.checkAll(addr));
1008            assert (addr % allocationGranularity == 0);
1009            int isize = (int)size;
1010            Unmapper um = new Unmapper(addr, mapSize, isize, mfd);
1011            return new DirectByteBuffer(isize, addr + pagePosition, mfd, um,
1012                    (!writable) || (imode == MAP_RO));
1013        } finally {
1014            threads.remove(ti);
1015            end(IOStatus.checkAll(addr));
1016        }
1017    }
1018
1019    // -- Locks --
1020
1021    // keeps track of locks on this file
1022    private volatile FileLockTable fileLockTable;
1023
1024    // indicates if file locks are maintained system-wide (as per spec)
1025    private static boolean isSharedFileLockTable;
1026
1027    // indicates if the disableSystemWideOverlappingFileLockCheck property
1028    // has been checked
1029    private static volatile boolean propertyChecked;
1030
1031    // The lock list in J2SE 1.4/5.0 was local to each FileChannel instance so
1032    // the overlap check wasn't system wide when there were multiple channels to
1033    // the same file. This property is used to get 1.4/5.0 behavior if desired.
1034    private static boolean isSharedFileLockTable() {
1035        if (!propertyChecked) {
1036            synchronized (FileChannelImpl.class) {
1037                if (!propertyChecked) {
1038                    String value = AccessController.doPrivileged(
1039                        new GetPropertyAction(
1040                            "sun.nio.ch.disableSystemWideOverlappingFileLockCheck"));
1041                    isSharedFileLockTable = ((value == null) || value.equals("false"));
1042                    propertyChecked = true;
1043                }
1044            }
1045        }
1046        return isSharedFileLockTable;
1047    }
1048
1049    private FileLockTable fileLockTable() throws IOException {
1050        if (fileLockTable == null) {
1051            synchronized (this) {
1052                if (fileLockTable == null) {
1053                    if (isSharedFileLockTable()) {
1054                        int ti = threads.add();
1055                        try {
1056                            ensureOpen();
1057                            fileLockTable = FileLockTable.newSharedFileLockTable(this, fd);
1058                        } finally {
1059                            threads.remove(ti);
1060                        }
1061                    } else {
1062                        fileLockTable = new SimpleFileLockTable();
1063                    }
1064                }
1065            }
1066        }
1067        return fileLockTable;
1068    }
1069
1070    public FileLock lock(long position, long size, boolean shared)
1071        throws IOException
1072    {
1073        ensureOpen();
1074        if (shared && !readable)
1075            throw new NonReadableChannelException();
1076        if (!shared && !writable)
1077            throw new NonWritableChannelException();
1078        FileLockImpl fli = new FileLockImpl(this, position, size, shared);
1079        FileLockTable flt = fileLockTable();
1080        flt.add(fli);
1081        boolean completed = false;
1082        int ti = -1;
1083        try {
1084            begin();
1085            ti = threads.add();
1086            if (!isOpen())
1087                return null;
1088            int n;
1089            do {
1090                n = nd.lock(fd, true, position, size, shared);
1091            } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
1092            if (isOpen()) {
1093                if (n == FileDispatcher.RET_EX_LOCK) {
1094                    assert shared;
1095                    FileLockImpl fli2 = new FileLockImpl(this, position, size,
1096                                                         false);
1097                    flt.replace(fli, fli2);
1098                    fli = fli2;
1099                }
1100                completed = true;
1101            }
1102        } finally {
1103            if (!completed)
1104                flt.remove(fli);
1105            threads.remove(ti);
1106            try {
1107                end(completed);
1108            } catch (ClosedByInterruptException e) {
1109                throw new FileLockInterruptionException();
1110            }
1111        }
1112        return fli;
1113    }
1114
1115    public FileLock tryLock(long position, long size, boolean shared)
1116        throws IOException
1117    {
1118        ensureOpen();
1119        if (shared && !readable)
1120            throw new NonReadableChannelException();
1121        if (!shared && !writable)
1122            throw new NonWritableChannelException();
1123        FileLockImpl fli = new FileLockImpl(this, position, size, shared);
1124        FileLockTable flt = fileLockTable();
1125        flt.add(fli);
1126        int result;
1127
1128        int ti = threads.add();
1129        try {
1130            try {
1131                ensureOpen();
1132                result = nd.lock(fd, false, position, size, shared);
1133            } catch (IOException e) {
1134                flt.remove(fli);
1135                throw e;
1136            }
1137            if (result == FileDispatcher.NO_LOCK) {
1138                flt.remove(fli);
1139                return null;
1140            }
1141            if (result == FileDispatcher.RET_EX_LOCK) {
1142                assert shared;
1143                FileLockImpl fli2 = new FileLockImpl(this, position, size,
1144                                                     false);
1145                flt.replace(fli, fli2);
1146                return fli2;
1147            }
1148            return fli;
1149        } finally {
1150            threads.remove(ti);
1151        }
1152    }
1153
1154    void release(FileLockImpl fli) throws IOException {
1155        int ti = threads.add();
1156        try {
1157            ensureOpen();
1158            nd.release(fd, fli.position(), fli.size());
1159        } finally {
1160            threads.remove(ti);
1161        }
1162        assert fileLockTable != null;
1163        fileLockTable.remove(fli);
1164    }
1165
1166    // -- File lock support --
1167
1168    /**
1169     * A simple file lock table that maintains a list of FileLocks obtained by a
1170     * FileChannel. Use to get 1.4/5.0 behaviour.
1171     */
1172    private static class SimpleFileLockTable extends FileLockTable {
1173        // synchronize on list for access
1174        private final List<FileLock> lockList = new ArrayList<FileLock>(2);
1175
1176        public SimpleFileLockTable() {
1177        }
1178
1179        private void checkList(long position, long size)
1180            throws OverlappingFileLockException
1181        {
1182            assert Thread.holdsLock(lockList);
1183            for (FileLock fl: lockList) {
1184                if (fl.overlaps(position, size)) {
1185                    throw new OverlappingFileLockException();
1186                }
1187            }
1188        }
1189
1190        public void add(FileLock fl) throws OverlappingFileLockException {
1191            synchronized (lockList) {
1192                checkList(fl.position(), fl.size());
1193                lockList.add(fl);
1194            }
1195        }
1196
1197        public void remove(FileLock fl) {
1198            synchronized (lockList) {
1199                lockList.remove(fl);
1200            }
1201        }
1202
1203        public List<FileLock> removeAll() {
1204            synchronized(lockList) {
1205                List<FileLock> result = new ArrayList<FileLock>(lockList);
1206                lockList.clear();
1207                return result;
1208            }
1209        }
1210
1211        public void replace(FileLock fl1, FileLock fl2) {
1212            synchronized (lockList) {
1213                lockList.remove(fl1);
1214                lockList.add(fl2);
1215            }
1216        }
1217    }
1218
1219    // -- Native methods --
1220
1221    // Creates a new mapping
1222    private native long map0(int prot, long position, long length)
1223        throws IOException;
1224
1225    // Removes an existing mapping
1226    private static native int unmap0(long address, long length);
1227
1228    // Transfers from src to dst, or returns -2 if kernel can't do that
1229    private native long transferTo0(FileDescriptor src, long position,
1230                                    long count, FileDescriptor dst);
1231
1232    // Sets or reports this file's position
1233    // If offset is -1, the current position is returned
1234    // otherwise the position is set to offset
1235    private native long position0(FileDescriptor fd, long offset);
1236
1237    // Caches fieldIDs
1238    private static native long initIDs();
1239
1240    static {
1241        allocationGranularity = initIDs();
1242    }
1243
1244}
1245