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