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