use super::*;
pub struct FileEditor {
file: fs::File,
directory: Directory,
high_mark: u32,
}
impl FileEditor {
#[inline]
pub fn create_new<P: ?Sized + AsRef<Path>>(path: &P, key: &Key) -> io::Result<FileEditor> {
create_new(path.as_ref(), key)
}
#[inline]
pub fn open<P: ?Sized + AsRef<Path>>(path: &P, key: &Key) -> io::Result<FileEditor> {
open(path.as_ref(), key)
}
#[inline]
pub fn create_empty<P: ?Sized + AsRef<Path>>(path: &P, key: &Key) -> io::Result<()> {
create_empty(path.as_ref(), key)
}
#[inline]
pub fn read_only<P: ?Sized + AsRef<Path>>(path: &P, key: &Key) -> io::Result<FileEditor> {
read_only(path.as_ref(), key)
}
}
#[inline(never)]
fn create_new(path: &Path, key: &Key) -> io::Result<FileEditor> {
let mut file = fs::OpenOptions::new().create_new(true).read(true).write(true).open(path)?;
let mut header = Header::default();
header.info.directory.offset = Header::BLOCKS_LEN as u32;
header.info.directory.size = 0;
crypt::encrypt_section(&mut [], &mut header.info.directory, key);
crypt::encrypt_header(&mut header, key);
file.write_all(dataview::bytes(&header))?;
file.sync_data()?;
let directory = Directory::new();
let high_mark = Header::BLOCKS_LEN as u32;
Ok(FileEditor { file, directory, high_mark })
}
#[inline(never)]
fn open(path: &Path, key: &Key) -> io::Result<FileEditor> {
let mut file = fs::OpenOptions::new().read(true).write(true).open(path)?;
let (info, directory) = read_header(&mut file, key)?;
let high_mark = info.directory.offset + info.directory.size * InfoHeader::BLOCKS_LEN as u32;
Ok(FileEditor { file, directory, high_mark })
}
#[inline(never)]
fn create_empty(path: &Path, key: &Key) -> io::Result<()> {
let mut header = Header::default();
header.info.directory.offset = Header::BLOCKS_LEN as u32;
header.info.directory.size = 0;
crypt::encrypt_section(&mut [], &mut header.info.directory, key);
crypt::encrypt_header(&mut header, key);
fs::write(path, dataview::bytes(&header))
}
#[inline(never)]
fn read_only(path: &Path, key: &Key) -> io::Result<FileEditor> {
let mut file = fs::File::open(path)?;
let (info, directory) = read_header(&mut file, key)?;
let high_mark = u32::max(Header::BLOCKS_LEN as u32, info.directory.offset + info.directory.size * InfoHeader::BLOCKS_LEN as u32);
Ok(FileEditor { file, directory, high_mark })
}
impl ops::Deref for FileEditor {
type Target = Directory;
#[inline]
fn deref(&self) -> &Directory {
&self.directory
}
}
impl ops::DerefMut for FileEditor {
#[inline]
fn deref_mut(&mut self) -> &mut Directory {
&mut self.directory
}
}
impl FileEditor {
#[inline]
pub fn high_mark(&self) -> u32 {
self.high_mark
}
#[inline]
pub fn edit_file(&mut self, path: &[u8]) -> FileEditFile<'_> {
let desc = self.directory.create(path);
let file = &self.file;
let high_mark = &mut self.high_mark;
FileEditFile { file, desc, high_mark }
}
pub fn create_file(&mut self, path: &[u8], data: &[u8], key: &Key) -> io::Result<&Descriptor> {
let mut edit_file = self.edit_file(path);
edit_file.set_content(1, data.len() as u32);
edit_file.allocate_data().write_data(data, key)?;
Ok(edit_file.desc)
}
pub fn read(&self, path: &[u8], key: &Key) -> io::Result<Vec<u8>> {
let desc = match self.find_file(path) {
Some(desc) => desc,
None => Err(io::ErrorKind::NotFound)?,
};
self.read_data(desc, key)
}
pub fn read_to_string(&self, path: &[u8], key: &Key) -> io::Result<String> {
let desc = match self.find_file(path) {
Some(desc) => desc,
None => Err(io::ErrorKind::NotFound)?,
};
let data = self.read_data(desc, key)?;
String::from_utf8(data).map_err(|_| io::ErrorKind::InvalidData.into())
}
#[inline]
pub fn read_section(&self, section: &Section, key: &Key) -> io::Result<Vec<Block>> {
read_section(&self.file, section, key)
}
#[inline]
pub fn read_data(&self, desc: &Descriptor, key: &Key) -> io::Result<Vec<u8>> {
read_data(&self.file, desc, key)
}
#[inline]
pub fn read_data_into(&self, desc: &Descriptor, key: &Key, byte_offset: usize, dest: &mut [u8]) -> io::Result<()> {
read_data_into(&self.file, desc, key, byte_offset, dest)
}
pub fn finish(self, key: &Key) -> io::Result<()> {
let FileEditor { mut file, mut directory, high_mark } = self;
let mut header = Header {
nonce: Block::default(),
mac: Block::default(),
info: InfoHeader {
version: InfoHeader::VERSION,
_unused: 0,
directory: Section {
offset: high_mark,
size: directory.len() as u32,
nonce: Block::default(),
mac: Block::default(),
},
},
};
crypt::encrypt_section(directory.as_blocks_mut(), &mut header.info.directory, key);
let mut section = Header::SECTION;
crypt::encrypt_section(header.info.as_mut(), &mut section, key);
header.nonce = section.nonce;
header.mac = section.mac;
let dir_offset = high_mark as u64 * BLOCK_SIZE as u64;
file.seek(io::SeekFrom::Start(dir_offset))?;
file.write_all(dataview::bytes(directory.as_ref()))?;
file.sync_data()?;
file.seek(io::SeekFrom::Start(0))?;
file.write_all(dataview::bytes(&header))?;
Ok(())
}
}