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