use super::protocol::{
NotifyInvalEntryOut, NotifyInvalInodeOut, OutHeader, FUSE_NOTIFY_INVAL_ENTRY,
FUSE_NOTIFY_INVAL_INODE,
};
pub trait Notifier: Send + Sync {
fn invalidate_inode(&self, nodeid: u64, off: i64, len: i64);
fn invalidate_entry(&self, parent_nodeid: u64, name: &[u8]);
}
pub fn build_inval_inode(nodeid: u64, off: i64, len: i64) -> Vec<u8> {
let total = core::mem::size_of::<OutHeader>() + core::mem::size_of::<NotifyInvalInodeOut>();
let mut buf = Vec::with_capacity(total);
let hdr = OutHeader {
len: total as u32,
error: -FUSE_NOTIFY_INVAL_INODE,
unique: 0,
};
let body = NotifyInvalInodeOut {
ino: nodeid as i64,
off,
len,
};
unsafe {
buf.extend_from_slice(std::slice::from_raw_parts(
&hdr as *const OutHeader as *const u8,
core::mem::size_of::<OutHeader>(),
));
buf.extend_from_slice(std::slice::from_raw_parts(
&body as *const NotifyInvalInodeOut as *const u8,
core::mem::size_of::<NotifyInvalInodeOut>(),
));
}
buf
}
pub fn build_inval_entry(parent_nodeid: u64, name: &[u8]) -> Vec<u8> {
let total = core::mem::size_of::<OutHeader>()
+ core::mem::size_of::<NotifyInvalEntryOut>()
+ name.len()
+ 1;
let mut buf = Vec::with_capacity(total);
let hdr = OutHeader {
len: total as u32,
error: -FUSE_NOTIFY_INVAL_ENTRY,
unique: 0,
};
let body = NotifyInvalEntryOut {
parent: parent_nodeid,
namelen: name.len() as u32, _pad: 0,
};
unsafe {
buf.extend_from_slice(std::slice::from_raw_parts(
&hdr as *const OutHeader as *const u8,
core::mem::size_of::<OutHeader>(),
));
buf.extend_from_slice(std::slice::from_raw_parts(
&body as *const NotifyInvalEntryOut as *const u8,
core::mem::size_of::<NotifyInvalEntryOut>(),
));
}
buf.extend_from_slice(name);
buf.push(0); buf
}
pub struct NullNotifier;
impl Notifier for NullNotifier {
fn invalidate_inode(&self, _nodeid: u64, _off: i64, _len: i64) {}
fn invalidate_entry(&self, _parent_nodeid: u64, _name: &[u8]) {}
}
pub struct MockNotifier {
pub calls: std::sync::Mutex<Vec<NotifyCall>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NotifyCall {
InvalInode { nodeid: u64, off: i64, len: i64 },
InvalEntry { parent: u64, name: Vec<u8> },
}
impl MockNotifier {
pub fn new() -> Self {
Self {
calls: std::sync::Mutex::new(Vec::new()),
}
}
}
impl Default for MockNotifier {
fn default() -> Self {
Self::new()
}
}
impl Notifier for MockNotifier {
fn invalidate_inode(&self, nodeid: u64, off: i64, len: i64) {
self.calls.lock().unwrap().push(NotifyCall::InvalInode { nodeid, off, len });
}
fn invalidate_entry(&self, parent_nodeid: u64, name: &[u8]) {
self.calls.lock().unwrap().push(NotifyCall::InvalEntry {
parent: parent_nodeid,
name: name.to_vec(),
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_inval_inode_layout() {
let bytes = build_inval_inode(42, 0, -1);
assert_eq!(bytes.len(), 40); let hdr: OutHeader = unsafe { core::ptr::read_unaligned(bytes.as_ptr() as *const OutHeader) };
assert_eq!(hdr.len, 40);
assert_eq!(hdr.unique, 0);
assert_eq!(hdr.error, -FUSE_NOTIFY_INVAL_INODE);
let body: NotifyInvalInodeOut = unsafe {
core::ptr::read_unaligned(
bytes.as_ptr().add(core::mem::size_of::<OutHeader>())
as *const NotifyInvalInodeOut,
)
};
assert_eq!(body.ino, 42);
assert_eq!(body.off, 0);
assert_eq!(body.len, -1);
}
#[test]
fn build_inval_entry_includes_nul_terminator() {
let bytes = build_inval_entry(1, b"foo.txt");
assert_eq!(bytes.len(), 40);
let hdr: OutHeader = unsafe { core::ptr::read_unaligned(bytes.as_ptr() as *const OutHeader) };
assert_eq!(hdr.error, -FUSE_NOTIFY_INVAL_ENTRY);
assert_eq!(hdr.unique, 0);
let body: NotifyInvalEntryOut = unsafe {
core::ptr::read_unaligned(
bytes.as_ptr().add(core::mem::size_of::<OutHeader>())
as *const NotifyInvalEntryOut,
)
};
assert_eq!(body.namelen, 7);
assert_eq!(*bytes.last().unwrap(), 0);
assert_eq!(&bytes[16 + 16..16 + 16 + 7], b"foo.txt");
}
#[test]
fn mock_notifier_records() {
let n = MockNotifier::new();
n.invalidate_inode(7, 0, -1);
n.invalidate_entry(1, b"x");
let calls = n.calls.lock().unwrap();
assert_eq!(calls.len(), 2);
assert_eq!(
calls[0],
NotifyCall::InvalInode { nodeid: 7, off: 0, len: -1 }
);
assert_eq!(
calls[1],
NotifyCall::InvalEntry { parent: 1, name: b"x".to_vec() }
);
}
}