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