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