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