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