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