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