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