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