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