use std::convert::TryInto;
#[allow(unused)]
use std::ffi::OsStr;
use std::io::IoSlice;
use std::num::TryFromIntError;
#[allow(unused)]
use std::os::unix::ffi::OsStrExt;
use smallvec::SmallVec;
use smallvec::smallvec;
use zerocopy::Immutable;
use zerocopy::IntoBytes;
use crate::INodeNo;
use crate::ll::fuse_abi as abi;
use crate::notify::PollHandle;
const INLINE_DATA_THRESHOLD: usize = size_of::<u64>() * 4;
type NotificationBuf = SmallVec<[u8; INLINE_DATA_THRESHOLD]>;
#[derive(Debug)]
pub(crate) enum Notification<'a> {
Bare(NotificationBuf),
#[allow(dead_code)]
WithData(NotificationBuf, &'a [u8]),
#[allow(unused)]
WithName(NotificationBuf, &'a [u8]),
}
impl<'a> Notification<'a> {
pub(crate) fn with_iovec<F: FnOnce(&[IoSlice<'_>]) -> T, T>(
&self,
code: abi::fuse_notify_code,
f: F,
) -> Result<T, TryFromIntError> {
let datalen = match &self {
Notification::Bare(b) => b.len(),
Notification::WithData(b, d) => b.len() + d.len(),
Notification::WithName(b, n) => b.len() + n.len() + 1, };
let header = abi::fuse_out_header {
unique: 0,
error: code as i32,
len: (size_of::<abi::fuse_out_header>() + datalen).try_into()?,
};
let mut v: SmallVec<[IoSlice<'_>; 4]> = smallvec![IoSlice::new(header.as_bytes())];
match &self {
Notification::Bare(b) => v.push(IoSlice::new(b)),
Notification::WithData(b, d) => {
v.push(IoSlice::new(b));
v.push(IoSlice::new(d));
}
Notification::WithName(b, n) => {
v.push(IoSlice::new(b));
v.push(IoSlice::new(n));
v.push(IoSlice::new(&[0u8])); }
}
Ok(f(&v))
}
pub(crate) fn new_inval_entry(
parent: INodeNo,
name: &'a OsStr,
) -> Result<Self, TryFromIntError> {
let r = abi::fuse_notify_inval_entry_out {
parent: parent.0,
namelen: name.len().try_into()?,
padding: 0,
};
Ok(Self::from_struct_with_name(&r, name.as_bytes()))
}
pub(crate) fn new_inval_inode(ino: INodeNo, offset: i64, len: i64) -> Self {
let r = abi::fuse_notify_inval_inode_out {
ino: ino.0,
off: offset,
len,
};
Self::from_struct(&r)
}
pub(crate) fn new_store(
ino: INodeNo,
offset: u64,
data: &'a [u8],
) -> Result<Self, TryFromIntError> {
let r = abi::fuse_notify_store_out {
nodeid: ino.0,
offset,
size: data.len().try_into()?,
padding: 0,
};
Ok(Self::from_struct_with_data(&r, data))
}
pub(crate) fn new_delete(
parent: INodeNo,
child: INodeNo,
name: &'a OsStr,
) -> Result<Self, TryFromIntError> {
let r = abi::fuse_notify_delete_out {
parent: parent.0,
child: child.0,
namelen: name.len().try_into()?,
padding: 0,
};
Ok(Self::from_struct_with_name(&r, name.as_bytes()))
}
pub(crate) fn new_poll(kh: PollHandle) -> Self {
let r = abi::fuse_notify_poll_wakeup_out { kh: kh.0 };
Self::from_struct(&r)
}
fn from_struct<T: IntoBytes + Immutable + ?Sized>(data: &T) -> Self {
Self::Bare(data.as_bytes().into())
}
#[allow(unused)]
fn from_struct_with_name<T: IntoBytes + Immutable + ?Sized>(buf: &T, name: &'a [u8]) -> Self {
Self::WithName(buf.as_bytes().into(), name)
}
#[allow(dead_code)]
fn from_struct_with_data<T: IntoBytes + Immutable + ?Sized>(buf: &T, data: &'a [u8]) -> Self {
Self::WithData(buf.as_bytes().into(), data)
}
}
#[cfg(test)]
mod test {
use crate::ll::notify::*;
use crate::ll::test::ioslice_to_vec;
#[test]
fn inval_entry() {
let n = Notification::new_inval_entry(INodeNo(0x42), OsStr::new("abc"))
.unwrap()
.with_iovec(
abi::fuse_notify_code::FUSE_NOTIFY_INVAL_ENTRY,
ioslice_to_vec,
)
.unwrap();
let expected = vec![
0x24, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x00,
];
assert_eq!(n, expected);
}
#[test]
fn inval_inode() {
let n = Notification::new_inval_inode(INodeNo(0x42), 100, 200)
.with_iovec(
abi::fuse_notify_code::FUSE_NOTIFY_INVAL_INODE,
ioslice_to_vec,
)
.unwrap();
let expected = vec![
0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
assert_eq!(n, expected);
}
#[test]
fn store() {
let n = Notification::new_store(INodeNo(0x42), 50, &[0xde, 0xad, 0xbe, 0xef])
.unwrap()
.with_iovec(abi::fuse_notify_code::FUSE_NOTIFY_STORE, ioslice_to_vec)
.unwrap();
let expected = vec![
0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad,
0xbe, 0xef,
];
assert_eq!(n, expected);
}
#[test]
fn delete() {
let n = Notification::new_inval_entry(INodeNo(0x42), OsStr::new("abc"))
.unwrap()
.with_iovec(abi::fuse_notify_code::FUSE_NOTIFY_DELETE, ioslice_to_vec)
.unwrap();
let expected = vec![
0x24, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x00,
];
assert_eq!(n, expected);
}
#[test]
fn poll() {
let n = Notification::new_poll(PollHandle(0x4321))
.with_iovec(abi::fuse_notify_code::FUSE_POLL, ioslice_to_vec)
.unwrap();
let expected = vec![
0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
assert_eq!(n, expected);
}
}