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