/* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.apache.cassandra.db.commitlog; import java.io.DataInput; import java.nio.ByteBuffer; import org.apache.cassandra.io.util.DataPosition; import org.apache.cassandra.io.util.FileDataInput; import org.apache.cassandra.io.util.FileSegmentInputStream; /** * Each segment of an encrypted file may contain many encrypted chunks, and each chunk needs to be individually decrypted * to reconstruct the full segment. */ public class EncryptedFileSegmentInputStream extends FileSegmentInputStream implements FileDataInput, DataInput { private final long segmentOffset; private final int expectedLength; private final ChunkProvider chunkProvider; /** * Offset representing the decrypted chunks already processed in this segment. */ private int totalChunkOffset; public EncryptedFileSegmentInputStream(String filePath, long segmentOffset, int position, int expectedLength, ChunkProvider chunkProvider) { super(chunkProvider.nextChunk(), filePath, position); this.segmentOffset = segmentOffset; this.expectedLength = expectedLength; this.chunkProvider = chunkProvider; } public interface ChunkProvider { /** * Get the next chunk from the backing provider, if any chunks remain. * @return Next chunk, else null if no more chunks remain. */ ByteBuffer nextChunk(); } public long getFilePointer() { return segmentOffset + totalChunkOffset + buffer.position(); } public boolean isEOF() { return totalChunkOffset + buffer.position() >= expectedLength; } public long bytesRemaining() { return expectedLength - (totalChunkOffset + buffer.position()); } public void seek(long position) { long bufferPos = position - totalChunkOffset - segmentOffset; while (buffer != null && bufferPos > buffer.capacity()) { // rebuffer repeatedly until we have reached desired position buffer.position(buffer.limit()); // increases totalChunkOffset reBuffer(); bufferPos = position - totalChunkOffset - segmentOffset; } if (buffer == null || bufferPos < 0 || bufferPos > buffer.capacity()) throw new IllegalArgumentException( String.format("Unable to seek to position %d in %s (%d bytes) in partial mode", position, getPath(), segmentOffset + expectedLength)); buffer.position((int) bufferPos); } public long bytesPastMark(DataPosition mark) { throw new UnsupportedOperationException(); } public void reBuffer() { totalChunkOffset += buffer.position(); buffer = chunkProvider.nextChunk(); } }