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