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