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