1 /*
   2  * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.nio.ch;
  27 
  28 import java.io.FileDescriptor;
  29 import java.io.IOException;
  30 import java.nio.ByteBuffer;
  31 import java.nio.MappedByteBuffer;
  32 import java.nio.channels.*;
  33 import java.util.ArrayList;
  34 import java.util.List;
  35 import java.security.AccessController;
  36 import sun.misc.Cleaner;
  37 import sun.misc.IoTrace;
  38 import sun.misc.IoTraceContext;
  39 import sun.security.action.GetPropertyAction;
  40 
  41 public class FileChannelImpl
  42     extends FileChannel
  43 {
  44     // Memory allocation size for mapping buffers
  45     private static final long allocationGranularity;
  46 
  47     // Used to make native read and write calls
  48     private final FileDispatcher nd;
  49 
  50     // File descriptor
  51     private final FileDescriptor fd;
  52 
  53     // File access mode (immutable)
  54     private final boolean writable;
  55     private final boolean readable;
  56     private final boolean append;
  57 
  58     // Required to prevent finalization of creating stream (immutable)
  59     private final Object parent;
  60 
  61     // The path of the referenced file (null if the parent stream is created with a file descriptor)
  62     private final String path;
  63 
  64     // Thread-safe set of IDs of native threads, for signalling
  65     private final NativeThreadSet threads = new NativeThreadSet(2);
  66 
  67     // Lock for operations involving position and size
  68     private final Object positionLock = new Object();
  69 
  70     private FileChannelImpl(FileDescriptor fd, String path, boolean readable,
  71                             boolean writable, boolean append, Object parent)
  72     {
  73         this.fd = fd;
  74         this.readable = readable;
  75         this.writable = writable;
  76         this.append = append;
  77         this.parent = parent;
  78         this.path = path;
  79         this.nd = new FileDispatcherImpl(append);
  80     }
  81 
  82     // Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()
  83     public static FileChannel open(FileDescriptor fd, String path,
  84                                    boolean readable, boolean writable,
  85                                    Object parent)
  86     {
  87         return new FileChannelImpl(fd, path, readable, writable, false, parent);
  88     }
  89 
  90     // Used by FileOutputStream.getChannel
  91     public static FileChannel open(FileDescriptor fd, String path,
  92                                    boolean readable, boolean writable,
  93                                    boolean append, Object parent)
  94     {
  95         return new FileChannelImpl(fd, path, readable, writable, append, parent);
  96     }
  97 
  98     private void ensureOpen() throws IOException {
  99         if (!isOpen())
 100             throw new ClosedChannelException();
 101     }
 102 
 103 
 104     // -- Standard channel operations --
 105 
 106     protected void implCloseChannel() throws IOException {
 107         // Release and invalidate any locks that we still hold
 108         if (fileLockTable != null) {
 109             for (FileLock fl: fileLockTable.removeAll()) {
 110                 synchronized (fl) {
 111                     if (fl.isValid()) {
 112                         nd.release(fd, fl.position(), fl.size());
 113                         ((FileLockImpl)fl).invalidate();
 114                     }
 115                 }
 116             }
 117         }
 118 
 119         nd.preClose(fd);
 120         threads.signalAndWait();
 121 
 122         if (parent != null) {
 123 
 124             // Close the fd via the parent stream's close method.  The parent
 125             // will reinvoke our close method, which is defined in the
 126             // superclass AbstractInterruptibleChannel, but the isOpen logic in
 127             // that method will prevent this method from being reinvoked.
 128             //
 129             ((java.io.Closeable)parent).close();
 130         } else {
 131             nd.close(fd);
 132         }
 133 
 134     }
 135 
 136     public int read(ByteBuffer dst) throws IOException {
 137         ensureOpen();
 138         if (!readable)
 139             throw new NonReadableChannelException();
 140         synchronized (positionLock) {
 141             int n = 0;
 142             int ti = -1;
 143             IoTraceContext traceContext = IoTrace.fileReadBegin(path);
 144             try {
 145                 begin();
 146                 ti = threads.add();
 147                 if (!isOpen())
 148                     return 0;
 149                 do {
 150                     n = IOUtil.read(fd, dst, -1, nd, positionLock);
 151                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 152                 return IOStatus.normalize(n);
 153             } finally {
 154                 threads.remove(ti);
 155                 IoTrace.fileReadEnd(traceContext, n > 0 ? n : 0);
 156                 end(n > 0);
 157                 assert IOStatus.check(n);
 158             }
 159         }
 160     }
 161 
 162     public long read(ByteBuffer[] dsts, int offset, int length)
 163         throws IOException
 164     {
 165         if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
 166             throw new IndexOutOfBoundsException();
 167         ensureOpen();
 168         if (!readable)
 169             throw new NonReadableChannelException();
 170         synchronized (positionLock) {
 171             long n = 0;
 172             int ti = -1;
 173             IoTraceContext traceContext = IoTrace.fileReadBegin(path);
 174             try {
 175                 begin();
 176                 ti = threads.add();
 177                 if (!isOpen())
 178                     return 0;
 179                 do {
 180                     n = IOUtil.read(fd, dsts, offset, length, nd);
 181                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 182                 return IOStatus.normalize(n);
 183             } finally {
 184                 threads.remove(ti);
 185                 IoTrace.fileReadEnd(traceContext, n > 0 ? n : 0);
 186                 end(n > 0);
 187                 assert IOStatus.check(n);
 188             }
 189         }
 190     }
 191 
 192     public int write(ByteBuffer src) throws IOException {
 193         ensureOpen();
 194         if (!writable)
 195             throw new NonWritableChannelException();
 196         synchronized (positionLock) {
 197             int n = 0;
 198             int ti = -1;
 199             IoTraceContext traceContext = IoTrace.fileWriteBegin(path);
 200             try {
 201                 begin();
 202                 ti = threads.add();
 203                 if (!isOpen())
 204                     return 0;
 205                 do {
 206                     n = IOUtil.write(fd, src, -1, nd, positionLock);
 207                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 208                 return IOStatus.normalize(n);
 209             } finally {
 210                 threads.remove(ti);
 211                 end(n > 0);
 212                 IoTrace.fileWriteEnd(traceContext, IOStatus.normalize(n));
 213                 assert IOStatus.check(n);
 214             }
 215         }
 216     }
 217 
 218     public long write(ByteBuffer[] srcs, int offset, int length)
 219         throws IOException
 220     {
 221         if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
 222             throw new IndexOutOfBoundsException();
 223         ensureOpen();
 224         if (!writable)
 225             throw new NonWritableChannelException();
 226         synchronized (positionLock) {
 227             long n = 0;
 228             int ti = -1;
 229             IoTraceContext traceContext = IoTrace.fileWriteBegin(path);
 230             try {
 231                 begin();
 232                 ti = threads.add();
 233                 if (!isOpen())
 234                     return 0;
 235                 do {
 236                     n = IOUtil.write(fd, srcs, offset, length, nd);
 237                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 238                 return IOStatus.normalize(n);
 239             } finally {
 240                 threads.remove(ti);
 241                 IoTrace.fileWriteEnd(traceContext, n > 0 ? n : 0);
 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         int n = 0;
 689         int ti = -1;
 690         IoTraceContext traceContext = IoTrace.fileReadBegin(path);
 691         try {
 692             begin();
 693             ti = threads.add();
 694             if (!isOpen())
 695                 return -1;
 696             do {
 697                 n = IOUtil.read(fd, dst, position, nd, positionLock);
 698             } while ((n == IOStatus.INTERRUPTED) && isOpen());
 699             return IOStatus.normalize(n);
 700         } finally {
 701             threads.remove(ti);
 702             IoTrace.fileReadEnd(traceContext, n > 0 ? n : 0);
 703             end(n > 0);
 704             assert IOStatus.check(n);
 705         }
 706     }
 707 
 708     public int write(ByteBuffer src, long position) throws IOException {
 709         if (src == null)
 710             throw new NullPointerException();
 711         if (position < 0)
 712             throw new IllegalArgumentException("Negative position");
 713         if (!writable)
 714             throw new NonWritableChannelException();
 715         ensureOpen();
 716         int n = 0;
 717         int ti = -1;
 718         IoTraceContext traceContext = IoTrace.fileWriteBegin(path);
 719         try {
 720             begin();
 721             ti = threads.add();
 722             if (!isOpen())
 723                 return -1;
 724             do {
 725                 n = IOUtil.write(fd, src, position, nd, positionLock);
 726             } while ((n == IOStatus.INTERRUPTED) && isOpen());
 727             return IOStatus.normalize(n);
 728         } finally {
 729             threads.remove(ti);
 730             end(n > 0);
 731             IoTrace.fileWriteEnd(traceContext, IOStatus.normalize(n));
 732             assert IOStatus.check(n);
 733         }
 734     }
 735 
 736 
 737     // -- Memory-mapped buffers --
 738 
 739     private static class Unmapper
 740         implements Runnable
 741     {
 742         // may be required to close file
 743         private static final NativeDispatcher nd = new FileDispatcherImpl();
 744 
 745         // keep track of mapped buffer usage
 746         static volatile int count;
 747         static volatile long totalSize;
 748         static volatile long totalCapacity;
 749 
 750         private volatile long address;
 751         private final long size;
 752         private final int cap;
 753         private final FileDescriptor fd;
 754 
 755         private Unmapper(long address, long size, int cap,
 756                          FileDescriptor fd)
 757         {
 758             assert (address != 0);
 759             this.address = address;
 760             this.size = size;
 761             this.cap = cap;
 762             this.fd = fd;
 763 
 764             synchronized (Unmapper.class) {
 765                 count++;
 766                 totalSize += size;
 767                 totalCapacity += cap;
 768             }
 769         }
 770 
 771         public void run() {
 772             if (address == 0)
 773                 return;
 774             unmap0(address, size);
 775             address = 0;
 776 
 777             // if this mapping has a valid file descriptor then we close it
 778             if (fd.valid()) {
 779                 try {
 780                     nd.close(fd);
 781                 } catch (IOException ignore) {
 782                     // nothing we can do
 783                 }
 784             }
 785 
 786             synchronized (Unmapper.class) {
 787                 count--;
 788                 totalSize -= size;
 789                 totalCapacity -= cap;
 790             }
 791         }
 792     }
 793 
 794     private static void unmap(MappedByteBuffer bb) {
 795         Cleaner cl = ((DirectBuffer)bb).cleaner();
 796         if (cl != null)
 797             cl.clean();
 798     }
 799 
 800     private static final int MAP_RO = 0;
 801     private static final int MAP_RW = 1;
 802     private static final int MAP_PV = 2;
 803 
 804     public MappedByteBuffer map(MapMode mode, long position, long size)
 805         throws IOException
 806     {
 807         ensureOpen();
 808         if (mode == null)
 809             throw new NullPointerException("Mode is null");
 810         if (position < 0L)
 811             throw new IllegalArgumentException("Negative position");
 812         if (size < 0L)
 813             throw new IllegalArgumentException("Negative size");
 814         if (position + size < 0)
 815             throw new IllegalArgumentException("Position + size overflow");
 816         if (size > Integer.MAX_VALUE)
 817             throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
 818 
 819         int imode = -1;
 820         if (mode == MapMode.READ_ONLY)
 821             imode = MAP_RO;
 822         else if (mode == MapMode.READ_WRITE)
 823             imode = MAP_RW;
 824         else if (mode == MapMode.PRIVATE)
 825             imode = MAP_PV;
 826         assert (imode >= 0);
 827         if ((mode != MapMode.READ_ONLY) && !writable)
 828             throw new NonWritableChannelException();
 829         if (!readable)
 830             throw new NonReadableChannelException();
 831 
 832         long addr = -1;
 833         int ti = -1;
 834         try {
 835             begin();
 836             ti = threads.add();
 837             if (!isOpen())
 838                 return null;
 839             if (size() < position + size) { // Extend file size
 840                 if (!writable) {
 841                     throw new IOException("Channel not open for writing " +
 842                         "- cannot extend file to required size");
 843                 }
 844                 int rv;
 845                 do {
 846                     rv = nd.truncate(fd, position + size);
 847                 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
 848             }
 849             if (size == 0) {
 850                 addr = 0;
 851                 // a valid file descriptor is not required
 852                 FileDescriptor dummy = new FileDescriptor();
 853                 if ((!writable) || (imode == MAP_RO))
 854                     return Util.newMappedByteBufferR(0, 0, dummy, null);
 855                 else
 856                     return Util.newMappedByteBuffer(0, 0, dummy, null);
 857             }
 858 
 859             int pagePosition = (int)(position % allocationGranularity);
 860             long mapPosition = position - pagePosition;
 861             long mapSize = size + pagePosition;
 862             try {
 863                 // If no exception was thrown from map0, the address is valid
 864                 addr = map0(imode, mapPosition, mapSize);
 865             } catch (OutOfMemoryError x) {
 866                 // An OutOfMemoryError may indicate that we've exhausted memory
 867                 // so force gc and re-attempt map
 868                 System.gc();
 869                 try {
 870                     Thread.sleep(100);
 871                 } catch (InterruptedException y) {
 872                     Thread.currentThread().interrupt();
 873                 }
 874                 try {
 875                     addr = map0(imode, mapPosition, mapSize);
 876                 } catch (OutOfMemoryError y) {
 877                     // After a second OOME, fail
 878                     throw new IOException("Map failed", y);
 879                 }
 880             }
 881 
 882             // On Windows, and potentially other platforms, we need an open
 883             // file descriptor for some mapping operations.
 884             FileDescriptor mfd;
 885             try {
 886                 mfd = nd.duplicateForMapping(fd);
 887             } catch (IOException ioe) {
 888                 unmap0(addr, mapSize);
 889                 throw ioe;
 890             }
 891 
 892             assert (IOStatus.checkAll(addr));
 893             assert (addr % allocationGranularity == 0);
 894             int isize = (int)size;
 895             Unmapper um = new Unmapper(addr, mapSize, isize, mfd);
 896             if ((!writable) || (imode == MAP_RO)) {
 897                 return Util.newMappedByteBufferR(isize,
 898                                                  addr + pagePosition,
 899                                                  mfd,
 900                                                  um);
 901             } else {
 902                 return Util.newMappedByteBuffer(isize,
 903                                                 addr + pagePosition,
 904                                                 mfd,
 905                                                 um);
 906             }
 907         } finally {
 908             threads.remove(ti);
 909             end(IOStatus.checkAll(addr));
 910         }
 911     }
 912 
 913     /**
 914      * Invoked by sun.management.ManagementFactoryHelper to create the management
 915      * interface for mapped buffers.
 916      */
 917     public static sun.misc.JavaNioAccess.BufferPool getMappedBufferPool() {
 918         return new sun.misc.JavaNioAccess.BufferPool() {
 919             @Override
 920             public String getName() {
 921                 return "mapped";
 922             }
 923             @Override
 924             public long getCount() {
 925                 return Unmapper.count;
 926             }
 927             @Override
 928             public long getTotalCapacity() {
 929                 return Unmapper.totalCapacity;
 930             }
 931             @Override
 932             public long getMemoryUsed() {
 933                 return Unmapper.totalSize;
 934             }
 935         };
 936     }
 937 
 938     // -- Locks --
 939 
 940 
 941 
 942     // keeps track of locks on this file
 943     private volatile FileLockTable fileLockTable;
 944 
 945     // indicates if file locks are maintained system-wide (as per spec)
 946     private static boolean isSharedFileLockTable;
 947 
 948     // indicates if the disableSystemWideOverlappingFileLockCheck property
 949     // has been checked
 950     private static volatile boolean propertyChecked;
 951 
 952     // The lock list in J2SE 1.4/5.0 was local to each FileChannel instance so
 953     // the overlap check wasn't system wide when there were multiple channels to
 954     // the same file. This property is used to get 1.4/5.0 behavior if desired.
 955     private static boolean isSharedFileLockTable() {
 956         if (!propertyChecked) {
 957             synchronized (FileChannelImpl.class) {
 958                 if (!propertyChecked) {
 959                     String value = AccessController.doPrivileged(
 960                         new GetPropertyAction(
 961                             "sun.nio.ch.disableSystemWideOverlappingFileLockCheck"));
 962                     isSharedFileLockTable = ((value == null) || value.equals("false"));
 963                     propertyChecked = true;
 964                 }
 965             }
 966         }
 967         return isSharedFileLockTable;
 968     }
 969 
 970     private FileLockTable fileLockTable() throws IOException {
 971         if (fileLockTable == null) {
 972             synchronized (this) {
 973                 if (fileLockTable == null) {
 974                     if (isSharedFileLockTable()) {
 975                         int ti = threads.add();
 976                         try {
 977                             ensureOpen();
 978                             fileLockTable = FileLockTable.newSharedFileLockTable(this, fd);
 979                         } finally {
 980                             threads.remove(ti);
 981                         }
 982                     } else {
 983                         fileLockTable = new SimpleFileLockTable();
 984                     }
 985                 }
 986             }
 987         }
 988         return fileLockTable;
 989     }
 990 
 991     public FileLock lock(long position, long size, boolean shared)
 992         throws IOException
 993     {
 994         ensureOpen();
 995         if (shared && !readable)
 996             throw new NonReadableChannelException();
 997         if (!shared && !writable)
 998             throw new NonWritableChannelException();
 999         FileLockImpl fli = new FileLockImpl(this, position, size, shared);
1000         FileLockTable flt = fileLockTable();
1001         flt.add(fli);
1002         boolean completed = false;
1003         int ti = -1;
1004         try {
1005             begin();
1006             ti = threads.add();
1007             if (!isOpen())
1008                 return null;
1009             int n;
1010             do {
1011                 n = nd.lock(fd, true, position, size, shared);
1012             } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
1013             if (isOpen()) {
1014                 if (n == FileDispatcher.RET_EX_LOCK) {
1015                     assert shared;
1016                     FileLockImpl fli2 = new FileLockImpl(this, position, size,
1017                                                          false);
1018                     flt.replace(fli, fli2);
1019                     fli = fli2;
1020                 }
1021                 completed = true;
1022             }
1023         } finally {
1024             if (!completed)
1025                 flt.remove(fli);
1026             threads.remove(ti);
1027             try {
1028                 end(completed);
1029             } catch (ClosedByInterruptException e) {
1030                 throw new FileLockInterruptionException();
1031             }
1032         }
1033         return fli;
1034     }
1035 
1036     public FileLock tryLock(long position, long size, boolean shared)
1037         throws IOException
1038     {
1039         ensureOpen();
1040         if (shared && !readable)
1041             throw new NonReadableChannelException();
1042         if (!shared && !writable)
1043             throw new NonWritableChannelException();
1044         FileLockImpl fli = new FileLockImpl(this, position, size, shared);
1045         FileLockTable flt = fileLockTable();
1046         flt.add(fli);
1047         int result;
1048 
1049         int ti = threads.add();
1050         try {
1051             try {
1052                 ensureOpen();
1053                 result = nd.lock(fd, false, position, size, shared);
1054             } catch (IOException e) {
1055                 flt.remove(fli);
1056                 throw e;
1057             }
1058             if (result == FileDispatcher.NO_LOCK) {
1059                 flt.remove(fli);
1060                 return null;
1061             }
1062             if (result == FileDispatcher.RET_EX_LOCK) {
1063                 assert shared;
1064                 FileLockImpl fli2 = new FileLockImpl(this, position, size,
1065                                                      false);
1066                 flt.replace(fli, fli2);
1067                 return fli2;
1068             }
1069             return fli;
1070         } finally {
1071             threads.remove(ti);
1072         }
1073     }
1074 
1075     void release(FileLockImpl fli) throws IOException {
1076         int ti = threads.add();
1077         try {
1078             ensureOpen();
1079             nd.release(fd, fli.position(), fli.size());
1080         } finally {
1081             threads.remove(ti);
1082         }
1083         assert fileLockTable != null;
1084         fileLockTable.remove(fli);
1085     }
1086 
1087     // -- File lock support --
1088 
1089     /**
1090      * A simple file lock table that maintains a list of FileLocks obtained by a
1091      * FileChannel. Use to get 1.4/5.0 behaviour.
1092      */
1093     private static class SimpleFileLockTable extends FileLockTable {
1094         // synchronize on list for access
1095         private final List<FileLock> lockList = new ArrayList<FileLock>(2);
1096 
1097         public SimpleFileLockTable() {
1098         }
1099 
1100         private void checkList(long position, long size)
1101             throws OverlappingFileLockException
1102         {
1103             assert Thread.holdsLock(lockList);
1104             for (FileLock fl: lockList) {
1105                 if (fl.overlaps(position, size)) {
1106                     throw new OverlappingFileLockException();
1107                 }
1108             }
1109         }
1110 
1111         public void add(FileLock fl) throws OverlappingFileLockException {
1112             synchronized (lockList) {
1113                 checkList(fl.position(), fl.size());
1114                 lockList.add(fl);
1115             }
1116         }
1117 
1118         public void remove(FileLock fl) {
1119             synchronized (lockList) {
1120                 lockList.remove(fl);
1121             }
1122         }
1123 
1124         public List<FileLock> removeAll() {
1125             synchronized(lockList) {
1126                 List<FileLock> result = new ArrayList<FileLock>(lockList);
1127                 lockList.clear();
1128                 return result;
1129             }
1130         }
1131 
1132         public void replace(FileLock fl1, FileLock fl2) {
1133             synchronized (lockList) {
1134                 lockList.remove(fl1);
1135                 lockList.add(fl2);
1136             }
1137         }
1138     }
1139 
1140     // -- Native methods --
1141 
1142     // Creates a new mapping
1143     private native long map0(int prot, long position, long length)
1144         throws IOException;
1145 
1146     // Removes an existing mapping
1147     private static native int unmap0(long address, long length);
1148 
1149     // Transfers from src to dst, or returns -2 if kernel can't do that
1150     private native long transferTo0(int src, long position, long count, int dst);
1151 
1152     // Sets or reports this file's position
1153     // If offset is -1, the current position is returned
1154     // otherwise the position is set to offset
1155     private native long position0(FileDescriptor fd, long offset);
1156 
1157     // Caches fieldIDs
1158     private static native long initIDs();
1159 
1160     static {
1161         Util.load();
1162         allocationGranularity = initIDs();
1163     }
1164 
1165 }