Skip to content
Open
45 changes: 32 additions & 13 deletions notify/src/fsevent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

use crate::event::*;
use crate::{
unbounded, Config, Error, EventHandler, PathsMut, RecursiveMode, Result, Sender, Watcher,
unbounded, Config, Error, EventHandler, PathsMut, RecursiveMode, Result, Sender, WatchFilter,
Watcher,
};
use fsevent_sys as fs;
use fsevent_sys::core_foundation as cf;
Expand Down Expand Up @@ -68,7 +69,7 @@ pub struct FsEventWatcher {
flags: fs::FSEventStreamCreateFlags,
event_handler: Arc<Mutex<dyn EventHandler>>,
runloop: Option<(cf::CFRunLoopRef, thread::JoinHandle<()>)>,
recursive_info: HashMap<PathBuf, bool>,
recursive_info: HashMap<PathBuf, (bool, WatchFilter)>,
}

impl fmt::Debug for FsEventWatcher {
Expand Down Expand Up @@ -244,7 +245,7 @@ fn translate_flags(flags: StreamFlags, precise: bool) -> Vec<Event> {

struct StreamContextInfo {
event_handler: Arc<Mutex<dyn EventHandler>>,
recursive_info: HashMap<PathBuf, bool>,
recursive_info: HashMap<PathBuf, (bool, WatchFilter)>,
}

// Free the context when the stream created by `FSEventStreamCreate` is released.
Expand Down Expand Up @@ -276,7 +277,8 @@ impl<'a> FsEventPathsMut<'a> {
}
impl PathsMut for FsEventPathsMut<'_> {
fn add(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
self.0.append_path(path, recursive_mode)
self.0
.append_path(path, recursive_mode, WatchFilter::accept_all())
}

fn remove(&mut self, path: &Path) -> Result<()> {
Expand Down Expand Up @@ -305,9 +307,14 @@ impl FsEventWatcher {
})
}

fn watch_inner(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
fn watch_inner(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
self.stop();
let result = self.append_path(path, recursive_mode);
let result = self.append_path(path, recursive_mode, watch_filter);
// ignore return error: may be empty path list
let _ = self.run();
result
Expand Down Expand Up @@ -387,7 +394,12 @@ impl FsEventWatcher {
}

// https://github.com/thibaudgg/rb-fsevent/blob/master/ext/fsevent_watch/main.c
fn append_path(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
fn append_path(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
if !path.exists() {
return Err(Error::path_not_found().add_path(path.into()));
}
Expand All @@ -405,8 +417,10 @@ impl FsEventWatcher {
cf::CFArrayAppendValue(self.paths, cf_path);
cf::CFRelease(cf_path);
}
self.recursive_info
.insert(canonical_path, recursive_mode.is_recursive());
self.recursive_info.insert(
canonical_path,
(recursive_mode.is_recursive(), watch_filter),
);
Ok(())
}

Expand Down Expand Up @@ -549,8 +563,8 @@ unsafe fn callback_impl(
});

let mut handle_event = false;
for (p, r) in &(*info).recursive_info {
if path.starts_with(p) {
for (p, (r, filt)) in &(*info).recursive_info {
if path.starts_with(p) && filt.should_watch(p) {
if *r || &path == p {
handle_event = true;
break;
Expand Down Expand Up @@ -584,8 +598,13 @@ impl Watcher for FsEventWatcher {
Self::from_event_handler(Arc::new(Mutex::new(event_handler)))
}

fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
self.watch_inner(path, recursive_mode)
fn watch_filtered(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
self.watch_inner(path, recursive_mode, watch_filter)
}

fn paths_mut<'me>(&'me mut self) -> Box<dyn PathsMut + 'me> {
Expand Down
90 changes: 65 additions & 25 deletions notify/src/inotify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! will return events for the directory itself, and for files inside the directory.

use super::event::*;
use super::{Config, Error, ErrorKind, EventHandler, RecursiveMode, Result, Watcher};
use super::{Config, Error, ErrorKind, EventHandler, RecursiveMode, Result, WatchFilter, Watcher};
use crate::{bounded, unbounded, BoundSender, Receiver, Sender};
use inotify as inotify_sys;
use inotify_sys::{EventMask, Inotify, WatchDescriptor, WatchMask};
Expand Down Expand Up @@ -37,7 +37,7 @@ struct EventLoop {
inotify: Option<Inotify>,
event_handler: Box<dyn EventHandler>,
/// PathBuf -> (WatchDescriptor, WatchMask, is_recursive, is_dir)
watches: HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool)>,
watches: HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool, WatchFilter)>,
paths: HashMap<WatchDescriptor, PathBuf>,
rename_event: Option<Event>,
follow_links: bool,
Expand All @@ -51,7 +51,7 @@ pub struct INotifyWatcher {
}

enum EventLoopMsg {
AddWatch(PathBuf, RecursiveMode, Sender<Result<()>>),
AddWatch(PathBuf, RecursiveMode, WatchFilter, Sender<Result<()>>),
RemoveWatch(PathBuf, Sender<Result<()>>),
Shutdown,
Configure(Config, BoundSender<Result<bool>>),
Expand All @@ -61,14 +61,14 @@ enum EventLoopMsg {
fn add_watch_by_event(
path: &PathBuf,
event: &inotify_sys::Event<&OsStr>,
watches: &HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool)>,
add_watches: &mut Vec<PathBuf>,
watches: &HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool, WatchFilter)>,
add_watches: &mut Vec<(PathBuf, WatchFilter)>,
) {
if event.mask.contains(EventMask::ISDIR) {
if let Some(parent_path) = path.parent() {
if let Some(&(_, _, is_recursive, _)) = watches.get(parent_path) {
if let Some(&(_, _, is_recursive, _, ref filter)) = watches.get(parent_path) {
if is_recursive {
add_watches.push(path.to_owned());
add_watches.push((path.to_owned(), filter.clone()));
}
}
}
Expand All @@ -78,7 +78,7 @@ fn add_watch_by_event(
#[inline]
fn remove_watch_by_event(
path: &PathBuf,
watches: &HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool)>,
watches: &HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool, WatchFilter)>,
remove_watches: &mut Vec<PathBuf>,
) {
if watches.contains_key(path) {
Expand Down Expand Up @@ -168,8 +168,13 @@ impl EventLoop {
fn handle_messages(&mut self) {
while let Ok(msg) = self.event_loop_rx.try_recv() {
match msg {
EventLoopMsg::AddWatch(path, recursive_mode, tx) => {
let _ = tx.send(self.add_watch(path, recursive_mode.is_recursive(), true));
EventLoopMsg::AddWatch(path, recursive_mode, watch_filter, tx) => {
let _ = tx.send(self.add_watch(
path,
recursive_mode.is_recursive(),
true,
watch_filter,
));
}
EventLoopMsg::RemoveWatch(path, tx) => {
let _ = tx.send(self.remove_watch(path, false));
Expand Down Expand Up @@ -304,8 +309,8 @@ impl EventLoop {
}
if event.mask.contains(EventMask::DELETE_SELF) {
let remove_kind = match self.watches.get(&path) {
Some(&(_, _, _, true)) => RemoveKind::Folder,
Some(&(_, _, _, false)) => RemoveKind::File,
Some(&(_, _, _, true, _)) => RemoveKind::Folder,
Some(&(_, _, _, false, _)) => RemoveKind::File,
None => RemoveKind::Other,
};
evs.push(
Expand Down Expand Up @@ -380,8 +385,8 @@ impl EventLoop {
self.remove_watch(path, true).ok();
}

for path in add_watches {
if let Err(add_watch_error) = self.add_watch(path, true, false) {
for (path, filter) in add_watches {
if let Err(add_watch_error) = self.add_watch(path, true, false, filter) {
// The handler should be notified if we have reached the limit.
// Otherwise, the user might expect that a recursive watch
// is continuing to work correctly, but it's not.
Expand All @@ -397,19 +402,35 @@ impl EventLoop {
}
}

fn add_watch(&mut self, path: PathBuf, is_recursive: bool, mut watch_self: bool) -> Result<()> {
fn add_watch(
&mut self,
path: PathBuf,
is_recursive: bool,
mut watch_self: bool,
watch_filter: WatchFilter,
) -> Result<()> {
if !watch_filter.should_watch(&path) {
return Ok(());
}

// If the watch is not recursive, or if we determine (by stat'ing the path to get its
// metadata) that the watched path is not a directory, add a single path watch.
if !is_recursive || !metadata(&path).map_err(Error::io_watch)?.is_dir() {
return self.add_single_watch(path, false, true);
return self.add_single_watch(path, false, true, WatchFilter::accept_all());
}

for entry in WalkDir::new(path)
.follow_links(self.follow_links)
.into_iter()
.filter_map(filter_dir)
.filter(|e| watch_filter.should_watch(e.path()))
{
self.add_single_watch(entry.into_path(), is_recursive, watch_self)?;
self.add_single_watch(
entry.into_path(),
is_recursive,
watch_self,
watch_filter.clone(),
)?;
watch_self = false;
}

Expand All @@ -421,6 +442,7 @@ impl EventLoop {
path: PathBuf,
is_recursive: bool,
watch_self: bool,
watch_filter: WatchFilter,
) -> Result<()> {
let mut watchmask = WatchMask::ATTRIB
| WatchMask::CREATE
Expand All @@ -436,7 +458,7 @@ impl EventLoop {
watchmask.insert(WatchMask::MOVE_SELF);
}

if let Some(&(_, old_watchmask, _, _)) = self.watches.get(&path) {
if let Some(&(_, old_watchmask, _, _, _)) = self.watches.get(&path) {
watchmask.insert(old_watchmask);
watchmask.insert(WatchMask::MASK_ADD);
}
Expand All @@ -459,8 +481,16 @@ impl EventLoop {
Ok(w) => {
watchmask.remove(WatchMask::MASK_ADD);
let is_dir = metadata(&path).map_err(Error::io)?.is_dir();
self.watches
.insert(path.clone(), (w.clone(), watchmask, is_recursive, is_dir));
self.watches.insert(
path.clone(),
(
w.clone(),
watchmask,
is_recursive,
is_dir,
watch_filter.clone(),
),
);
self.paths.insert(w, path);
Ok(())
}
Expand All @@ -473,7 +503,7 @@ impl EventLoop {
fn remove_watch(&mut self, path: PathBuf, remove_recursive: bool) -> Result<()> {
match self.watches.remove(&path) {
None => return Err(Error::watch_not_found().add_path(path)),
Some((w, _, is_recursive, _)) => {
Some((w, _, is_recursive, _, _)) => {
if let Some(ref mut inotify) = self.inotify {
let mut inotify_watches = inotify.watches();
log::trace!("removing inotify watch: {}", path.display());
Expand Down Expand Up @@ -544,15 +574,20 @@ impl INotifyWatcher {
Ok(INotifyWatcher { channel, waker })
}

fn watch_inner(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
fn watch_inner(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
let pb = if path.is_absolute() {
path.to_owned()
} else {
let p = env::current_dir().map_err(Error::io)?;
p.join(path)
};
let (tx, rx) = unbounded();
let msg = EventLoopMsg::AddWatch(pb, recursive_mode, tx);
let msg = EventLoopMsg::AddWatch(pb, recursive_mode, watch_filter, tx);

// we expect the event loop to live and reply => unwraps must not panic
self.channel.send(msg).unwrap();
Expand Down Expand Up @@ -583,8 +618,13 @@ impl Watcher for INotifyWatcher {
Self::from_event_handler(Box::new(event_handler), config.follow_symlinks())
}

fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
self.watch_inner(path, recursive_mode)
fn watch_filtered(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
self.watch_inner(path, recursive_mode, watch_filter)
}

fn unwatch(&mut self, path: &Path) -> Result<()> {
Expand Down
Loading