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