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