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