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