use crate::Pfs;
use crate::image::Image;
use crate::inode::Inode;
use std::cmp::min;
use std::io::{self, Error, Read, Seek, SeekFrom};
use std::sync::Arc;
#[must_use]
pub struct File<'a> {
pfs: Arc<Pfs<'a>>,
inode: usize,
}
impl<'a> std::fmt::Debug for File<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("File")
.field("inode", &self.inode)
.field("len", &self.len())
.field("mode", &self.mode())
.finish_non_exhaustive()
}
}
impl<'a> File<'a> {
pub(crate) fn new(pfs: Arc<Pfs<'a>>, inode: usize) -> Self {
Self { pfs, inode }
}
#[must_use]
pub fn mode(&self) -> u16 {
self.inode_ref().mode()
}
#[must_use]
pub fn flags(&self) -> u32 {
self.inode_ref().flags().value()
}
#[must_use]
pub fn len(&self) -> u64 {
self.inode_ref().size()
}
#[must_use]
pub fn compressed_len(&self) -> u64 {
self.inode_ref().compressed_len()
}
#[must_use]
pub fn atime(&self) -> u64 {
self.inode_ref().atime()
}
#[must_use]
pub fn mtime(&self) -> u64 {
self.inode_ref().mtime()
}
#[must_use]
pub fn ctime(&self) -> u64 {
self.inode_ref().ctime()
}
#[must_use]
pub fn birthtime(&self) -> u64 {
self.inode_ref().birthtime()
}
#[must_use]
pub fn mtimensec(&self) -> u32 {
self.inode_ref().mtimensec()
}
#[must_use]
pub fn atimensec(&self) -> u32 {
self.inode_ref().atimensec()
}
#[must_use]
pub fn ctimensec(&self) -> u32 {
self.inode_ref().ctimensec()
}
#[must_use]
pub fn birthnsec(&self) -> u32 {
self.inode_ref().birthnsec()
}
#[must_use]
pub fn uid(&self) -> u32 {
self.inode_ref().uid()
}
#[must_use]
pub fn gid(&self) -> u32 {
self.inode_ref().gid()
}
#[must_use]
pub fn is_compressed(&self) -> bool {
self.inode_ref().flags().is_compressed()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn as_slice(&self) -> Option<&'a [u8]> {
let data = self.pfs.data?;
if self.is_compressed() {
return None;
}
let inode = self.inode_ref();
if inode.size() == 0 {
return Some(&[]);
}
let (start_block, _) = inode.contiguous_blocks()?;
let block_size = self.pfs.block_size as u64;
let start = (start_block as u64) * block_size;
let end = start + inode.size();
data.get(start as usize..end as usize)
}
pub fn read_at(&self, offset: u64, buf: &mut [u8]) -> std::io::Result<usize> {
pfs_read_at(&self.pfs, self.inode, offset, buf)
}
#[must_use]
pub fn reader(&self) -> FileReader<'a> {
FileReader {
file: self.clone(),
pos: 0,
}
}
pub fn into_image(self) -> PfsFileImage<'a> {
PfsFileImage {
pfs: self.pfs,
inode: self.inode,
}
}
fn inode_ref(&self) -> &Inode {
self.pfs.inode(self.inode)
}
}
impl<'a> Clone for File<'a> {
fn clone(&self) -> Self {
Self {
pfs: self.pfs.clone(),
inode: self.inode,
}
}
}
pub struct FileReader<'a> {
file: File<'a>,
pos: u64,
}
impl<'a> std::fmt::Debug for FileReader<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FileReader")
.field("file", &self.file)
.field("pos", &self.pos)
.finish()
}
}
impl Read for FileReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let n = self.file.read_at(self.pos, buf)?;
self.pos += n as u64;
Ok(n)
}
}
impl Seek for FileReader<'_> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let file_len = self.file.len();
let new_pos = match pos {
SeekFrom::Start(offset) => offset as i64,
SeekFrom::End(offset) => file_len as i64 + offset,
SeekFrom::Current(offset) => self.pos as i64 + offset,
};
if new_pos < 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"seek to a negative position",
));
}
self.pos = new_pos as u64;
Ok(self.pos)
}
}
#[derive(Clone)]
pub struct PfsFileImage<'a> {
pfs: Arc<Pfs<'a>>,
inode: usize,
}
impl<'a> std::fmt::Debug for PfsFileImage<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PfsFileImage")
.field("inode", &self.inode)
.field("len", &self.len())
.finish_non_exhaustive()
}
}
impl Image for PfsFileImage<'_> {
fn read_at(&self, offset: u64, output_buf: &mut [u8]) -> std::io::Result<usize> {
pfs_read_at(&self.pfs, self.inode, offset, output_buf)
}
fn len(&self) -> u64 {
self.pfs.inode(self.inode).size()
}
}
fn pfs_read_at(pfs: &Pfs<'_>, inode: usize, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
let file_size = pfs.inode(inode).size();
if buf.is_empty() || offset >= file_size {
return Ok(0);
}
let block_map = pfs.block_map(inode);
let block_size = pfs.block_size as u64;
let image = pfs.image();
let mut copied = 0usize;
let mut pos = offset;
loop {
let block_index = pos / block_size;
let offset_in_block = pos % block_size;
let block_num = match block_map.get(block_index as usize) {
Some(&v) => v,
None => {
return Err(Error::other(format!(
"block #{} is not available",
block_index
)));
}
};
let block_end = (block_index + 1) * block_size;
let remaining_in_block = (min(block_end, file_size) - pos) as usize;
let to_read = min(remaining_in_block, buf.len() - copied);
let phys_offset = (block_num as u64) * block_size + offset_in_block;
image.read_exact_at(phys_offset, &mut buf[copied..copied + to_read])?;
copied += to_read;
pos += to_read as u64;
if copied == buf.len() || pos >= file_size {
break Ok(copied);
}
}
}