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