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