@@ -377,7 +377,9 @@ impl EventLoop {
377377 }
378378
379379 for path in remove_watches {
380- self . remove_watch ( path, true ) . ok ( ) ;
380+ if let Err ( err) = self . remove_watch ( path, true ) {
381+ log:: warn!( "Unable to remove the path from the watches: {err:?}" ) ;
382+ }
381383 }
382384
383385 for path in add_watches {
@@ -476,20 +478,16 @@ impl EventLoop {
476478 Some ( ( w, _, is_recursive, _) ) => {
477479 if let Some ( ref mut inotify) = self . inotify {
478480 let mut inotify_watches = inotify. watches ( ) ;
479- log:: trace!( "removing inotify watch: {}" , path . display ( ) ) ;
481+ log:: trace!( "removing inotify watch for {path:?}, remove_recursive: {remove_recursive:?}" ) ;
480482
481- inotify_watches
482- . remove ( w. clone ( ) )
483- . map_err ( |e| Error :: io ( e) . add_path ( path. clone ( ) ) ) ?;
483+ Self :: remove_single_descriptor ( & mut inotify_watches, w. clone ( ) ) ;
484484 self . paths . remove ( & w) ;
485485
486486 if is_recursive || remove_recursive {
487487 let mut remove_list = Vec :: new ( ) ;
488488 for ( w, p) in & self . paths {
489489 if p. starts_with ( & path) {
490- inotify_watches
491- . remove ( w. clone ( ) )
492- . map_err ( |e| Error :: io ( e) . add_path ( p. into ( ) ) ) ?;
490+ Self :: remove_single_descriptor ( & mut inotify_watches, w. clone ( ) ) ;
493491 self . watches . remove ( p) ;
494492 remove_list. push ( w. clone ( ) ) ;
495493 }
@@ -504,6 +502,34 @@ impl EventLoop {
504502 Ok ( ( ) )
505503 }
506504
505+ /// As long as we use the `inotify` crate its behaviour is specified by the documentation of
506+ /// a [`inotify::Watches::remove`] method:
507+ /// ```text
508+ /// Directly returns the error from the call to [inotify_rm_watch].
509+ /// Returns an [io::Error] with [ErrorKind]::InvalidInput,
510+ /// if the given WatchDescriptor did not originate from this [Inotify] instance.
511+ /// ```
512+ ///
513+ /// inotify documentation says, that `inotify_rm_watch` may fail with two specific errors:
514+ /// * EBADF - fd is not a valid file descriptor.
515+ /// * EINVAL - The watch descriptor wd is not valid or fd is not an inotify file descriptor.
516+ ///
517+ /// Therefore, we can ignore this errors (and log it), because
518+ /// * in the case, when we are removing a watch because of an caught `DELETE` or `DELETE_SELF` event we want the
519+ /// path to be not watched, and in error cases it's already done (unknown file descriptor == it is not watched)
520+ /// * in the case, when user is trying to remove the watch, they can do nothing with that kind of an error,
521+ /// it's totally internal. BUT, if there are no "strange" states (like races between user call and internal call,
522+ /// when internal inotify file descriptor has already been invalidated, but the event still hasn't been handled)
523+ /// they will get an [`ErrorKind::WatchNotFound`] error and can deal with it
524+ ///
525+ /// Log level is info, because it is not a "real" error. Expectedly, it may occurred only by race condition
526+ /// (like described above), in other cases it is a bug (but we aren't able to distinguish that states)
527+ fn remove_single_descriptor ( watches : & mut inotify:: Watches , wd : WatchDescriptor ) {
528+ if let Err ( err) = watches. remove ( wd) {
529+ log:: info!( "unable to remove watch descriptor from inotify: {err:?}" ) ;
530+ }
531+ }
532+
507533 fn remove_all_watches ( & mut self ) -> Result < ( ) > {
508534 if let Some ( ref mut inotify) = self . inotify {
509535 let mut inotify_watches = inotify. watches ( ) ;
@@ -771,6 +797,29 @@ mod tests {
771797 ) ;
772798 }
773799
800+ /// https://github.com/notify-rs/notify/issues/709
801+ #[ test]
802+ fn remove_a_subdir_in_a_recursively_watched_parent ( ) {
803+ let tmpdir = tempfile:: tempdir ( ) . expect ( "tmpdir" ) ;
804+ let subdirectory_path_1 = tmpdir. path ( ) . join ( "subdir" ) ;
805+ let subdirectory_path_2 = subdirectory_path_1. join ( "nested" ) ;
806+ std:: fs:: create_dir ( & subdirectory_path_1) . expect ( "unable to create a subdir" ) ;
807+ std:: fs:: create_dir ( & subdirectory_path_2) . expect ( "unable to create a nested dir" ) ;
808+
809+ let mut watcher =
810+ INotifyWatcher :: new ( |_| ( ) , Config :: default ( ) ) . expect ( "unable to create watcher" ) ;
811+ watcher
812+ . watch ( tmpdir. path ( ) , RecursiveMode :: Recursive )
813+ . expect ( "unable to watch" ) ;
814+ std:: fs:: remove_dir_all ( & subdirectory_path_1) . expect ( "unable to remove a subdir" ) ;
815+ let unwatch_result = watcher. unwatch ( tmpdir. path ( ) ) ;
816+
817+ assert ! (
818+ matches!( unwatch_result, Ok ( ( ) ) ) ,
819+ "error: {unwatch_result:#?}"
820+ ) ;
821+ }
822+
774823 #[ test]
775824 fn create_file ( ) {
776825 let tmpdir = testdir ( ) ;
0 commit comments