fuser/
notify.rs

1use std::io;
2
3#[allow(unused)]
4use std::{convert::TryInto, ffi::OsStr};
5
6use crate::{
7    channel::ChannelSender,
8    ll::{fuse_abi::fuse_notify_code as notify_code, notify::Notification},
9
10    // What we're sending here aren't really replies, but they
11    // move in the same direction (userspace->kernel), so we can
12    // reuse ReplySender for it.
13    reply::ReplySender,
14};
15
16/// A handle to a pending poll() request. Can be saved and used to notify the
17/// kernel when a poll is ready.
18#[derive(Clone)]
19pub struct PollHandle {
20    handle: u64,
21    notifier: Notifier,
22}
23
24impl PollHandle {
25    pub(crate) fn new(cs: ChannelSender, kh: u64) -> Self {
26        Self {
27            handle: kh,
28            notifier: Notifier::new(cs),
29        }
30    }
31
32    /// Notify the kernel that the associated file handle is ready to be polled.
33    pub fn notify(self) -> io::Result<()> {
34        self.notifier.poll(self.handle)
35    }
36}
37
38impl From<PollHandle> for u64 {
39    fn from(value: PollHandle) -> Self {
40        value.handle
41    }
42}
43
44impl std::fmt::Debug for PollHandle {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        f.debug_tuple("PollHandle").field(&self.handle).finish()
47    }
48}
49
50/// A handle by which the application can send notifications to the server
51#[derive(Debug, Clone)]
52pub struct Notifier(ChannelSender);
53
54impl Notifier {
55    pub(crate) fn new(cs: ChannelSender) -> Self {
56        Self(cs)
57    }
58
59    /// Notify poll clients of I/O readiness
60    pub fn poll(&self, kh: u64) -> io::Result<()> {
61        let notif = Notification::new_poll(kh);
62        self.send(notify_code::FUSE_POLL, &notif)
63    }
64
65    /// Invalidate the kernel cache for a given directory entry
66    pub fn inval_entry(&self, parent: u64, name: &OsStr) -> io::Result<()> {
67        let notif = Notification::new_inval_entry(parent, name).map_err(Self::too_big_err)?;
68        self.send_inval(notify_code::FUSE_NOTIFY_INVAL_ENTRY, &notif)
69    }
70
71    /// Invalidate the kernel cache for a given inode (metadata and
72    /// data in the given range)
73    pub fn inval_inode(&self, ino: u64, offset: i64, len: i64) -> io::Result<()> {
74        let notif = Notification::new_inval_inode(ino, offset, len);
75        self.send_inval(notify_code::FUSE_NOTIFY_INVAL_INODE, &notif)
76    }
77
78    /// Update the kernel's cached copy of a given inode's data
79    pub fn store(&self, ino: u64, offset: u64, data: &[u8]) -> io::Result<()> {
80        let notif = Notification::new_store(ino, offset, data).map_err(Self::too_big_err)?;
81        // Not strictly an invalidate, but the inode we're operating
82        // on may have been evicted anyway, so treat is as such
83        self.send_inval(notify_code::FUSE_NOTIFY_STORE, &notif)
84    }
85
86    /// Invalidate the kernel cache for a given directory entry and inform
87    /// inotify watchers of a file deletion.
88    pub fn delete(&self, parent: u64, child: u64, name: &OsStr) -> io::Result<()> {
89        let notif = Notification::new_delete(parent, child, name).map_err(Self::too_big_err)?;
90        self.send_inval(notify_code::FUSE_NOTIFY_DELETE, &notif)
91    }
92
93    #[allow(unused)]
94    fn send_inval(&self, code: notify_code, notification: &Notification<'_>) -> io::Result<()> {
95        match self.send(code, notification) {
96            // ENOENT is harmless for an invalidation (the
97            // kernel may have already dropped the cached
98            // entry on its own anyway), so ignore it.
99            Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()),
100            x => x,
101        }
102    }
103
104    fn send(&self, code: notify_code, notification: &Notification<'_>) -> io::Result<()> {
105        notification
106            .with_iovec(code, |iov| self.0.send(iov))
107            .map_err(Self::too_big_err)?
108    }
109
110    /// Create an error for indicating when a notification message
111    /// would exceed the capacity that its length descriptor field is
112    /// capable of encoding.
113    fn too_big_err(tfie: std::num::TryFromIntError) -> io::Error {
114        io::Error::new(io::ErrorKind::Other, format!("Data too large: {tfie:?}"))
115    }
116}