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