use std::os::fd::AsRawFd;
use std::path::Path;
use fanotify_fid::prelude::*;
use crate::fid_parser::{FsGroup, mark_directory, path_mask_from_options};
use crate::filters::PathOptions;
use super::Monitor;
impl Monitor {
pub(crate) fn add_temp_parent_mark(&mut self, target_path: &Path) -> bool {
let parent = match Self::nearest_existing_ancestor(target_path) {
Some(p) => p,
None => return false,
};
if parent == *target_path {
return false;
}
let canonical = parent.canonicalize().unwrap_or_else(|_| parent.clone());
let saved_entries: Vec<_> = self
.monitored_entries
.iter()
.filter(|(p, _)| p == target_path)
.cloned()
.collect();
let pending_opts: Vec<PathOptions> = self
.inotify_state
.pending_paths
.iter()
.filter(|(p, _)| p == target_path)
.filter_map(|(_, entry)| PathOptions::try_from(entry).ok())
.collect();
if saved_entries.is_empty() && pending_opts.is_empty() {
return false;
}
let path_mask: u64 = saved_entries
.iter()
.map(|(_, o)| path_mask_from_options(o))
.chain(pending_opts.iter().map(path_mask_from_options))
.fold(0, |a, b| a | b);
if path_mask == 0 {
return false;
}
let dev_id = std::fs::metadata(&canonical)
.ok()
.map(|m| std::os::linux::fs::MetadataExt::st_dev(&m))
.unwrap_or(0);
let group_key = if let Some((key, _)) = self
.fanotify
.groups
.iter()
.find(|(_, g)| g.dev_id == dev_id)
{
let fan_fd = &self.fanotify.groups[key].fan_fd;
if mark_directory(fan_fd, path_mask, &canonical).is_err() {
return false;
}
self.fanotify.groups[key].ref_count += 1;
key
} else {
use fanotify_fid::consts::{
FAN_CLASS_NOTIF, FAN_CLOEXEC, FAN_NONBLOCK, FAN_REPORT_DIR_FID, FAN_REPORT_FID,
FAN_REPORT_NAME,
};
let new_fd = match fanotify_fid::prelude::fanotify_init(
FAN_CLOEXEC
| FAN_NONBLOCK
| FAN_CLASS_NOTIF
| FAN_REPORT_FID
| FAN_REPORT_DIR_FID
| FAN_REPORT_NAME,
(libc::O_CLOEXEC | libc::O_RDONLY) as u32,
) {
Ok(fd) => fd,
Err(_) => return false,
};
if mark_directory(&new_fd, path_mask, &canonical).is_err() {
drop(new_fd);
return false;
}
let mount_fd = match Self::open_dir(&canonical) {
Ok(fd) => fd,
Err(_) => {
drop(new_fd);
return false;
}
};
let key = self.fanotify.groups.insert(FsGroup {
dev_id,
fan_fd: new_fd,
mount_fd,
ref_count: 1,
});
self.spawn_fd_reader(key);
key
};
debug_log!(
self.debug,
"temp parent mark: {} ← watching for {}",
canonical.display(),
target_path.display()
);
self.inotify_state
.temp_parent_marks
.insert(target_path.to_path_buf(), (parent, group_key));
true
}
pub(crate) fn cleanup_temp_parent_marks(&mut self) {
let to_remove: Vec<_> = self
.inotify_state
.temp_parent_marks
.keys()
.filter(|target| self.paths.contains(target))
.cloned()
.collect();
for target in to_remove {
self.remove_temp_parent_mark(&target);
}
}
fn remove_temp_parent_mark(&mut self, target_path: &Path) {
let Some((parent, key)) = self.inotify_state.temp_parent_marks.remove(target_path) else {
return;
};
let canonical = parent.canonicalize().unwrap_or_else(|_| parent.clone());
if let Some(group) = self.fanotify.groups.get(key) {
let fan_fd_raw = group.fan_fd.as_raw_fd();
let _ = fanotify_mark(
&group.fan_fd,
fanotify_fid::consts::FAN_MARK_REMOVE | fanotify_fid::consts::FAN_MARK_FILESYSTEM,
0,
fanotify_fid::consts::AT_FDCWD,
&canonical,
);
let _ = fanotify_mark(
&group.fan_fd,
fanotify_fid::consts::FAN_MARK_REMOVE,
0,
fanotify_fid::consts::AT_FDCWD,
&canonical,
);
self.fanotify.groups[key].ref_count =
self.fanotify.groups[key].ref_count.saturating_sub(1);
if self.fanotify.groups[key].ref_count == 0 {
debug_log!(
self.debug,
"temp parent mark removed, freeing FsGroup (fd {})",
fan_fd_raw
);
self.fanotify.groups.remove(key);
}
}
}
}