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