FileChannelImpl.java revision fc549a0b0388987b26dea524894d75a63d14783b
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 org.apache.harmony.luni.platform.Platform; 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 boolean completed = false; 297 int bytesRead = 0; 298 synchronized (repositioningLock) { 299 if (buffer.isDirect()) { 300 try { 301 begin(); 302 /* 303 * if (bytesRead <= EOF) dealt by read completed = false; 304 */ 305 int address = NioUtils.getDirectBufferAddress(buffer); 306 bytesRead = (int) Platform.FILE_SYSTEM.readDirect(IoUtils.getFd(fd), address, 307 buffer.position(), buffer.remaining()); 308 completed = true; 309 } finally { 310 end(completed && bytesRead >= 0); 311 } 312 } else { 313 try { 314 begin(); 315 /* 316 * if (bytesRead <= EOF) dealt by read completed = false; 317 */ 318 bytesRead = (int) Platform.FILE_SYSTEM.read(IoUtils.getFd(fd), buffer.array(), 319 buffer.arrayOffset() + buffer.position(), buffer.remaining()); 320 completed = true; 321 } finally { 322 end(completed && bytesRead >= 0); 323 } 324 } 325 if (bytesRead > 0) { 326 buffer.position(buffer.position() + bytesRead); 327 } 328 } 329 return bytesRead; 330 } 331 332 public long read(ByteBuffer[] buffers, int offset, int length) throws IOException { 333 Arrays.checkOffsetAndCount(buffers.length, offset, length); 334 checkOpen(); 335 checkReadable(); 336 int count = FileChannelImpl.calculateTotalRemaining(buffers, offset, length, true); 337 if (count == 0) { 338 return 0; 339 } 340 ByteBuffer[] directBuffers = new ByteBuffer[length]; 341 int[] handles = new int[length]; 342 int[] offsets = new int[length]; 343 int[] lengths = new int[length]; 344 for (int i = 0; i < length; i++) { 345 ByteBuffer buffer = buffers[i + offset]; 346 if (!buffer.isDirect()) { 347 buffer = ByteBuffer.allocateDirect(buffer.remaining()); 348 directBuffers[i] = buffer; 349 offsets[i] = 0; 350 } else { 351 offsets[i] = buffer.position(); 352 } 353 handles[i] = NioUtils.getDirectBufferAddress(buffer); 354 lengths[i] = buffer.remaining(); 355 } 356 long bytesRead = 0; 357 { 358 boolean completed = false; 359 try { 360 begin(); 361 synchronized (repositioningLock) { 362 bytesRead = Platform.FILE_SYSTEM.readv(IoUtils.getFd(fd), handles, offsets, lengths, length); 363 364 } 365 completed = true; 366 /* 367 * if (bytesRead < EOF) //dealt by readv? completed = false; 368 */ 369 } finally { 370 end(completed); 371 } 372 } 373 int end = offset + length; 374 long bytesRemaining = bytesRead; 375 for (int i = offset; i < end && bytesRemaining > 0; i++) { 376 if (buffers[i].isDirect()) { 377 if (lengths[i] < bytesRemaining) { 378 int pos = buffers[i].limit(); 379 buffers[i].position(pos); 380 bytesRemaining -= lengths[i]; 381 } else { 382 int pos = (int) bytesRemaining; 383 buffers[i].position(pos); 384 break; 385 } 386 } else { 387 ByteBuffer buf = directBuffers[i - offset]; 388 if (bytesRemaining < buf.remaining()) { 389 // this is the last step. 390 int pos = buf.position(); 391 buffers[i].put(buf); 392 buffers[i].position(pos + (int) bytesRemaining); 393 bytesRemaining = 0; 394 } else { 395 bytesRemaining -= buf.remaining(); 396 buffers[i].put(buf); 397 } 398 } 399 } 400 return bytesRead; 401 } 402 403 public long size() throws IOException { 404 checkOpen(); 405 try { 406 return Libcore.os.fstat(fd).st_size; 407 } catch (ErrnoException errnoException) { 408 throw errnoException.rethrowAsIOException(); 409 } 410 } 411 412 public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException { 413 checkOpen(); 414 if (!src.isOpen()) { 415 throw new ClosedChannelException(); 416 } 417 checkWritable(); 418 if (position < 0 || count < 0 || count > Integer.MAX_VALUE) { 419 throw new IllegalArgumentException("position=" + position + " count=" + count); 420 } 421 if (position > size()) { 422 return 0; 423 } 424 425 ByteBuffer buffer = null; 426 427 try { 428 if (src instanceof FileChannel) { 429 FileChannel fileSrc = (FileChannel) src; 430 long size = fileSrc.size(); 431 long filePosition = fileSrc.position(); 432 count = Math.min(count, size - filePosition); 433 buffer = fileSrc.map(MapMode.READ_ONLY, filePosition, count); 434 fileSrc.position(filePosition + count); 435 } else { 436 buffer = ByteBuffer.allocateDirect((int) count); 437 src.read(buffer); 438 buffer.flip(); 439 } 440 return write(buffer, position); 441 } finally { 442 NioUtils.freeDirectBuffer(buffer); 443 } 444 } 445 446 public long transferTo(long position, long count, WritableByteChannel target) 447 throws IOException { 448 checkOpen(); 449 if (!target.isOpen()) { 450 throw new ClosedChannelException(); 451 } 452 if (target instanceof FileChannelImpl) { 453 ((FileChannelImpl) target).checkWritable(); 454 } 455 if (position < 0 || count < 0) { 456 throw new IllegalArgumentException("position=" + position + " count=" + count); 457 } 458 459 if (count == 0 || position >= size()) { 460 return 0; 461 } 462 ByteBuffer buffer = null; 463 count = Math.min(count, size() - position); 464 if (target instanceof SocketChannelImpl) { 465 // only socket can be transfered by system call 466 return transferToSocket(fd, ((SocketChannelImpl) target).getFD(), position, count); 467 } 468 469 try { 470 buffer = map(MapMode.READ_ONLY, position, count); 471 return target.write(buffer); 472 } finally { 473 NioUtils.freeDirectBuffer(buffer); 474 } 475 } 476 477 private long transferToSocket(FileDescriptor src, FileDescriptor dst, long position, long count) throws IOException { 478 boolean completed = false; 479 try { 480 begin(); 481 long ret = Platform.FILE_SYSTEM.transfer(IoUtils.getFd(src), fd, position, count); 482 completed = true; 483 return ret; 484 } finally { 485 end(completed); 486 } 487 } 488 489 public FileChannel truncate(long size) throws IOException { 490 checkOpen(); 491 if (size < 0) { 492 throw new IllegalArgumentException("size: " + size); 493 } 494 checkWritable(); 495 if (size < size()) { 496 try { 497 Libcore.os.ftruncate(fd, size); 498 } catch (ErrnoException errnoException) { 499 throw errnoException.rethrowAsIOException(); 500 } 501 } 502 return this; 503 } 504 505 public int write(ByteBuffer buffer, long position) throws IOException { 506 if (buffer == null) { 507 throw new NullPointerException("buffer == null"); 508 } 509 if (position < 0) { 510 throw new IllegalArgumentException("position: " + position); 511 } 512 checkOpen(); 513 checkWritable(); 514 if (!buffer.hasRemaining()) { 515 return 0; 516 } 517 int bytesWritten = 0; 518 synchronized (repositioningLock) { 519 long preWritePosition = position(); 520 position(position); 521 try { 522 bytesWritten = writeImpl(buffer); 523 } finally { 524 position(preWritePosition); 525 } 526 } 527 return bytesWritten; 528 } 529 530 public int write(ByteBuffer buffer) throws IOException { 531 checkOpen(); 532 checkWritable(); 533 if ((mode & O_APPEND) != 0) { 534 position(size()); 535 } 536 return writeImpl(buffer); 537 } 538 539 private int writeImpl(ByteBuffer buffer) throws IOException { 540 int bytesWritten; 541 boolean completed = false; 542 synchronized (repositioningLock) { 543 if (buffer.isDirect()) { 544 try { 545 begin(); 546 int address = NioUtils.getDirectBufferAddress(buffer); 547 bytesWritten = (int) Platform.FILE_SYSTEM.writeDirect(IoUtils.getFd(fd), 548 address, buffer.position(), buffer.remaining()); 549 completed = true; 550 } finally { 551 end(completed); 552 } 553 } else { 554 try { 555 begin(); 556 bytesWritten = (int) Platform.FILE_SYSTEM.write(IoUtils.getFd(fd), buffer 557 .array(), buffer.arrayOffset() + buffer.position(), 558 buffer.remaining()); 559 completed = true; 560 } finally { 561 end(completed); 562 } 563 } 564 if (bytesWritten > 0) { 565 buffer.position(buffer.position() + bytesWritten); 566 } 567 } 568 return bytesWritten; 569 } 570 571 public long write(ByteBuffer[] buffers, int offset, int length) throws IOException { 572 Arrays.checkOffsetAndCount(buffers.length, offset, length); 573 checkOpen(); 574 checkWritable(); 575 int count = FileChannelImpl.calculateTotalRemaining(buffers, offset, length, false); 576 if (count == 0) { 577 return 0; 578 } 579 int[] handles = new int[length]; 580 int[] offsets = new int[length]; 581 int[] lengths = new int[length]; 582 // list of allocated direct ByteBuffers to prevent them from being GC-ed 583 ByteBuffer[] allocatedBufs = new ByteBuffer[length]; 584 585 for (int i = 0; i < length; i++) { 586 ByteBuffer buffer = buffers[i + offset]; 587 if (!buffer.isDirect()) { 588 ByteBuffer directBuffer = ByteBuffer.allocateDirect(buffer.remaining()); 589 directBuffer.put(buffer); 590 directBuffer.flip(); 591 buffer = directBuffer; 592 allocatedBufs[i] = directBuffer; 593 offsets[i] = 0; 594 } else { 595 offsets[i] = buffer.position(); 596 allocatedBufs[i] = null; 597 } 598 handles[i] = NioUtils.getDirectBufferAddress(buffer); 599 lengths[i] = buffer.remaining(); 600 } 601 602 long bytesWritten = 0; 603 boolean completed = false; 604 synchronized (repositioningLock) { 605 try { 606 begin(); 607 bytesWritten = Platform.FILE_SYSTEM.writev(IoUtils.getFd(fd), handles, offsets, lengths, length); 608 completed = true; 609 } finally { 610 end(completed); 611 for (ByteBuffer buffer : allocatedBufs) { 612 NioUtils.freeDirectBuffer(buffer); 613 } 614 } 615 } 616 617 long bytesRemaining = bytesWritten; 618 for (int i = offset; i < length + offset; i++) { 619 if (bytesRemaining > buffers[i].remaining()) { 620 int pos = buffers[i].limit(); 621 buffers[i].position(pos); 622 bytesRemaining -= buffers[i].remaining(); 623 } else { 624 int pos = buffers[i].position() + (int) bytesRemaining; 625 buffers[i].position(pos); 626 break; 627 } 628 } 629 return bytesWritten; 630 } 631 632 static void checkWritable(ByteBuffer buffer) { 633 if (buffer.isReadOnly()) { 634 throw new IllegalArgumentException("read-only buffer"); 635 } 636 } 637 638 /** 639 * @param copyingIn true if we're copying data into the buffers (typically 640 * because the caller is a file/network read operation), false if we're 641 * copying data out of the buffers (for a file/network write operation). 642 */ 643 static int calculateTotalRemaining(ByteBuffer[] buffers, int offset, int length, boolean copyingIn) { 644 int count = 0; 645 for (int i = offset; i < offset + length; ++i) { 646 count += buffers[i].remaining(); 647 if (copyingIn) { 648 checkWritable(buffers[i]); 649 } 650 } 651 return count; 652 } 653 654 public FileDescriptor getFD() { 655 return fd; 656 } 657 658 /** 659 * Add a new pending lock to the manager. Throws an exception if the lock 660 * would overlap an existing lock. Once the lock is acquired it remains in 661 * this set as an acquired lock. 662 */ 663 private synchronized void addLock(FileLock lock) throws OverlappingFileLockException { 664 long lockEnd = lock.position() + lock.size(); 665 for (FileLock existingLock : locks) { 666 if (existingLock.position() > lockEnd) { 667 // This, and all remaining locks, start beyond our end (so 668 // cannot overlap). 669 break; 670 } 671 if (existingLock.overlaps(lock.position(), lock.size())) { 672 throw new OverlappingFileLockException(); 673 } 674 } 675 locks.add(lock); 676 } 677 678 /** 679 * Removes an acquired lock from the lock manager. If the lock did not exist 680 * in the lock manager the operation is a no-op. 681 */ 682 private synchronized void removeLock(FileLock lock) { 683 locks.remove(lock); 684 } 685} 686