1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.nio; 19 20 import android.system.ErrnoException; 21 import android.system.StructFlock; 22 import android.util.MutableLong; 23 import java.io.Closeable; 24 import java.io.FileDescriptor; 25 import java.io.IOException; 26 import java.nio.channels.ClosedByInterruptException; 27 import java.nio.channels.ClosedChannelException; 28 import java.nio.channels.FileChannel; 29 import java.nio.channels.FileLock; 30 import java.nio.channels.FileLockInterruptionException; 31 import java.nio.channels.NonReadableChannelException; 32 import java.nio.channels.NonWritableChannelException; 33 import java.nio.channels.OverlappingFileLockException; 34 import java.nio.channels.ReadableByteChannel; 35 import java.nio.channels.WritableByteChannel; 36 import java.util.Arrays; 37 import java.util.Comparator; 38 import java.util.SortedSet; 39 import java.util.TreeSet; 40 import libcore.io.Libcore; 41 import static android.system.OsConstants.*; 42 43 /** 44 * Our concrete implementation of the abstract FileChannel class. 45 */ 46 final class FileChannelImpl extends FileChannel { 47 private static final Comparator<FileLock> LOCK_COMPARATOR = new Comparator<FileLock>() { 48 public int compare(FileLock lock1, FileLock lock2) { 49 long position1 = lock1.position(); 50 long position2 = lock2.position(); 51 return position1 > position2 ? 1 : (position1 < position2 ? -1 : 0); 52 } 53 }; 54 55 private final Closeable ioObject; 56 private final FileDescriptor fd; 57 private final int mode; 58 59 // The set of acquired and pending locks. 60 private final SortedSet<FileLock> locks = new TreeSet<FileLock>(LOCK_COMPARATOR); 61 62 /** 63 * Create a new file channel implementation class that wraps the given 64 * fd and operates in the specified mode. 65 */ 66 public FileChannelImpl(Closeable ioObject, FileDescriptor fd, int mode) { 67 this.fd = fd; 68 this.ioObject = ioObject; 69 this.mode = mode; 70 } 71 72 private void checkOpen() throws ClosedChannelException { 73 if (!isOpen()) { 74 throw new ClosedChannelException(); 75 } 76 } 77 78 private void checkReadable() { 79 if ((mode & O_ACCMODE) == O_WRONLY) { 80 throw new NonReadableChannelException(); 81 } 82 } 83 84 private void checkWritable() { 85 if ((mode & O_ACCMODE) == O_RDONLY) { 86 throw new NonWritableChannelException(); 87 } 88 } 89 90 protected void implCloseChannel() throws IOException { 91 ioObject.close(); 92 } 93 94 private FileLock basicLock(long position, long size, boolean shared, boolean wait) throws IOException { 95 int accessMode = (mode & O_ACCMODE); 96 if (accessMode == O_RDONLY) { 97 if (!shared) { 98 throw new NonWritableChannelException(); 99 } 100 } else if (accessMode == O_WRONLY) { 101 if (shared) { 102 throw new NonReadableChannelException(); 103 } 104 } 105 106 if (position < 0 || size < 0) { 107 throw new IllegalArgumentException("position=" + position + " size=" + size); 108 } 109 110 FileLock pendingLock = new FileLockImpl(this, position, size, shared); 111 addLock(pendingLock); 112 113 StructFlock flock = new StructFlock(); 114 flock.l_type = (short) (shared ? F_RDLCK : F_WRLCK); 115 flock.l_whence = (short) SEEK_SET; 116 flock.l_start = position; 117 flock.l_len = translateLockLength(size); 118 119 boolean success = false; 120 try { 121 success = (Libcore.os.fcntlFlock(fd, wait ? F_SETLKW64 : F_SETLK64, flock) != -1); 122 } catch (ErrnoException errnoException) { 123 throw errnoException.rethrowAsIOException(); 124 } finally { 125 if (!success) { 126 removeLock(pendingLock); 127 } 128 } 129 return success ? pendingLock : null; 130 } 131 132 private static long translateLockLength(long byteCount) { 133 // FileChannel uses Long.MAX_VALUE to mean "lock the whole file" where POSIX uses 0. 134 return (byteCount == Long.MAX_VALUE) ? 0 : byteCount; 135 } 136 137 private static final class FileLockImpl extends FileLock { 138 private boolean isReleased = false; 139 140 public FileLockImpl(FileChannel channel, long position, long size, boolean shared) { 141 super(channel, position, size, shared); 142 } 143 144 public boolean isValid() { 145 return !isReleased && channel().isOpen(); 146 } 147 148 public void release() throws IOException { 149 if (!channel().isOpen()) { 150 throw new ClosedChannelException(); 151 } 152 if (!isReleased) { 153 ((FileChannelImpl) channel()).release(this); 154 isReleased = true; 155 } 156 } 157 } 158 159 public final FileLock lock(long position, long size, boolean shared) throws IOException { 160 checkOpen(); 161 FileLock resultLock = null; 162 { 163 boolean completed = false; 164 try { 165 begin(); 166 resultLock = basicLock(position, size, shared, true); 167 completed = true; 168 } finally { 169 try { 170 end(completed); 171 } catch (ClosedByInterruptException e) { 172 throw new FileLockInterruptionException(); 173 } 174 } 175 } 176 return resultLock; 177 } 178 179 public final FileLock tryLock(long position, long size, boolean shared) throws IOException { 180 checkOpen(); 181 return basicLock(position, size, shared, false); 182 } 183 184 /** 185 * Non-API method to release a given lock on a file channel. Assumes that 186 * the lock will mark itself invalid after successful unlocking. 187 */ 188 public void release(FileLock lock) throws IOException { 189 checkOpen(); 190 191 StructFlock flock = new StructFlock(); 192 flock.l_type = (short) F_UNLCK; 193 flock.l_whence = (short) SEEK_SET; 194 flock.l_start = lock.position(); 195 flock.l_len = translateLockLength(lock.size()); 196 try { 197 Libcore.os.fcntlFlock(fd, F_SETLKW64, flock); 198 } catch (ErrnoException errnoException) { 199 throw errnoException.rethrowAsIOException(); 200 } 201 202 removeLock(lock); 203 } 204 205 public void force(boolean metadata) throws IOException { 206 checkOpen(); 207 if ((mode & O_ACCMODE) != O_RDONLY) { 208 try { 209 if (metadata) { 210 Libcore.os.fsync(fd); 211 } else { 212 Libcore.os.fdatasync(fd); 213 } 214 } catch (ErrnoException errnoException) { 215 throw errnoException.rethrowAsIOException(); 216 } 217 } 218 } 219 220 public final MappedByteBuffer map(MapMode mapMode, long position, long size) throws IOException { 221 checkOpen(); 222 if (mapMode == null) { 223 throw new NullPointerException("mapMode == null"); 224 } 225 if (position < 0 || size < 0 || size > Integer.MAX_VALUE) { 226 throw new IllegalArgumentException("position=" + position + " size=" + size); 227 } 228 int accessMode = (mode & O_ACCMODE); 229 if (accessMode == O_RDONLY) { 230 if (mapMode != MapMode.READ_ONLY) { 231 throw new NonWritableChannelException(); 232 } 233 } else if (accessMode == O_WRONLY) { 234 throw new NonReadableChannelException(); 235 } 236 if (position + size > size()) { 237 // We can't defer to FileChannel.truncate because that will only make a file shorter, 238 // and we only care about making our backing file longer here. 239 try { 240 Libcore.os.ftruncate(fd, position + size); 241 } catch (ErrnoException ftruncateException) { 242 // EINVAL can be thrown if we're dealing with non-regular 243 // files, for example, character devices such as /dev/zero. 244 // In those cases, we ignore the failed truncation and 245 // continue on. 246 try { 247 if (S_ISREG(Libcore.os.fstat(fd).st_mode) || ftruncateException.errno != EINVAL) { 248 throw ftruncateException.rethrowAsIOException(); 249 } 250 } catch (ErrnoException fstatException) { 251 throw fstatException.rethrowAsIOException(); 252 } 253 } 254 } 255 long alignment = position - position % Libcore.os.sysconf(_SC_PAGE_SIZE); 256 int offset = (int) (position - alignment); 257 MemoryBlock block = MemoryBlock.mmap(fd, alignment, size + offset, mapMode); 258 return new DirectByteBuffer(block, (int) size, offset, (mapMode == MapMode.READ_ONLY), mapMode); 259 } 260 261 public long position() throws IOException { 262 checkOpen(); 263 try { 264 return Libcore.os.lseek(fd, 0L, SEEK_CUR); 265 } catch (ErrnoException errnoException) { 266 throw errnoException.rethrowAsIOException(); 267 } 268 } 269 270 public FileChannel position(long newPosition) throws IOException { 271 checkOpen(); 272 if (newPosition < 0) { 273 throw new IllegalArgumentException("position: " + newPosition); 274 } 275 try { 276 Libcore.os.lseek(fd, newPosition, SEEK_SET); 277 } catch (ErrnoException errnoException) { 278 throw errnoException.rethrowAsIOException(); 279 } 280 return this; 281 } 282 283 public int read(ByteBuffer buffer, long position) throws IOException { 284 if (position < 0) { 285 throw new IllegalArgumentException("position: " + position); 286 } 287 return readImpl(buffer, position); 288 } 289 290 public int read(ByteBuffer buffer) throws IOException { 291 return readImpl(buffer, -1); 292 } 293 294 private int readImpl(ByteBuffer buffer, long position) throws IOException { 295 buffer.checkWritable(); 296 checkOpen(); 297 checkReadable(); 298 if (!buffer.hasRemaining()) { 299 return 0; 300 } 301 int bytesRead = 0; 302 boolean completed = false; 303 try { 304 begin(); 305 try { 306 if (position == -1) { 307 bytesRead = Libcore.os.read(fd, buffer); 308 } else { 309 bytesRead = Libcore.os.pread(fd, buffer, position); 310 } 311 if (bytesRead == 0) { 312 bytesRead = -1; 313 } 314 } catch (ErrnoException errnoException) { 315 if (errnoException.errno == EAGAIN) { 316 // We don't throw if we try to read from an empty non-blocking pipe. 317 bytesRead = 0; 318 } else { 319 throw errnoException.rethrowAsIOException(); 320 } 321 } 322 completed = true; 323 } finally { 324 end(completed && bytesRead >= 0); 325 } 326 327 return bytesRead; 328 } 329 330 private int transferIoVec(IoVec ioVec) throws IOException { 331 if (ioVec.init() == 0) { 332 return 0; 333 } 334 int bytesTransferred = 0; 335 boolean completed = false; 336 try { 337 begin(); 338 bytesTransferred = ioVec.doTransfer(fd); 339 completed = true; 340 } finally { 341 end(completed); 342 } 343 ioVec.didTransfer(bytesTransferred); 344 return bytesTransferred; 345 } 346 347 public long read(ByteBuffer[] buffers, int offset, int length) throws IOException { 348 Arrays.checkOffsetAndCount(buffers.length, offset, length); 349 checkOpen(); 350 checkReadable(); 351 return transferIoVec(new IoVec(buffers, offset, length, IoVec.Direction.READV)); 352 } 353 354 public long size() throws IOException { 355 checkOpen(); 356 try { 357 return Libcore.os.fstat(fd).st_size; 358 } catch (ErrnoException errnoException) { 359 throw errnoException.rethrowAsIOException(); 360 } 361 } 362 363 public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException { 364 checkOpen(); 365 if (!src.isOpen()) { 366 throw new ClosedChannelException(); 367 } 368 checkWritable(); 369 if (position < 0 || count < 0 || count > Integer.MAX_VALUE) { 370 throw new IllegalArgumentException("position=" + position + " count=" + count); 371 } 372 if (position > size()) { 373 return 0; 374 } 375 376 // Although sendfile(2) originally supported writing to a regular file. 377 // In Linux 2.6 and later, it only supports writing to sockets. 378 379 // If our source is a regular file, mmap(2) rather than reading. 380 // Callers should only be using transferFrom for large transfers, 381 // so the mmap(2) overhead isn't a concern. 382 if (src instanceof FileChannel) { 383 FileChannel fileSrc = (FileChannel) src; 384 long size = fileSrc.size(); 385 long filePosition = fileSrc.position(); 386 count = Math.min(count, size - filePosition); 387 ByteBuffer buffer = fileSrc.map(MapMode.READ_ONLY, filePosition, count); 388 try { 389 fileSrc.position(filePosition + count); 390 return write(buffer, position); 391 } finally { 392 NioUtils.freeDirectBuffer(buffer); 393 } 394 } 395 396 // For non-file channels, all we can do is read and write via userspace. 397 ByteBuffer buffer = ByteBuffer.allocate((int) count); 398 src.read(buffer); 399 buffer.flip(); 400 return write(buffer, position); 401 } 402 403 public long transferTo(long position, long count, WritableByteChannel target) throws IOException { 404 checkOpen(); 405 if (!target.isOpen()) { 406 throw new ClosedChannelException(); 407 } 408 checkReadable(); 409 if (target instanceof FileChannelImpl) { 410 ((FileChannelImpl) target).checkWritable(); 411 } 412 if (position < 0 || count < 0) { 413 throw new IllegalArgumentException("position=" + position + " count=" + count); 414 } 415 416 if (count == 0 || position >= size()) { 417 return 0; 418 } 419 count = Math.min(count, size() - position); 420 421 // Try sendfile(2) first... 422 boolean completed = false; 423 if (target instanceof SocketChannelImpl) { 424 FileDescriptor outFd = ((SocketChannelImpl) target).getFD(); 425 try { 426 begin(); 427 try { 428 MutableLong offset = new MutableLong(position); 429 long rc = Libcore.os.sendfile(outFd, fd, offset, count); 430 completed = true; 431 return rc; 432 } catch (ErrnoException errnoException) { 433 // If the OS doesn't support what we asked for, we want to fall through and 434 // try a different approach. If it does support it, but it failed, we're done. 435 if (errnoException.errno != ENOSYS && errnoException.errno != EINVAL) { 436 throw errnoException.rethrowAsIOException(); 437 } 438 } 439 } finally { 440 end(completed); 441 } 442 } 443 // ...fall back to write(2). 444 ByteBuffer buffer = null; 445 try { 446 buffer = map(MapMode.READ_ONLY, position, count); 447 return target.write(buffer); 448 } finally { 449 NioUtils.freeDirectBuffer(buffer); 450 } 451 } 452 453 public FileChannel truncate(long size) throws IOException { 454 checkOpen(); 455 if (size < 0) { 456 throw new IllegalArgumentException("size < 0: " + size); 457 } 458 checkWritable(); 459 if (size < size()) { 460 try { 461 Libcore.os.ftruncate(fd, size); 462 } catch (ErrnoException errnoException) { 463 throw errnoException.rethrowAsIOException(); 464 } 465 } 466 if (position() > size) { 467 position(size); 468 } 469 return this; 470 } 471 472 public int write(ByteBuffer buffer, long position) throws IOException { 473 if (position < 0) { 474 throw new IllegalArgumentException("position < 0: " + position); 475 } 476 return writeImpl(buffer, position); 477 } 478 479 public int write(ByteBuffer buffer) throws IOException { 480 return writeImpl(buffer, -1); 481 } 482 483 private int writeImpl(ByteBuffer buffer, long position) throws IOException { 484 checkOpen(); 485 checkWritable(); 486 if (buffer == null) { 487 throw new NullPointerException("buffer == null"); 488 } 489 if (!buffer.hasRemaining()) { 490 return 0; 491 } 492 int bytesWritten = 0; 493 boolean completed = false; 494 try { 495 begin(); 496 try { 497 if (position == -1) { 498 bytesWritten = Libcore.os.write(fd, buffer); 499 } else { 500 bytesWritten = Libcore.os.pwrite(fd, buffer, position); 501 } 502 } catch (ErrnoException errnoException) { 503 throw errnoException.rethrowAsIOException(); 504 } 505 completed = true; 506 } finally { 507 end(completed); 508 } 509 return bytesWritten; 510 } 511 512 public long write(ByteBuffer[] buffers, int offset, int length) throws IOException { 513 Arrays.checkOffsetAndCount(buffers.length, offset, length); 514 checkOpen(); 515 checkWritable(); 516 return transferIoVec(new IoVec(buffers, offset, length, IoVec.Direction.WRITEV)); 517 } 518 519 /** 520 * @param copyingIn true if we're copying data into the buffers (typically 521 * because the caller is a file/network read operation), false if we're 522 * copying data out of the buffers (for a file/network write operation). 523 */ 524 static int calculateTotalRemaining(ByteBuffer[] buffers, int offset, int length, boolean copyingIn) { 525 int count = 0; 526 for (int i = offset; i < offset + length; ++i) { 527 count += buffers[i].remaining(); 528 if (copyingIn) { 529 buffers[i].checkWritable(); 530 } 531 } 532 return count; 533 } 534 535 public FileDescriptor getFD() { 536 return fd; 537 } 538 539 /** 540 * Add a new pending lock to the manager. Throws an exception if the lock 541 * would overlap an existing lock. Once the lock is acquired it remains in 542 * this set as an acquired lock. 543 */ 544 private synchronized void addLock(FileLock lock) throws OverlappingFileLockException { 545 long lockEnd = lock.position() + lock.size(); 546 for (FileLock existingLock : locks) { 547 if (existingLock.position() > lockEnd) { 548 // This, and all remaining locks, start beyond our end (so 549 // cannot overlap). 550 break; 551 } 552 if (existingLock.overlaps(lock.position(), lock.size())) { 553 throw new OverlappingFileLockException(); 554 } 555 } 556 locks.add(lock); 557 } 558 559 /** 560 * Removes an acquired lock from the lock manager. If the lock did not exist 561 * in the lock manager the operation is a no-op. 562 */ 563 private synchronized void removeLock(FileLock lock) { 564 locks.remove(lock); 565 } 566 } 567