use std::{
fs::File,
io::{self, Read, Seek, SeekFrom},
path::Path,
};
use byteorder::{BigEndian, ByteOrder};
use crate::{BlockFileOpenError, BlockFileReadError, decrypt};
pub struct FileBlockEncryptReadStream {
file: File,
file_size: u64,
block_size: u64,
block_count: u64,
key: Vec<u8>,
cur_pos: u64,
cur_block: Option<u64>,
cur_block_encrypted: Vec<u8>,
cur_block_decrypted: Vec<u8>,
}
impl FileBlockEncryptReadStream {
pub fn new<P>(
file_path: P,
key: Vec<u8>,
) -> Result<FileBlockEncryptReadStream, BlockFileOpenError>
where
P: AsRef<Path>,
{
let mut file = File::open(file_path)?;
let mut buf: [u8; 8] = [0; 8];
file.read_exact(&mut buf)?;
let file_size = BigEndian::read_u64(&buf);
file.read_exact(&mut buf)?;
let block_size = BigEndian::read_u64(&buf);
if block_size == 0 {
return Err(BlockFileOpenError::InvalidBlockSize);
}
let mut block_count = file_size / block_size;
if file_size % block_size != 0 {
block_count += 1;
}
Ok(FileBlockEncryptReadStream {
file,
key,
file_size,
block_size,
block_count,
cur_block: None,
cur_pos: 0,
cur_block_encrypted: Vec::new(),
cur_block_decrypted: Vec::new(),
})
}
pub fn get_file_size(&self) -> u64 {
self.file_size
}
pub fn get_block_size(&self) -> u64 {
self.block_size
}
pub fn get_block_count(&self) -> u64 {
self.block_count
}
pub fn get_cursor(&self) -> u64 {
self.cur_pos
}
fn fetch_block(&mut self, block_num: u64) -> Result<(), BlockFileReadError> {
if block_num >= self.block_count {
return Err(BlockFileReadError::OutOfBounds);
}
let mut buf_block_index: [u8; 16] = [0; 16];
self.file.seek(io::SeekFrom::Start(16 + block_num * 16))?;
self.file.read_exact(&mut buf_block_index)?;
let block_start = BigEndian::read_u64(&buf_block_index[0..8]);
let block_length = BigEndian::read_u64(&buf_block_index[8..16]);
self.file.seek(io::SeekFrom::Start(block_start))?;
self.cur_block_encrypted.resize(block_length as usize, 0);
self.file.read_exact(&mut self.cur_block_encrypted)?;
self.cur_block_decrypted = decrypt(&self.cur_block_encrypted, &self.key)?;
self.cur_block = Some(block_num);
Ok(())
}
pub fn seek(&mut self, pos: SeekFrom) -> Result<u64, BlockFileReadError> {
let new_pos = match pos {
SeekFrom::Start(o) => o,
SeekFrom::End(o) => (self.file_size as i64).wrapping_add(o) as u64,
SeekFrom::Current(o) => (self.cur_pos as i64).wrapping_add(o) as u64,
};
if new_pos > self.file_size {
return Err(BlockFileReadError::OutOfBounds);
}
self.cur_pos = new_pos;
Ok(new_pos)
}
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, BlockFileReadError> {
if self.cur_pos >= self.file_size {
return Err(BlockFileReadError::EndOfFile);
}
let mut filled_length: usize = 0;
while filled_length < buf.len() && self.cur_pos < self.file_size {
let block_index = self.cur_pos / self.block_size;
let block_offset = (self.cur_pos % self.block_size) as usize;
if self.cur_block.is_none() || self.cur_block.unwrap() != block_index {
self.fetch_block(block_index)?;
}
let block_length = self.cur_block_decrypted.len();
if block_offset > block_length {
return Err(BlockFileReadError::OutOfBounds);
}
let mut bytes_to_copy = block_length - block_offset;
let bytes_can_fit = buf.len() - filled_length;
if bytes_to_copy > bytes_can_fit {
bytes_to_copy = bytes_can_fit;
}
buf[filled_length..filled_length + bytes_to_copy].copy_from_slice(
&self.cur_block_decrypted[block_offset..block_offset + bytes_to_copy],
);
filled_length += bytes_to_copy;
self.cur_pos += bytes_to_copy as u64;
}
Ok(filled_length)
}
pub fn close(self) {}
}