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