diff --git a/scripts/tests/run.sh b/scripts/tests/run.sh index cd6585c..32797c5 100755 --- a/scripts/tests/run.sh +++ b/scripts/tests/run.sh @@ -5,7 +5,7 @@ PASSWORD="password" run_with() { EXTRA_OPTIONS=$* - pna create src.pna -r ./src --keep-dir --keep-permission --keep-timestamp --overwrite $EXTRA_OPTIONS + pna create src.pna -r ./src --keep-dir --keep-permission --keep-timestamp --keep-xattr --overwrite $EXTRA_OPTIONS pnafs mount src.pna ./mnt/pna/src/ $EXTRA_OPTIONS & PID=$(echo $!) while [ ! -e ./mnt/pna/src/src ]; do diff --git a/src/file_manager.rs b/src/file_manager.rs index 3836730..94cff8d 100644 --- a/src/file_manager.rs +++ b/src/file_manager.rs @@ -14,6 +14,7 @@ pub type Inode = u64; pub(crate) struct LoadedEntry { data: Vec, + xattrs: HashMap>, } pub(crate) struct UnprocessedEntry { @@ -30,15 +31,22 @@ pub(crate) struct Entry(State); impl Entry { fn empty() -> Self { - Self(State::Loaded(LoadedEntry { data: Vec::new() })) + Self(State::Loaded(LoadedEntry { + data: Vec::new(), + xattrs: HashMap::new(), + })) } fn load(&mut self) { if let State::Unprocessed(e) = &self.0 { + let mut xattrs = HashMap::with_capacity(e.entry.xattrs().len()); + for xattr in e.entry.xattrs() { + xattrs.insert(xattr.name().into(), xattr.value().into()); + } let mut buf = Vec::new(); let mut reader = e.entry.reader(e.option.clone()).unwrap(); reader.read_to_end(&mut buf).unwrap(); - self.0 = State::Loaded(LoadedEntry { data: buf }); + self.0 = State::Loaded(LoadedEntry { data: buf, xattrs }); } } @@ -49,6 +57,14 @@ impl Entry { State::Unprocessed(_) => unreachable!(), } } + + pub(crate) fn xattrs(&mut self) -> &HashMap> { + self.load(); + match &self.0 { + State::Loaded(e) => &e.xattrs, + State::Unprocessed(_) => unreachable!(), + } + } } pub(crate) struct File { diff --git a/src/filesystem.rs b/src/filesystem.rs index 4750db5..49fc698 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -1,8 +1,11 @@ use crate::file_manager::FileManager; -use fuser::{Filesystem, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, Request}; +use fuser::{ + Filesystem, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyXattr, Request, +}; use libc::ENOENT; use log::info; -use std::ffi::OsStr; +use std::ffi::{CString, OsStr}; +use std::os::unix::ffi::OsStrExt; use std::path::PathBuf; use std::time::Duration; @@ -109,4 +112,55 @@ impl Filesystem for PnaFS { } reply.ok(); } + + fn getxattr( + &mut self, + _req: &Request<'_>, + ino: u64, + name: &OsStr, + size: u32, + reply: ReplyXattr, + ) { + info!( + "[Implemented] getxattr(ino: {:#x?}, name: {:?}, size: {})", + ino, name, size + ); + if let Some(file) = self.manager.get_file_mut(ino) { + if let Some(value) = file.data.xattrs().get(name) { + if size == 0 { + reply.size(value.len() as u32); + } else { + reply.data(value); + } + } else { + reply.error(ENOENT); + } + } else { + reply.error(ENOENT); + } + } + + fn listxattr(&mut self, _req: &Request<'_>, ino: u64, size: u32, reply: ReplyXattr) { + info!("[Implemented] listxattr(ino: {:#x?}, size: {})", ino, size); + if let Some(file) = self.manager.get_file_mut(ino) { + let keys = file + .data + .xattrs() + .keys() + .flat_map(|key| { + CString::new(key.as_bytes()) + .unwrap_or_default() + .as_bytes_with_nul() + .to_vec() + }) + .collect::>(); + if size == 0 { + reply.size(keys.len() as u32); + } else { + reply.data(&keys); + } + } else { + reply.error(ENOENT); + } + } }