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