diff options
Diffstat (limited to 'src/client/fs')
-rw-r--r-- | src/client/fs/dcache.rs | 107 | ||||
-rw-r--r-- | src/client/fs/fcache.rs | 31 | ||||
-rw-r--r-- | src/client/fs/fh.rs | 110 | ||||
-rw-r--r-- | src/client/fs/file.rs | 78 | ||||
-rw-r--r-- | src/client/fs/ino.rs | 51 |
5 files changed, 377 insertions, 0 deletions
diff --git a/src/client/fs/dcache.rs b/src/client/fs/dcache.rs new file mode 100644 index 0000000..b171abc --- /dev/null +++ b/src/client/fs/dcache.rs @@ -0,0 +1,107 @@ +use super::ino::Ino; +use std::{ + collections::{ + btree_map::{self, OccupiedError}, + BTreeMap, HashMap, + }, + ffi::{OsStr, OsString}, +}; + +#[derive(Debug)] +pub(super) struct Dcache { + inner: HashMap<Ino, Dentry>, +} + +impl Dcache { + pub fn new() -> Self { + Self { + inner: HashMap::new(), + } + } + + pub fn get_ino(&self, parent: Ino, name: &OsStr) -> Option<&Ino> { + self.get(parent).and_then(|dentry| dentry.get(name)) + } + + pub fn try_insert_name( + &mut self, + parent: Ino, + name: OsString, + ino: Ino, + ) -> Option<Result<(), ()>> { + match self + .get_mut(parent) + .map(|dentry| dentry.try_insert(name, ino)) + { + Some(Ok(_)) => Some(Ok(())), + Some(Err(_)) => Some(Err(())), + None => None, + } + } + + pub fn add_dentry(&mut self, ino: Ino, parent: Ino) -> Option<Dentry> { + self.insert(ino, Dentry::new(ino, parent)) + } + + // Map-like API + + pub fn insert(&mut self, ino: Ino, dentry: Dentry) -> Option<Dentry> { + self.inner.insert(ino, dentry) + } + + pub fn get(&self, ino: Ino) -> Option<&Dentry> { + self.inner.get(&ino) + } + + pub fn get_mut(&mut self, ino: Ino) -> Option<&mut Dentry> { + self.inner.get_mut(&ino) + } + + pub fn remove(&mut self, ino: Ino) -> Option<Dentry> { + self.inner.remove(&ino) + } +} + +#[derive(Debug)] +pub(super) struct Dentry { + inner: BTreeMap<OsString, Ino>, + pub loaded: bool, +} + +impl Dentry { + pub fn new(ino: Ino, parent: Ino) -> Self { + let mut dentry = Self { + inner: BTreeMap::new(), + loaded: false, + }; + + dentry.insert(".".into(), ino); + dentry.insert("..".into(), parent); + + dentry + } + + pub fn insert(&mut self, k: OsString, v: Ino) -> Option<Ino> { + self.inner.insert(k, v) + } + + pub fn try_insert( + &mut self, + k: OsString, + v: Ino, + ) -> Result<&mut Ino, btree_map::OccupiedError<'_, OsString, Ino>> { + self.inner.try_insert(k, v) + } + + pub fn get(&self, k: &OsStr) -> Option<&Ino> { + self.inner.get(k) + } + + pub fn remove(&mut self, k: &OsStr) -> Option<Ino> { + self.inner.remove(k) + } + + pub fn iter(&self) -> impl Iterator<Item = (&OsString, &Ino)> { + self.inner.iter() + } +} diff --git a/src/client/fs/fcache.rs b/src/client/fs/fcache.rs new file mode 100644 index 0000000..03785c0 --- /dev/null +++ b/src/client/fs/fcache.rs @@ -0,0 +1,31 @@ +use super::{file::File, ino::Ino}; +use std::collections::HashMap; + +#[derive(Debug)] +pub struct Fcache { + inner: HashMap<Ino, File>, +} + +impl Fcache { + pub fn new() -> Self { + Self { + inner: HashMap::new(), + } + } + + pub fn insert(&mut self, ino: Ino, file: File) -> Option<File> { + self.inner.insert(ino, file) + } + + pub fn get(&self, ino: Ino) -> Option<&File> { + self.inner.get(&ino) + } + + pub fn get_mut(&mut self, ino: Ino) -> Option<&mut File> { + self.inner.get_mut(&ino) + } + + pub fn remove(&mut self, ino: Ino) -> Option<File> { + self.inner.remove(&ino) + } +} diff --git a/src/client/fs/fh.rs b/src/client/fs/fh.rs new file mode 100644 index 0000000..b1e8739 --- /dev/null +++ b/src/client/fs/fh.rs @@ -0,0 +1,110 @@ +use crate::{common::temp_file, server::Stream}; +use log::error; +use std::{ + fs::{self, File}, + io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}, + ops::Index, + path::PathBuf, + slice::SliceIndex, +}; + +pub struct FileHandle { + buffer: Vec<u8>, + size: usize, + tmp: Option<(PathBuf, BufWriter<File>)>, + reader: Option<BufReader<File>>, + dirty: bool, +} + +impl FileHandle { + pub fn empty() -> std::io::Result<Self> { + let (temp_path, temp_file) = temp_file()?; + + Ok(Self { + buffer: Vec::new(), + size: 0, + tmp: Some((temp_path, BufWriter::new(temp_file))), + reader: None, + dirty: false, + }) + } + + pub fn new_rw(reader: BufReader<File>) -> std::io::Result<Self> { + // TODO: Copy read to tmp + let (temp_path, temp_file) = temp_file()?; + + Ok(Self { + buffer: Vec::new(), + size: 0, + tmp: Some((temp_path, BufWriter::new(temp_file))), + reader: Some(reader), + dirty: false, + }) + } + + pub fn new_ro(reader: BufReader<File>) -> Self { + Self { + buffer: Vec::new(), + size: 0, + tmp: None, + reader: Some(reader), + dirty: false, + } + } + + pub fn is_dirty(&self) -> bool { + self.dirty + } + + pub fn write(&mut self, data: &[u8], offset: usize) -> Option<usize> { + let Some((_, ref mut tmp)) = self.tmp else { + error!("Tried to write to a RO file handle"); + return None; + }; + + tmp.seek(SeekFrom::Start(offset as u64)) + .inspect_err(|e| error!("Seek error: {e}")) + .ok()?; + let written = tmp + .write(data) + .inspect_err(|e| error!("Write error: {e}")) + .ok()?; + + self.dirty = true; + + Some(written) + } + + pub fn read(&mut self, offset: usize, size: usize) -> Vec<u8> { + let Some(mut reader) = self.reader else { + error!("Tried to read from an empty file handle"); + return Vec::new(); + }; + + let mut buf = Vec::with_capacity(size); + + // TODO: error handling... + reader.seek(SeekFrom::Start(offset as u64)).unwrap(); + reader.read_exact(&mut buf).unwrap(); + + buf + } + + pub fn clear(&mut self) { + self.tmp.as_mut().map(|b| b.1.flush()); + self.buffer.clear(); + self.dirty = false; + } +} + +impl Drop for FileHandle { + fn drop(&mut self) { + let Some((ref temp_path, _)) = self.tmp else { + return; + }; + + if let Err(e) = fs::remove_file(temp_path) { + error!("Couldn't delete temp file {temp_path:?}: {e}"); + } + } +} diff --git a/src/client/fs/file.rs b/src/client/fs/file.rs new file mode 100644 index 0000000..5d51913 --- /dev/null +++ b/src/client/fs/file.rs @@ -0,0 +1,78 @@ +use super::ino::Ino; +use crate::server::blobref::BlobRef; +use fuser::{FileAttr, FileType}; +use std::{ + ffi::{OsStr, OsString}, + time::SystemTime, +}; + +const DEFAULT_PERMISSIONS: u16 = 0o644; + +#[derive(Debug)] +pub struct File { + // Files only have a blobref if they were written to. No blob is created if the + // files is only `touch`ed. This means empty files will disappear on `umount`. + pub blobref: Option<BlobRef>, + parent: Option<Ino>, + pub attr: FileAttr, + name: OsString, +} + +impl File { + fn new(ino: Ino, name: &OsStr, kind: FileType) -> Self { + let now = SystemTime::now(); + + let attr = FileAttr { + ino: ino.into(), + size: 0, + blocks: 0, + atime: now, + mtime: now, + ctime: now, + crtime: now, + kind, + perm: DEFAULT_PERMISSIONS, + nlink: 0, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + blksize: 0, + }; + + File { + blobref: None, + parent: None, + attr, + name: name.into(), + } + } + + pub fn new_regular_file<T: Into<OsString>>(ino: Ino, name: T) -> Self { + Self::new(ino, &name.into(), FileType::RegularFile) + } + + pub fn new_directory<T: Into<OsString>>(ino: Ino, name: T) -> Self { + Self::new(ino, &name.into(), FileType::Directory) + } + + pub fn set_parent(&mut self, ino: Ino) { + self.parent = Some(ino); + } + + pub fn name(&self) -> OsString { + self.name.clone() + } + + pub fn parent(&self) -> Option<Ino> { + self.parent + } + + pub fn ino(&self) -> Ino { + self.attr.ino.into() + } + + pub fn size(&self) -> usize { + self.attr.size as usize + } +} diff --git a/src/client/fs/ino.rs b/src/client/fs/ino.rs new file mode 100644 index 0000000..0b7628e --- /dev/null +++ b/src/client/fs/ino.rs @@ -0,0 +1,51 @@ +use std::fmt::Display; + +const ROOT_INO: u64 = 1; + +#[derive(Clone, Debug, Eq, PartialEq, Hash, Copy)] +pub struct Ino(u64); + +impl Ino { + pub fn new() -> Self { + Self(ROOT_INO) + } + + pub fn next(&mut self) -> &Self { + self.0 += 1; + self + } + + pub fn prev(&mut self) { + self.0 -= 1; + } +} + +impl From<Ino> for u64 { + fn from(ino: Ino) -> Self { + ino.0 + } +} + +impl From<u64> for Ino { + fn from(ino: u64) -> Self { + Self(ino) + } +} + +impl From<u32> for Ino { + fn from(ino: u32) -> Self { + Self(u64::from(ino)) + } +} + +impl Display for Ino { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl Default for Ino { + fn default() -> Self { + Self::new() + } +} |