From b557b9c4862035c1c4c3c4250432a3776ccaf26e Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Mon, 26 Aug 2024 16:10:49 -0700 Subject: [PATCH 1/2] Add config option to disable following symbolic links. --- notify/src/config.rs | 23 ++++++++++++++++++++-- notify/src/inotify.rs | 21 +++++++++++++++------ notify/src/kqueue.rs | 22 ++++++++++++++++------ notify/src/poll.rs | 44 +++++++++++++++++++++++++++++++++---------- 4 files changed, 86 insertions(+), 24 deletions(-) diff --git a/notify/src/config.rs b/notify/src/config.rs index a588eb78..178d23a0 100644 --- a/notify/src/config.rs +++ b/notify/src/config.rs @@ -37,11 +37,13 @@ impl RecursiveMode { /// Some options can be changed during runtime, others have to be set when creating the watcher backend. #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct Config { - /// See [BackendConfig::with_poll_interval] + /// See [Config::with_poll_interval] poll_interval: Option, - /// See [BackendConfig::with_compare_contents] + /// See [Config::with_compare_contents] compare_contents: bool, + + follow_symlinks: bool, } impl Config { @@ -94,6 +96,22 @@ impl Config { pub fn compare_contents(&self) -> bool { self.compare_contents } + + /// For the [INotifyWatcher](crate::INotifyWatcher), [KqueueWatcher](crate::KqueueWatcher), + /// and [PollWatcher](crate::PollWatcher). + /// + /// Determine if sybolic links should be followed when recursively watching a directory. + /// + /// This can't be changed during runtime. On by default. + pub fn with_follow_symlinks(mut self, follow_symlinks: bool) -> Self { + self.follow_symlinks = follow_symlinks; + self + } + + /// Returns current setting + pub fn follow_symlinks(&self) -> bool { + self.follow_symlinks + } } impl Default for Config { @@ -101,6 +119,7 @@ impl Default for Config { Self { poll_interval: Some(Duration::from_secs(30)), compare_contents: false, + follow_symlinks: true, } } } diff --git a/notify/src/inotify.rs b/notify/src/inotify.rs index 3c35787c..823892c1 100644 --- a/notify/src/inotify.rs +++ b/notify/src/inotify.rs @@ -40,6 +40,7 @@ struct EventLoop { watches: HashMap, paths: HashMap, rename_event: Option, + follow_links: bool, } /// Watcher implementation based on inotify @@ -90,7 +91,11 @@ fn remove_watch_by_event( } impl EventLoop { - pub fn new(inotify: Inotify, event_handler: Box) -> Result { + pub fn new( + inotify: Inotify, + event_handler: Box, + follow_links: bool, + ) -> Result { let (event_loop_tx, event_loop_rx) = unbounded::(); let poll = mio::Poll::new()?; @@ -112,6 +117,7 @@ impl EventLoop { watches: HashMap::new(), paths: HashMap::new(), rename_event: None, + follow_links, }; Ok(event_loop) } @@ -398,7 +404,7 @@ impl EventLoop { } for entry in WalkDir::new(path) - .follow_links(true) + .follow_links(self.follow_links) .into_iter() .filter_map(filter_dir) { @@ -522,9 +528,12 @@ fn filter_dir(e: walkdir::Result) -> Option) -> Result { + fn from_event_handler( + event_handler: Box, + follow_links: bool, + ) -> Result { let inotify = Inotify::init()?; - let event_loop = EventLoop::new(inotify, event_handler)?; + let event_loop = EventLoop::new(inotify, event_handler, follow_links)?; let channel = event_loop.event_loop_tx.clone(); let waker = event_loop.event_loop_waker.clone(); event_loop.run(); @@ -566,8 +575,8 @@ impl INotifyWatcher { impl Watcher for INotifyWatcher { /// Create a new watcher. - fn new(event_handler: F, _config: Config) -> Result { - Self::from_event_handler(Box::new(event_handler)) + fn new(event_handler: F, config: Config) -> Result { + Self::from_event_handler(Box::new(event_handler), config.follow_symlinks()) } fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> { diff --git a/notify/src/kqueue.rs b/notify/src/kqueue.rs index 5869b071..999c1223 100644 --- a/notify/src/kqueue.rs +++ b/notify/src/kqueue.rs @@ -34,6 +34,7 @@ struct EventLoop { kqueue: kqueue::Watcher, event_handler: Box, watches: HashMap, + follow_symlinks: bool, } /// Watcher implementation based on inotify @@ -292,7 +293,10 @@ impl EventLoop { if !is_recursive || !metadata(&path).map_err(Error::io)?.is_dir() { self.add_single_watch(path, false)?; } else { - for entry in WalkDir::new(path).follow_links(true).into_iter() { + for entry in WalkDir::new(path) + .follow_links(self.follow_symlinks) + .into_iter() + { let entry = entry.map_err(map_walkdir_error)?; self.add_single_watch(entry.path().to_path_buf(), is_recursive)?; } @@ -338,7 +342,10 @@ impl EventLoop { .map_err(|e| Error::io(e).add_path(path.clone()))?; if is_recursive || remove_recursive { - for entry in WalkDir::new(path).follow_links(true).into_iter() { + for entry in WalkDir::new(path) + .follow_links(self.follow_symlinks) + .into_iter() + { let p = entry.map_err(map_walkdir_error)?.path().to_path_buf(); self.kqueue .remove_filename(&p, EventFilter::EVFILT_VNODE) @@ -362,9 +369,12 @@ fn map_walkdir_error(e: walkdir::Error) -> Error { } impl KqueueWatcher { - fn from_event_handler(event_handler: Box) -> Result { + fn from_event_handler( + event_handler: Box, + follow_symlinks: bool, + ) -> Result { let kqueue = kqueue::Watcher::new()?; - let event_loop = EventLoop::new(kqueue, event_handler)?; + let event_loop = EventLoop::new(kqueue, event_handler, follow_symlinks)?; let channel = event_loop.event_loop_tx.clone(); let waker = event_loop.event_loop_waker.clone(); event_loop.run(); @@ -416,8 +426,8 @@ impl KqueueWatcher { impl Watcher for KqueueWatcher { /// Create a new watcher. - fn new(event_handler: F, _config: Config) -> Result { - Self::from_event_handler(Box::new(event_handler)) + fn new(event_handler: F, config: Config) -> Result { + Self::from_event_handler(Box::new(event_handler), config.follow_symlinks()) } fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> { diff --git a/notify/src/poll.rs b/notify/src/poll.rs index 9a9a9e17..b1ad76cd 100644 --- a/notify/src/poll.rs +++ b/notify/src/poll.rs @@ -127,8 +127,9 @@ mod data { &self, root: PathBuf, is_recursive: bool, + follow_symlinks: bool, ) -> Option { - WatchData::new(self, root, is_recursive) + WatchData::new(self, root, is_recursive, follow_symlinks) } /// Create [`PathData`]. @@ -151,6 +152,7 @@ mod data { // config part, won't change. root: PathBuf, is_recursive: bool, + follow_symlinks: bool, // current status part. all_path_data: HashMap, @@ -162,7 +164,12 @@ mod data { /// # Side effect /// /// This function may send event by `data_builder.emitter`. - fn new(data_builder: &DataBuilder, root: PathBuf, is_recursive: bool) -> Option { + fn new( + data_builder: &DataBuilder, + root: PathBuf, + is_recursive: bool, + follow_symlinks: bool, + ) -> Option { // If metadata read error at `root` path, it will emit // a error event and stop to create the whole `WatchData`. // @@ -186,12 +193,19 @@ mod data { return None; } - let all_path_data = - Self::scan_all_path_data(data_builder, root.clone(), is_recursive, true).collect(); + let all_path_data = Self::scan_all_path_data( + data_builder, + root.clone(), + is_recursive, + follow_symlinks, + true, + ) + .collect(); Some(Self { root, is_recursive, + follow_symlinks, all_path_data, }) } @@ -203,9 +217,13 @@ mod data { /// This function may emit event by `data_builder.emitter`. pub(super) fn rescan(&mut self, data_builder: &mut DataBuilder) { // scan current filesystem. - for (path, new_path_data) in - Self::scan_all_path_data(data_builder, self.root.clone(), self.is_recursive, false) - { + for (path, new_path_data) in Self::scan_all_path_data( + data_builder, + self.root.clone(), + self.is_recursive, + self.follow_symlinks, + false, + ) { let old_path_data = self .all_path_data .insert(path.clone(), new_path_data.clone()); @@ -247,6 +265,7 @@ mod data { data_builder: &'_ DataBuilder, root: PathBuf, is_recursive: bool, + follow_symlinks: bool, // whether this is an initial scan, used only for events is_initial: bool, ) -> impl Iterator + '_ { @@ -256,7 +275,7 @@ mod data { // // See: https://docs.rs/walkdir/2.0.1/walkdir/struct.WalkDir.html#method.new WalkDir::new(root) - .follow_links(true) + .follow_links(follow_symlinks) .max_depth(Self::dir_scan_depth(is_recursive)) .into_iter() // @@ -480,6 +499,7 @@ pub struct PollWatcher { /// currently used only for manual polling message_channel: Sender<()>, delay: Option, + follow_sylinks: bool, } impl PollWatcher { @@ -523,6 +543,7 @@ impl PollWatcher { data_builder: Arc::new(Mutex::new(data_builder)), want_to_stop: Arc::new(AtomicBool::new(false)), delay: config.poll_interval(), + follow_sylinks: config.follow_symlinks(), message_channel: tx, }; @@ -582,8 +603,11 @@ impl PollWatcher { { data_builder.update_timestamp(); - let watch_data = - data_builder.build_watch_data(path.to_path_buf(), recursive_mode.is_recursive()); + let watch_data = data_builder.build_watch_data( + path.to_path_buf(), + recursive_mode.is_recursive(), + self.follow_sylinks, + ); // if create watch_data successful, add it to watching list. if let Some(watch_data) = watch_data { From e14dd51eaac415c9a8a60087c68e20785a180673 Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Tue, 27 Aug 2024 08:58:09 -0700 Subject: [PATCH 2/2] Fix mac and bsd compilation. --- notify/src/kqueue.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/notify/src/kqueue.rs b/notify/src/kqueue.rs index 999c1223..db6ff12e 100644 --- a/notify/src/kqueue.rs +++ b/notify/src/kqueue.rs @@ -51,7 +51,11 @@ enum EventLoopMsg { } impl EventLoop { - pub fn new(kqueue: kqueue::Watcher, event_handler: Box) -> Result { + pub fn new( + kqueue: kqueue::Watcher, + event_handler: Box, + follow_symlinks: bool, + ) -> Result { let (event_loop_tx, event_loop_rx) = unbounded::(); let poll = mio::Poll::new()?; @@ -71,6 +75,7 @@ impl EventLoop { kqueue, event_handler, watches: HashMap::new(), + follow_symlinks, }; Ok(event_loop) }