use snafu::{OptionExt, ResultExt, ensure};
use self::dirent::Dirent;
use crate::Pfs;
use crate::file::File;
use crate::inode::Inode;
use std::collections::BTreeMap;
use std::sync::Arc;
pub mod dirent;
#[derive(Debug, snafu::Snafu)]
#[non_exhaustive]
pub enum OpenError {
#[snafu(display("inode #{inode} is not valid"))]
InvalidInode { inode: usize },
#[snafu(display("cannot read block #{block}"))]
ReadBlock { block: u32, source: std::io::Error },
#[snafu(display("cannot read directory entry"))]
ReadDirEntry { source: dirent::ReadError },
#[snafu(display("dirent #{dirent} in block #{block} has invalid size"))]
DirentInvalidSize { block: u32, dirent: usize },
#[snafu(display("dirent #{dirent} in block #{block} has unknown type"))]
DirentUnknownType { block: u32, dirent: usize },
}
#[derive(Clone)]
#[must_use]
pub struct Directory<'a> {
pfs: Arc<Pfs<'a>>,
inode: usize,
}
impl<'a> std::fmt::Debug for Directory<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Directory")
.field("inode", &self.inode)
.field("mode", &self.mode())
.finish_non_exhaustive()
}
}
impl<'a> Directory<'a> {
pub(super) 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 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()
}
pub fn open(&self) -> Result<DirEntries<'a>, OpenError> {
let blocks = self.pfs.block_map(self.inode);
let block_size = self.pfs.block_size;
let img = self.pfs.image();
let mut items: BTreeMap<Vec<u8>, DirEntry<'a>> = BTreeMap::new();
let mut block_data = vec![0; block_size as usize];
for &block_num in blocks {
let offset = (block_num as u64) * (block_size as u64);
img.read_exact_at(offset, &mut block_data)
.context(ReadBlockSnafu { block: block_num })?;
let mut next = block_data.as_slice();
for num in 0_usize.. {
let dirent = match Dirent::read(&mut next) {
Ok(v) => v,
Err(dirent::ReadError::TooSmall | dirent::ReadError::EndOfEntry) => {
break;
}
err => err.context(ReadDirEntrySnafu)?,
};
next = next
.get(dirent.padding_size()..)
.context(DirentInvalidSizeSnafu {
block: block_num,
dirent: num,
})?;
let inode = dirent.inode();
ensure!(inode < self.pfs.inode_count(), InvalidInodeSnafu { inode });
let entry = match dirent.ty() {
Dirent::FILE => DirEntry::File(File::new(self.pfs.clone(), inode)),
Dirent::DIRECTORY => {
DirEntry::Directory(Directory::new(self.pfs.clone(), inode))
}
Dirent::SELF | Dirent::PARENT => continue,
_ => {
return Err(DirentUnknownTypeSnafu {
block: block_num,
dirent: num,
}
.build());
}
};
items.insert(dirent.name().to_vec(), entry);
}
}
Ok(DirEntries { items })
}
fn inode_ref(&self) -> &Inode {
self.pfs.inode(self.inode)
}
}
#[derive(Debug)]
#[must_use]
pub struct DirEntries<'a> {
items: BTreeMap<Vec<u8>, DirEntry<'a>>,
}
impl<'a> DirEntries<'a> {
#[must_use]
pub fn len(&self) -> usize {
self.items.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[must_use]
pub fn get(&self, name: &[u8]) -> Option<&DirEntry<'a>> {
self.items.get(name)
}
pub fn remove(&mut self, name: &[u8]) -> Option<DirEntry<'a>> {
self.items.remove(name)
}
pub fn iter(&self) -> DirEntriesIter<'_, 'a> {
DirEntriesIter {
inner: self.items.iter(),
}
}
pub fn names(&self) -> impl Iterator<Item = &[u8]> {
self.items.keys().map(|k| k.as_slice())
}
}
impl<'a> IntoIterator for DirEntries<'a> {
type Item = (Vec<u8>, DirEntry<'a>);
type IntoIter = DirEntriesOwnedIter<'a>;
fn into_iter(self) -> Self::IntoIter {
DirEntriesOwnedIter {
inner: self.items.into_iter(),
}
}
}
impl<'b, 'a> IntoIterator for &'b DirEntries<'a> {
type Item = (&'b [u8], &'b DirEntry<'a>);
type IntoIter = DirEntriesIter<'b, 'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct DirEntriesIter<'b, 'a> {
inner: std::collections::btree_map::Iter<'b, Vec<u8>, DirEntry<'a>>,
}
impl<'b, 'a> Iterator for DirEntriesIter<'b, 'a> {
type Item = (&'b [u8], &'b DirEntry<'a>);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, v)| (k.as_slice(), v))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for DirEntriesIter<'_, '_> {}
#[derive(Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct DirEntriesOwnedIter<'a> {
inner: std::collections::btree_map::IntoIter<Vec<u8>, DirEntry<'a>>,
}
impl<'a> Iterator for DirEntriesOwnedIter<'a> {
type Item = (Vec<u8>, DirEntry<'a>);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for DirEntriesOwnedIter<'_> {}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum DirEntry<'a> {
Directory(Directory<'a>),
File(File<'a>),
}