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 */ 64 public FileChannelImpl(Object stream, FileDescriptor fd, int mode) { 65 this.fd = fd; 66 this.stream = stream; 67 this.mode = mode; 68 } 69 70 private void checkOpen() throws ClosedChannelException { 71 if (!isOpen()) { 72 throw new ClosedChannelException(); 73 } 74 } 75 76 private void checkReadable() { 77 if ((mode & O_ACCMODE) == O_WRONLY) { 78 throw new NonReadableChannelException(); 79 } 80 } 81 82 private void checkWritable() { 83 if ((mode & O_ACCMODE) == O_RDONLY) { 84 throw new NonWritableChannelException(); 85 } 86 } 87 88 protected void implCloseChannel() throws IOException { 89 if (stream instanceof Closeable) { 90 ((Closeable) stream).close(); 91 } 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 end(completed); 170 } 171 } 172 return resultLock; 173 } 174 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 */ 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 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 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 ftruncateException) { 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 try { 243 if (S_ISREG(Libcore.os.fstat(fd).st_mode) || ftruncateException.errno != EINVAL) { 244 throw ftruncateException.rethrowAsIOException(); 245 } 246 } catch (ErrnoException fstatException) { 247 throw fstatException.rethrowAsIOException(); 248 } 249 } 250 } 251 long alignment = position - position % Libcore.os.sysconf(_SC_PAGE_SIZE); 252 int offset = (int) (position - alignment); 253 MemoryBlock block = MemoryBlock.mmap(fd, alignment, size + offset, mapMode); 254 return new DirectByteBuffer(block, (int) size, offset, (mapMode == MapMode.READ_ONLY), mapMode); 255 } 256 257 public long position() throws IOException { 258 checkOpen(); 259 try { 260 return Libcore.os.lseek(fd, 0L, SEEK_CUR); 261 } catch (ErrnoException errnoException) { 262 throw errnoException.rethrowAsIOException(); 263 } 264 } 265 266 public FileChannel position(long newPosition) throws IOException { 267 checkOpen(); 268 if (newPosition < 0) { 269 throw new IllegalArgumentException("position: " + newPosition); 270 } 271 try { 272 Libcore.os.lseek(fd, newPosition, SEEK_SET); 273 } catch (ErrnoException errnoException) { 274 throw errnoException.rethrowAsIOException(); 275 } 276 return this; 277 } 278 279 public int read(ByteBuffer buffer, long position) throws IOException { 280 if (position < 0) { 281 throw new IllegalArgumentException("position: " + position); 282 } 283 return readImpl(buffer, position); 284 } 285 286 public int read(ByteBuffer buffer) throws IOException { 287 return readImpl(buffer, -1); 288 } 289 290 private int readImpl(ByteBuffer buffer, long position) throws IOException { 291 buffer.checkWritable(); 292 checkOpen(); 293 checkReadable(); 294 if (!buffer.hasRemaining()) { 295 return 0; 296 } 297 int bytesRead = 0; 298 boolean completed = false; 299 try { 300 begin(); 301 try { 302 if (position == -1) { 303 bytesRead = Libcore.os.read(fd, buffer); 304 } else { 305 bytesRead = Libcore.os.pread(fd, buffer, position); 306 } 307 if (bytesRead == 0) { 308 bytesRead = -1; 309 } 310 } catch (ErrnoException errnoException) { 311 if (errnoException.errno == EAGAIN) { 312 // We don't throw if we try to read from an empty non-blocking pipe. 313 bytesRead = 0; 314 } else { 315 throw errnoException.rethrowAsIOException(); 316 } 317 } 318 completed = true; 319 } finally { 320 end(completed && bytesRead >= 0); 321 } 322 if (bytesRead > 0) { 323 buffer.position(buffer.position() + bytesRead); 324 } 325 return bytesRead; 326 } 327 328 private int transferIoVec(IoVec ioVec) throws IOException { 329 if (ioVec.init() == 0) { 330 return 0; 331 } 332 int bytesTransferred = 0; 333 boolean completed = false; 334 try { 335 begin(); 336 bytesTransferred = ioVec.doTransfer(fd); 337 completed = true; 338 } finally { 339 end(completed); 340 } 341 ioVec.didTransfer(bytesTransferred); 342 return bytesTransferred; 343 } 344 345 public long read(ByteBuffer[] buffers, int offset, int length) throws IOException { 346 Arrays.checkOffsetAndCount(buffers.length, offset, length); 347 checkOpen(); 348 checkReadable(); 349 return transferIoVec(new IoVec(buffers, offset, length, IoVec.Direction.READV)); 350 } 351 352 public long size() throws IOException { 353 checkOpen(); 354 try { 355 return Libcore.os.fstat(fd).st_size; 356 } catch (ErrnoException errnoException) { 357 throw errnoException.rethrowAsIOException(); 358 } 359 } 360 361 public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException { 362 checkOpen(); 363 if (!src.isOpen()) { 364 throw new ClosedChannelException(); 365 } 366 checkWritable(); 367 if (position < 0 || count < 0 || count > Integer.MAX_VALUE) { 368 throw new IllegalArgumentException("position=" + position + " count=" + count); 369 } 370 if (position > size()) { 371 return 0; 372 } 373 374 // Although sendfile(2) originally supported writing to a regular file. 375 // In Linux 2.6 and later, it only supports writing to sockets. 376 377 // If our source is a regular file, mmap(2) rather than reading. 378 // Callers should only be using transferFrom for large transfers, 379 // so the mmap(2) overhead isn't a concern. 380 if (src instanceof FileChannel) { 381 FileChannel fileSrc = (FileChannel) src; 382 long size = fileSrc.size(); 383 long filePosition = fileSrc.position(); 384 count = Math.min(count, size - filePosition); 385 ByteBuffer buffer = fileSrc.map(MapMode.READ_ONLY, filePosition, count); 386 try { 387 fileSrc.position(filePosition + count); 388 return write(buffer, position); 389 } finally { 390 NioUtils.freeDirectBuffer(buffer); 391 } 392 } 393 394 // For non-file channels, all we can do is read and write via userspace. 395 ByteBuffer buffer = ByteBuffer.allocate((int) count); 396 src.read(buffer); 397 buffer.flip(); 398 return write(buffer, position); 399 } 400 401 public long transferTo(long position, long count, WritableByteChannel target) throws IOException { 402 checkOpen(); 403 if (!target.isOpen()) { 404 throw new ClosedChannelException(); 405 } 406 checkReadable(); 407 if (target instanceof FileChannelImpl) { 408 ((FileChannelImpl) target).checkWritable(); 409 } 410 if (position < 0 || count < 0) { 411 throw new IllegalArgumentException("position=" + position + " count=" + count); 412 } 413 414 if (count == 0 || position >= size()) { 415 return 0; 416 } 417 count = Math.min(count, size() - position); 418 419 // Try sendfile(2) first... 420 boolean completed = false; 421 if (target instanceof SocketChannelImpl) { 422 FileDescriptor outFd = ((SocketChannelImpl) target).getFD(); 423 try { 424 begin(); 425 try { 426 MutableLong offset = new MutableLong(position); 427 long rc = Libcore.os.sendfile(outFd, fd, offset, count); 428 completed = true; 429 return rc; 430 } catch (ErrnoException errnoException) { 431 // If the OS doesn't support what we asked for, we want to fall through and 432 // try a different approach. If it does support it, but it failed, we're done. 433 if (errnoException.errno != ENOSYS && errnoException.errno != EINVAL) { 434 throw errnoException.rethrowAsIOException(); 435 } 436 } 437 } finally { 438 end(completed); 439 } 440 } 441 // ...fall back to write(2). 442 ByteBuffer buffer = null; 443 try { 444 buffer = map(MapMode.READ_ONLY, position, count); 445 return target.write(buffer); 446 } finally { 447 NioUtils.freeDirectBuffer(buffer); 448 } 449 } 450 451 public FileChannel truncate(long size) throws IOException { 452 checkOpen(); 453 if (size < 0) { 454 throw new IllegalArgumentException("size < 0: " + size); 455 } 456 checkWritable(); 457 if (size < size()) { 458 try { 459 Libcore.os.ftruncate(fd, size); 460 } catch (ErrnoException errnoException) { 461 throw errnoException.rethrowAsIOException(); 462 } 463 } 464 return this; 465 } 466 467 public int write(ByteBuffer buffer, long position) throws IOException { 468 if (position < 0) { 469 throw new IllegalArgumentException("position < 0: " + position); 470 } 471 return writeImpl(buffer, position); 472 } 473 474 public int write(ByteBuffer buffer) throws IOException { 475 return writeImpl(buffer, -1); 476 } 477 478 private int writeImpl(ByteBuffer buffer, long position) throws IOException { 479 checkOpen(); 480 checkWritable(); 481 if (buffer == null) { 482 throw new NullPointerException("buffer == null"); 483 } 484 if (!buffer.hasRemaining()) { 485 return 0; 486 } 487 int bytesWritten = 0; 488 boolean completed = false; 489 try { 490 begin(); 491 try { 492 if (position == -1) { 493 bytesWritten = Libcore.os.write(fd, buffer); 494 } else { 495 bytesWritten = Libcore.os.pwrite(fd, buffer, position); 496 } 497 } catch (ErrnoException errnoException) { 498 throw errnoException.rethrowAsIOException(); 499 } 500 completed = true; 501 } finally { 502 end(completed); 503 } 504 if (bytesWritten > 0) { 505 buffer.position(buffer.position() + bytesWritten); 506 } 507 return bytesWritten; 508 } 509 510 public long write(ByteBuffer[] buffers, int offset, int length) throws IOException { 511 Arrays.checkOffsetAndCount(buffers.length, offset, length); 512 checkOpen(); 513 checkWritable(); 514 return transferIoVec(new IoVec(buffers, offset, length, IoVec.Direction.WRITEV)); 515 } 516 517 /** 518 * @param copyingIn true if we're copying data into the buffers (typically 519 * because the caller is a file/network read operation), false if we're 520 * copying data out of the buffers (for a file/network write operation). 521 */ 522 static int calculateTotalRemaining(ByteBuffer[] buffers, int offset, int length, boolean copyingIn) { 523 int count = 0; 524 for (int i = offset; i < offset + length; ++i) { 525 count += buffers[i].remaining(); 526 if (copyingIn) { 527 buffers[i].checkWritable(); 528 } 529 } 530 return count; 531 } 532 533 public FileDescriptor getFD() { 534 return fd; 535 } 536 537 /** 538 * Add a new pending lock to the manager. Throws an exception if the lock 539 * would overlap an existing lock. Once the lock is acquired it remains in 540 * this set as an acquired lock. 541 */ 542 private synchronized void addLock(FileLock lock) throws OverlappingFileLockException { 543 long lockEnd = lock.position() + lock.size(); 544 for (FileLock existingLock : locks) { 545 if (existingLock.position() > lockEnd) { 546 // This, and all remaining locks, start beyond our end (so 547 // cannot overlap). 548 break; 549 } 550 if (existingLock.overlaps(lock.position(), lock.size())) { 551 throw new OverlappingFileLockException(); 552 } 553 } 554 locks.add(lock); 555 } 556 557 /** 558 * Removes an acquired lock from the lock manager. If the lock did not exist 559 * in the lock manager the operation is a no-op. 560 */ 561 private synchronized void removeLock(FileLock lock) { 562 locks.remove(lock); 563 } 564 } 565