/** * Reads up to {@code count} bytes from this channel's file starting at {@code position} and * writes them to {@code target}. No bytes are transferred if {@code position} is larger than the * size of this channel's file. Less than {@code count} bytes are transferred if there less bytes * available from this channel's file or if the target channel is non-blocking and has less than * {@code count} bytes free in its input buffer. * * <p>Note that this channel's position is not modified. * * @param position the non-negative position to begin. * @param count the non-negative number of bytes to transfer. * @param target the target channel to write to. * @return the number of bytes that were transferred. * @throws IllegalArgumentException if the parameters are invalid. * @throws NonReadableChannelException if this channel is not readable. * @throws NonWritableChannelException if the target channel is not writable. * @throws ClosedChannelException if either channel has already been closed. * @throws AsynchronousCloseException if either channel is closed by other threads during this * operation. * @throws ClosedByInterruptException if the thread is interrupted during this operation. * @throws IOException if any I/O error occurs. */ public long transferTo(long position, long count, WritableByteChannel target) throws IOException { checkOpen(); if (!target.isOpen()) { throw new ClosedChannelException(); } checkReadable(); if (target instanceof IOCipherFileChannel) { ((IOCipherFileChannel) target).checkWritable(); } if (position < 0 || count < 0) { throw new IllegalArgumentException("position=" + position + " count=" + count); } if (count == 0 || position >= size()) { return 0; } count = Math.min(count, size() - position); try { ByteBuffer buffer = ByteBuffer.allocate((int) count); read(buffer, position); buffer.flip(); return target.write(buffer); } finally { // TODO determine whether we have memory leaking or perf issues here #567 } }
private int writeImpl(ByteBuffer buffer, long position) throws IOException { checkOpen(); checkWritable(); if (buffer == null) { throw new NullPointerException("buffer == null"); } if (!buffer.hasRemaining()) { return 0; } int bytesWritten = 0; boolean completed = false; try { begin(); try { if (position == -1) { bytesWritten = Libcore.os.write(fd, buffer, this.mode); } else { bytesWritten = Libcore.os.pwrite(fd, buffer, position, this.mode); } } catch (ErrnoException errnoException) { throw errnoException.rethrowAsIOException(); } completed = true; } finally { end(completed); } if (bytesWritten > 0) { buffer.position(buffer.position() + bytesWritten); } return bytesWritten; }
/** * Sets the file position pointer to a new value. * * <p>The argument is the number of bytes counted from the start of the file. The position cannot * be set to a value that is negative. The new position can be set beyond the current file size. * If set beyond the current file size, attempts to read will return end of file. Write operations * will succeed but they will fill the bytes between the current end of file and the new position * with the required number of (unspecified) byte values. * * @param newPosition the new file position, in bytes. * @return the receiver. * @throws IllegalArgumentException if the new position is negative. * @throws ClosedChannelException if this channel is closed. */ public IOCipherFileChannel position(long newPosition) throws IOException { if (newPosition < 0) throw new IllegalArgumentException("negative file position not allowed: " + newPosition); checkOpen(); fd.position = newPosition; return this; }
/** * Requests that all updates to this channel are committed to the storage device. * * <p>When this method returns, all modifications made to the platform file underlying this * channel have been committed if the file resides on a local storage device. If the file is not * hosted locally, for example on a networked file system, then applications cannot be certain * that the modifications have been committed. * * <p>There are no assurances given that changes made to the file using methods defined elsewhere * will be committed. For example, changes made via a mapped byte buffer may not be committed. * * <p>The <code>metadata</code> parameter indicates whether the update should include the file's * metadata such as last modification time, last access time, etc. Note that passing <code>true * </code> may invoke an underlying write to the operating system (if the platform is maintaining * metadata such as last access time), even if the channel is opened read-only. * * @param metadata {@code true} if the file metadata should be flushed in addition to the file * content, {@code false} otherwise. * @throws ClosedChannelException if this channel is already closed. * @throws IOException if another I/O error occurs. */ public void force(boolean metadata) throws IOException { checkOpen(); if ((mode & O_ACCMODE) != O_RDONLY) { try { Libcore.os.fsync(fd); // FUSE only has fsync, not fdatasync } catch (ErrnoException errnoException) { throw errnoException.rethrowAsIOException(); } } }
/** * Truncates the file underlying this channel to a given size. Any bytes beyond the given size are * removed from the file. If there are no bytes beyond the given size then the file contents are * unmodified. * * <p>If the file position is currently greater than the given size, then it is set to the new * size. * * @param size the maximum size of the underlying file. * @throws IllegalArgumentException if the requested size is negative. * @throws ClosedChannelException if this channel is closed. * @throws NonWritableChannelException if the channel cannot be written to. * @throws IOException if another I/O error occurs. * @return this channel. */ public IOCipherFileChannel truncate(long size) throws IOException { checkOpen(); if (size < 0) { throw new IllegalArgumentException("size: " + size); } checkWritable(); if (size < size()) { try { Libcore.os.ftruncate(fd, size); } catch (ErrnoException errnoException) { throw errnoException.rethrowAsIOException(); } } return this; }
/** * IOCipher version of POSIX lseek, since the underlying FUSE layer does not track the position in * open files for us, we do it inside of this class. This class wraps a {@link FileDescriptor}, so * you cannot specify one as an argument. * * @param offset the new position to seek to. * @param whence changes the pointer repositioning behavior: * <ul> * <li>if {@link info.guardianproject.libcore.io.OsConstants.SEEK_SET SEEK_SET} then file * pointer is set to <i>offset</i> * <li>if {@link info.guardianproject.libcore.io.OsConstants.SEEK_CUR SEEK_CUR} then file * pointer is set to <i>current position + offset</i> * <li>if {@link info.guardianproject.libcore.io.OsConstants.SEEK_END SEEK_END} then file * pointer is set to <i>file size + offset</i> * </ul> * * @throws ClosedChannelException if this channel is already closed. * @return new position of file pointer */ public long lseek(long offset, int whence) throws IOException { checkOpen(); long tmpPosition = fd.position; if (whence == SEEK_SET) { tmpPosition = offset; } else if (whence == SEEK_CUR) { tmpPosition += offset; } else if (whence == SEEK_END) { tmpPosition = size() + offset; } else { throw new IllegalArgumentException("Unknown 'whence': " + whence); } if (tmpPosition < 0) throw new IOException("negative resulting position: " + tmpPosition); else fd.position = tmpPosition; return fd.position; }
/** * Reads up to {@code count} bytes from {@code src} and stores them in this channel's file * starting at {@code position}. No bytes are transferred if {@code position} is larger than the * size of this channel's file. Less than {@code count} bytes are transferred if there are less * bytes remaining in the source channel or if the source channel is non-blocking and has less * than {@code count} bytes immediately available in its output buffer. * * <p>Note that this channel's position is not modified. * * @param src the source channel to read bytes from. * @param position the non-negative start position. * @param count the non-negative number of bytes to transfer. * @return the number of bytes that are transferred. * @throws IllegalArgumentException if the parameters are invalid. * @throws NonReadableChannelException if the source channel is not readable. * @throws NonWritableChannelException if this channel is not writable. * @throws ClosedChannelException if either channel has already been closed. * @throws AsynchronousCloseException if either channel is closed by other threads during this * operation. * @throws ClosedByInterruptException if the thread is interrupted during this operation. * @throws IOException if any I/O error occurs. */ public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException { checkOpen(); if (!src.isOpen()) { throw new ClosedChannelException(); } checkWritable(); if (position < 0 || count < 0 || count > Integer.MAX_VALUE) { throw new IllegalArgumentException("position=" + position + " count=" + count); } if (position > size()) { return 0; } ByteBuffer buffer = ByteBuffer.allocate((int) count); src.read(buffer); buffer.flip(); return write(buffer, position); }
private int readImpl(ByteBuffer buffer, long position) throws IOException { if (buffer.isReadOnly()) { throw new IllegalArgumentException("read-only buffer"); } checkOpen(); checkReadable(); if (!buffer.hasRemaining()) { return 0; } int bytesRead = 0; boolean completed = false; try { begin(); try { if (position == -1) { bytesRead = Libcore.os.read(fd, buffer); } else { bytesRead = Libcore.os.pread(fd, buffer, position); } if (bytesRead == 0) { bytesRead = -1; } } catch (ErrnoException errnoException) { if (errnoException.errno == EAGAIN) { // We don't throw if we try to read from an empty // non-blocking pipe. bytesRead = 0; } else { throw errnoException.rethrowAsIOException(); } } completed = true; } finally { end(completed && bytesRead >= 0); } if (bytesRead > 0) { buffer.position(buffer.position() + bytesRead); } return bytesRead; }
/** * Returns the current value of the file position pointer. * * @return the current position as a positive integer number of bytes from the start of the file. * @throws ClosedChannelException if this channel is closed. */ public long position() throws IOException { checkOpen(); return fd.position; }