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