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