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