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