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