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