diff --git a/.gitignore b/.gitignore index bf3bba40..0f341b9e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ tests/last-fails tests/last-run.log .cargo +.idea/ +*.iml diff --git a/notify/src/config.rs b/notify/src/config.rs index a588eb78..e64f2df6 100644 --- a/notify/src/config.rs +++ b/notify/src/config.rs @@ -42,6 +42,9 @@ pub struct Config { /// See [BackendConfig::with_compare_contents] compare_contents: bool, + + /// See [BackendConfig::with_fail_on_no_permissions] + fail_on_no_permissions: bool, } impl Config { @@ -94,6 +97,21 @@ impl Config { pub fn compare_contents(&self) -> bool { self.compare_contents } + + /// For the [INotifyWatcher](crate::INotifyWatcher) backend. + /// + /// This flag sets whether inotify should fail when the user has no permissions on a subfolder. + /// + /// On by default. + pub fn with_fail_on_no_permissions(mut self, fail_on_no_permissions: bool) -> Self { + self.fail_on_no_permissions = fail_on_no_permissions; + self + } + + /// Returns current setting + pub fn fail_on_no_permissions(&self) -> bool { + self.fail_on_no_permissions + } } impl Default for Config { @@ -101,6 +119,7 @@ impl Default for Config { Self { poll_interval: Some(Duration::from_secs(30)), compare_contents: false, + fail_on_no_permissions: true, } } } diff --git a/notify/src/inotify.rs b/notify/src/inotify.rs index d8d81401..8c30774b 100644 --- a/notify/src/inotify.rs +++ b/notify/src/inotify.rs @@ -10,13 +10,13 @@ use crate::{bounded, unbounded, BoundSender, Receiver, Sender}; use inotify as inotify_sys; use inotify_sys::{EventMask, Inotify, WatchDescriptor, WatchMask}; use std::collections::HashMap; -use std::env; use std::ffi::OsStr; use std::fs::metadata; use std::os::unix::io::AsRawFd; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::thread; +use std::{env, io}; use walkdir::WalkDir; const INOTIFY: mio::Token = mio::Token(0); @@ -40,6 +40,7 @@ struct EventLoop { watches: HashMap, paths: HashMap, rename_event: Option, + fail_on_no_permissions: 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, + fail_on_no_permissions: 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, + fail_on_no_permissions, }; Ok(event_loop) } @@ -307,9 +313,11 @@ impl EventLoop { } } None => { - log::trace!("No patch for DELETE_SELF event, may be a bug?"); + log::trace!( + "No patch for DELETE_SELF event, may be a bug?" + ); RemoveKind::Other - }, + } }; evs.push( Event::new(EventKind::Remove(remove_kind)) @@ -396,7 +404,20 @@ impl EventLoop { .into_iter() .filter_map(filter_dir) { - self.add_single_watch(entry.path().to_path_buf(), is_recursive, watch_self)?; + if let Err(e) = + self.add_single_watch(entry.path().to_path_buf(), is_recursive, watch_self) + { + match &e.kind { + ErrorKind::Io(io) if io.kind() == io::ErrorKind::PermissionDenied => { + // If this is the root folder we want to fail on permission error even if + // fail_on_permissions_for_subfolders flag is not set + if watch_self || self.fail_on_no_permissions { + return Err(e); + } + } + _ => return Err(e), + } + } watch_self = false; } @@ -514,9 +535,12 @@ fn filter_dir(e: walkdir::Result) -> Option) -> Result { + fn from_event_handler( + event_handler: Box, + fail_on_no_permissions: bool, + ) -> Result { let inotify = Inotify::init()?; - let event_loop = EventLoop::new(inotify, event_handler)?; + let event_loop = EventLoop::new(inotify, event_handler, fail_on_no_permissions)?; let channel = event_loop.event_loop_tx.clone(); let waker = event_loop.event_loop_waker.clone(); event_loop.run(); @@ -558,8 +582,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.fail_on_no_permissions()) } fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> { diff --git a/notify/src/windows.rs b/notify/src/windows.rs index 65c85147..1e2d2884 100644 --- a/notify/src/windows.rs +++ b/notify/src/windows.rs @@ -282,8 +282,8 @@ fn start_read(rd: &ReadData, event_handler: Arc>, handle // for our own purposes let req_buf = request.buffer.as_mut_ptr() as *mut c_void; - let request_p = Box::into_raw(request) as isize; - overlapped.hEvent = request_p; + let request_p = Box::into_raw(request); + overlapped.hEvent = request_p as isize; // This is using an asynchronous call with a completion routine for receiving notifications // An I/O completion port would probably be more performant @@ -304,7 +304,7 @@ fn start_read(rd: &ReadData, event_handler: Arc>, handle // over to `ReadDirectoryChangesW`. // So we can claim ownership back. let _overlapped_alloc = std::mem::ManuallyDrop::into_inner(overlapped); - let request: Box = mem::transmute(request_p); + let request: Box = Box::from_raw(request_p); ReleaseSemaphore(request.data.complete_sem, 1, ptr::null_mut()); } }