1use super::event::*;
8use super::{
9 Config, Error, ErrorKind, EventHandler, RecursiveMode, Result, WatchPathConfig, Watcher,
10};
11use crate::paths::{
12 WalkRoots, WatchMetadata, WatchPath, absolute_path, is_preserved_watch_root,
13 preserved_watch_mode, preserved_watch_roots, recursive_user_watch_ancestor, reported_path,
14};
15use crate::{BoundSender, Receiver, Sender, bounded, unbounded};
16use inotify as inotify_sys;
17use inotify_sys::{EventMask, Inotify, WatchDescriptor, WatchMask};
18use notify_types::event::EventKindMask;
19use std::collections::{BTreeMap, HashMap, HashSet};
20use std::ffi::OsStr;
21use std::fs::{Metadata, metadata, read_dir, symlink_metadata};
22use std::os::unix::fs::MetadataExt;
23use std::os::unix::io::AsRawFd;
24use std::path::{Path, PathBuf};
25use std::sync::Arc;
26use std::thread;
27
28const INOTIFY: mio::Token = mio::Token(0);
29const MESSAGE: mio::Token = mio::Token(1);
30
31const RESOLUTION_FLAGS: WatchMask = WatchMask::DONT_FOLLOW
34 .union(WatchMask::MASK_ADD)
35 .union(WatchMask::MASK_CREATE)
36 .union(WatchMask::ONLYDIR);
37
38fn event_kind_mask_to_watch_mask(mask: EventKindMask, is_recursive: bool) -> WatchMask {
43 let mut watch_mask = WatchMask::empty();
44
45 if is_recursive {
46 watch_mask |= WatchMask::CREATE | WatchMask::MOVED_TO;
47 }
48
49 if mask.intersects(EventKindMask::CREATE) {
50 watch_mask |= WatchMask::CREATE | WatchMask::MOVED_TO;
51 }
52
53 if mask.intersects(EventKindMask::REMOVE) {
54 watch_mask |= WatchMask::DELETE | WatchMask::MOVED_FROM;
55 }
56
57 if mask.intersects(EventKindMask::MODIFY_DATA) {
58 watch_mask |= WatchMask::MODIFY;
62 }
63
64 if mask.intersects(EventKindMask::MODIFY_META) {
65 watch_mask |= WatchMask::ATTRIB;
66 }
67
68 if mask.intersects(EventKindMask::MODIFY_NAME) {
69 watch_mask |= WatchMask::MOVE_SELF;
70 }
71
72 if mask.intersects(EventKindMask::ACCESS_OPEN) {
73 watch_mask |= WatchMask::OPEN;
74 }
75
76 if mask.intersects(EventKindMask::ACCESS_CLOSE) {
77 watch_mask |= WatchMask::CLOSE_WRITE;
78 }
79
80 if mask.intersects(EventKindMask::ACCESS_CLOSE_NOWRITE) {
81 watch_mask |= WatchMask::CLOSE_NOWRITE;
82 }
83
84 watch_mask
85}
86
87struct EventLoop {
94 running: bool,
95 poll: mio::Poll,
96 event_loop_waker: Arc<mio::Waker>,
97 event_loop_tx: Sender<EventLoopMsg>,
98 event_loop_rx: Receiver<EventLoopMsg>,
99 inotify: Option<Inotify>,
100 event_handler: Box<dyn EventHandler>,
101 watches: HashMap<PathBuf, Watch>,
103 paths: HashMap<WatchDescriptor, PathBuf>,
104 rename_event: Option<Event>,
105 follow_links: bool,
106 event_kind_mask: EventKindMask,
107}
108
109struct Watch {
110 watch_descriptor: WatchDescriptor,
111 watch_mask: WatchMask,
112 is_dir: bool,
113 dereference: bool,
114 metadata: WatchMetadata,
115}
116
117#[derive(Debug)]
119pub struct INotifyWatcher {
120 channel: Sender<EventLoopMsg>,
121 waker: Arc<mio::Waker>,
122}
123
124enum EventLoopMsg {
125 AddWatch(WatchPath, WatchPathConfig, Sender<Result<()>>),
126 RemoveWatch(PathBuf, Sender<Result<()>>),
127 GetWatchedPaths(Sender<Vec<(PathBuf, RecursiveMode)>>),
128 Shutdown,
129 Configure(Config, BoundSender<Result<bool>>),
130}
131
132#[inline]
133fn watch_metadata(path: &Path, dereference: bool) -> std::io::Result<Metadata> {
134 if dereference {
135 metadata(path)
136 } else {
137 symlink_metadata(path)
138 }
139}
140
141#[inline]
142fn add_watch_by_event(
143 path: &PathBuf,
144 event: &inotify_sys::Event<&OsStr>,
145 watches: &HashMap<PathBuf, Watch>,
146 add_watches: &mut Vec<WatchPath>,
147) {
148 if event.mask.contains(EventMask::ISDIR) {
149 if let Some(parent_path) = path.parent() {
150 if let Some(watch) = watches.get(parent_path) {
151 if watch.metadata.is_recursive {
152 add_watches.push(WatchPath::from_parts(
153 path.to_owned(),
154 reported_path(parent_path, &watch.metadata.reported_path, path),
155 ));
156 }
157 }
158 }
159 }
160}
161
162#[inline]
164fn queue_watch_removal(
165 path: &Path,
166 descriptor: Option<&WatchDescriptor>,
167 watches: &HashMap<PathBuf, Watch>,
168 remove_watches: &mut BTreeMap<PathBuf, WatchRemoval>,
169 removal: WatchRemoval,
170) {
171 let watched = watches
172 .get(path)
173 .is_some_and(|watch| descriptor.is_none_or(|wd| &watch.watch_descriptor == wd));
174 if watched {
175 let pending = remove_watches.entry(path.to_owned()).or_insert(removal);
176 *pending = (*pending).max(removal);
177 }
178}
179
180#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
182enum WatchRemoval {
183 WithOsCall,
185 DescriptorAlreadyRemoved,
188}
189
190#[inline]
191fn unmount_event(path: PathBuf) -> Event {
192 Event::new(EventKind::Remove(RemoveKind::Other))
193 .add_path(path)
194 .set_info("unmount")
195}
196
197impl EventLoop {
198 pub fn new(
199 inotify: Inotify,
200 event_handler: Box<dyn EventHandler>,
201 config: &Config,
202 ) -> Result<Self> {
203 let (event_loop_tx, event_loop_rx) = unbounded::<EventLoopMsg>();
204 let poll = mio::Poll::new()?;
205
206 let event_loop_waker = Arc::new(mio::Waker::new(poll.registry(), MESSAGE)?);
207
208 let inotify_fd = inotify.as_raw_fd();
209 let mut evented_inotify = mio::unix::SourceFd(&inotify_fd);
210 poll.registry()
211 .register(&mut evented_inotify, INOTIFY, mio::Interest::READABLE)?;
212
213 let event_loop = EventLoop {
214 running: true,
215 poll,
216 event_loop_waker,
217 event_loop_tx,
218 event_loop_rx,
219 inotify: Some(inotify),
220 event_handler,
221 watches: HashMap::new(),
222 paths: HashMap::new(),
223 rename_event: None,
224 follow_links: config.follow_symlinks(),
225 event_kind_mask: config.event_kinds(),
226 };
227 Ok(event_loop)
228 }
229
230 pub fn run(self) {
232 let _ = thread::Builder::new()
233 .name("notify-rs inotify loop".to_string())
234 .spawn(|| self.event_loop_thread());
235 }
236
237 fn event_loop_thread(mut self) {
238 let mut events = mio::Events::with_capacity(16);
239 loop {
240 match self.poll.poll(&mut events, None) {
242 Err(ref e) if matches!(e.kind(), std::io::ErrorKind::Interrupted) => {
243 }
246 Err(e) => panic!("poll failed: {e}"),
247 Ok(()) => {}
248 }
249
250 for event in &events {
252 self.handle_event(event);
253 }
254
255 if !self.running {
257 break;
258 }
259 }
260 }
261
262 fn handle_event(&mut self, event: &mio::event::Event) {
264 match event.token() {
265 MESSAGE => {
266 self.handle_messages()
268 }
269 INOTIFY => {
270 self.handle_inotify()
272 }
273 _ => unreachable!(),
274 }
275 }
276
277 fn handle_messages(&mut self) {
278 while let Ok(msg) = self.event_loop_rx.try_recv() {
279 match msg {
280 EventLoopMsg::AddWatch(path, config, tx) => {
281 let _ = tx.send(self.add_watch(path, config, true));
282 }
283 EventLoopMsg::RemoveWatch(path, tx) => {
284 let _ = tx.send(self.remove_watch(path, false));
285 }
286 EventLoopMsg::GetWatchedPaths(tx) => {
287 let _ = tx.send(
288 self.watches
289 .iter()
290 .filter(|(_path, watch)| watch.metadata.is_user_watch)
291 .map(|(_path, watch)| {
292 (
293 watch.metadata.reported_path.clone(),
294 if watch.metadata.user_is_recursive {
295 RecursiveMode::Recursive
296 } else {
297 RecursiveMode::NonRecursive
298 },
299 )
300 })
301 .collect(),
302 );
303 }
304 EventLoopMsg::Shutdown => {
305 let _ = self.remove_all_watches();
306 if let Some(inotify) = self.inotify.take() {
307 let _ = inotify.close();
308 }
309 self.running = false;
310 break;
311 }
312 EventLoopMsg::Configure(config, tx) => {
313 self.configure_raw_mode(config, tx);
314 }
315 }
316 }
317 }
318
319 fn configure_raw_mode(&mut self, _config: Config, tx: BoundSender<Result<bool>>) {
320 tx.send(Ok(false))
321 .expect("configuration channel disconnected");
322 }
323
324 fn handle_inotify(&mut self) {
325 let mut add_watches = Vec::new();
326 let mut remove_watches = BTreeMap::new();
327
328 if let Some(ref mut inotify) = self.inotify {
329 let mut buffer = [0; 1024];
330 loop {
332 match inotify.read_events(&mut buffer) {
333 Ok(events) => {
334 let mut num_events = 0;
335 for event in events {
336 log::trace!("inotify event: {event:?}");
337
338 num_events += 1;
339 if event.mask.contains(EventMask::Q_OVERFLOW) {
340 let ev = Ok(Event::new(EventKind::Other).set_flag(Flag::Rescan));
341 self.event_handler.handle_event(ev);
342 }
343
344 if event.mask.contains(EventMask::IGNORED) {
345 if let Some(path) = self.paths.get(&event.wd).cloned() {
349 if self
350 .watches
351 .get(&path)
352 .is_some_and(|watch| watch.watch_descriptor == event.wd)
353 {
354 queue_watch_removal(
355 &path,
356 None,
357 &self.watches,
358 &mut remove_watches,
359 WatchRemoval::DescriptorAlreadyRemoved,
360 );
361 } else {
362 self.paths.remove(&event.wd);
363 }
364 }
365 continue;
366 }
367
368 let paths = self.paths.get(&event.wd).and_then(|root| {
369 self.watches.get(root).map(|watch| match event.name {
370 Some(name) => {
371 let path = root.join(name);
372 let reported_path = reported_path(
373 root,
374 &watch.metadata.reported_path,
375 &path,
376 );
377 (path, reported_path)
378 }
379 None => (root.clone(), watch.metadata.reported_path.clone()),
380 })
381 });
382
383 let (path, event_path) = match paths {
384 Some(paths) => paths,
385 None => {
386 log::debug!("inotify event with unknown descriptor: {event:?}");
387 continue;
388 }
389 };
390
391 let mut evs = Vec::new();
392
393 if event.mask.contains(EventMask::MOVED_FROM) {
394 queue_watch_removal(
395 &path,
396 None,
397 &self.watches,
398 &mut remove_watches,
399 WatchRemoval::WithOsCall,
400 );
401
402 let event = Event::new(EventKind::Modify(ModifyKind::Name(
403 RenameMode::From,
404 )))
405 .add_path(event_path.clone())
406 .set_tracker(event.cookie as usize);
407
408 self.rename_event = Some(event.clone());
409
410 evs.push(event);
411 } else if event.mask.contains(EventMask::MOVED_TO) {
412 evs.push(
413 Event::new(EventKind::Modify(ModifyKind::Name(RenameMode::To)))
414 .set_tracker(event.cookie as usize)
415 .add_path(event_path.clone()),
416 );
417
418 let trackers_match =
419 self.rename_event.as_ref().and_then(|e| e.tracker())
420 == Some(event.cookie as usize);
421
422 if trackers_match {
423 let rename_event = self.rename_event.take().unwrap(); evs.push(
425 Event::new(EventKind::Modify(ModifyKind::Name(
426 RenameMode::Both,
427 )))
428 .set_tracker(event.cookie as usize)
429 .add_some_path(rename_event.paths.first().cloned())
430 .add_path(event_path.clone()),
431 );
432 }
433 add_watch_by_event(&path, &event, &self.watches, &mut add_watches);
434 }
435 if event.mask.contains(EventMask::MOVE_SELF) {
436 evs.push(
437 Event::new(EventKind::Modify(ModifyKind::Name(
438 RenameMode::From,
439 )))
440 .add_path(event_path.clone()),
441 );
442 }
446 if event.mask.contains(EventMask::CREATE) {
447 evs.push(
448 Event::new(EventKind::Create(
449 if event.mask.contains(EventMask::ISDIR) {
450 CreateKind::Folder
451 } else {
452 CreateKind::File
453 },
454 ))
455 .add_path(event_path.clone()),
456 );
457 add_watch_by_event(&path, &event, &self.watches, &mut add_watches);
458 }
459 if event.mask.contains(EventMask::DELETE) {
460 evs.push(
461 Event::new(EventKind::Remove(
462 if event.mask.contains(EventMask::ISDIR) {
463 RemoveKind::Folder
464 } else {
465 RemoveKind::File
466 },
467 ))
468 .add_path(event_path.clone()),
469 );
470 queue_watch_removal(
471 &path,
472 None,
473 &self.watches,
474 &mut remove_watches,
475 WatchRemoval::WithOsCall,
476 );
477 }
478 if event.mask.contains(EventMask::DELETE_SELF) {
479 let remove_kind = match self.watches.get(&path) {
480 Some(watch) if watch.is_dir => RemoveKind::Folder,
481 Some(_) => RemoveKind::File,
482 None => RemoveKind::Other,
483 };
484 evs.push(
485 Event::new(EventKind::Remove(remove_kind))
486 .add_path(event_path.clone()),
487 );
488 queue_watch_removal(
491 &path,
492 Some(&event.wd),
493 &self.watches,
494 &mut remove_watches,
495 WatchRemoval::DescriptorAlreadyRemoved,
496 );
497 }
498 if event.mask.contains(EventMask::UNMOUNT) {
499 evs.push(unmount_event(event_path.clone()));
500 queue_watch_removal(
504 &path,
505 Some(&event.wd),
506 &self.watches,
507 &mut remove_watches,
508 WatchRemoval::DescriptorAlreadyRemoved,
509 );
510 }
511 if event.mask.contains(EventMask::MODIFY) {
512 evs.push(
513 Event::new(EventKind::Modify(ModifyKind::Data(
514 DataChange::Any,
515 )))
516 .add_path(event_path.clone()),
517 );
518 }
519 if event.mask.contains(EventMask::CLOSE_WRITE) {
520 evs.push(
521 Event::new(EventKind::Access(AccessKind::Close(
522 AccessMode::Write,
523 )))
524 .add_path(event_path.clone()),
525 );
526 }
527 if event.mask.contains(EventMask::CLOSE_NOWRITE) {
528 evs.push(
529 Event::new(EventKind::Access(AccessKind::Close(
530 AccessMode::Read,
531 )))
532 .add_path(event_path.clone()),
533 );
534 }
535 if event.mask.contains(EventMask::ATTRIB) {
536 evs.push(
537 Event::new(EventKind::Modify(ModifyKind::Metadata(
538 MetadataKind::Any,
539 )))
540 .add_path(event_path.clone()),
541 );
542 }
543 if event.mask.contains(EventMask::OPEN) {
544 evs.push(
545 Event::new(EventKind::Access(AccessKind::Open(
546 AccessMode::Any,
547 )))
548 .add_path(event_path.clone()),
549 );
550 }
551
552 for ev in evs {
554 if self.event_kind_mask.matches(&ev.kind) {
555 self.event_handler.handle_event(Ok(ev));
556 }
557 }
558 }
559
560 if num_events == 0 {
562 break;
563 }
564 }
565 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
566 break;
568 }
569 Err(e) => {
570 self.event_handler.handle_event(Err(Error::io(e)));
571 }
572 }
573 }
574 }
575
576 for (path, removal) in remove_watches.into_iter().rev() {
580 if !self.watches.contains_key(&path) {
581 continue;
582 }
583
584 let result = match removal {
585 WatchRemoval::WithOsCall => self.remove_watch(path, true),
586 WatchRemoval::DescriptorAlreadyRemoved => {
587 self.remove_watch_without_root_os_call(path, true)
588 }
589 };
590 if let Err(err) = result {
591 log::warn!("Unable to remove the path from the watches: {err:?}");
592 }
593 }
594
595 for path in add_watches {
596 let config = WatchPathConfig::new(RecursiveMode::Recursive);
597 if let Err(err) = self.add_watch(path, config, false) {
598 match WalkFailure::of(&err.kind) {
599 WalkFailure::Skip => {}
600 WalkFailure::Report => self.event_handler.handle_event(Err(err)),
601 WalkFailure::Abort => {
602 self.event_handler.handle_event(Err(err));
603 break;
604 }
605 }
606 }
607 }
608 }
609
610 fn add_watch(
611 &mut self,
612 path: WatchPath,
613 config: WatchPathConfig,
614 watch_self: bool,
615 ) -> Result<()> {
616 let is_recursive = config.recursive_mode().is_recursive();
617 let dereference = config.dereference_symlinks() || is_recursive;
619 let path_is_dir = watch_metadata(&path.absolute, dereference)
620 .map_err(Error::io_watch)?
621 .is_dir();
622 let requested_is_recursive = is_recursive && path_is_dir;
623 if watch_self {
624 if let Some(watch) = self
625 .watches
626 .get(&path.absolute)
627 .filter(|watch| watch.metadata.is_user_watch)
628 {
629 if watch.metadata.user_is_recursive == requested_is_recursive
630 && watch.metadata.reported_path == path.requested
631 && watch.dereference == dereference
632 {
633 return Ok(());
634 }
635
636 let inherited_recursive_root =
641 if !requested_is_recursive && path_is_dir && watch.metadata.is_recursive {
642 recursive_user_watch_ancestor(
643 &path.absolute,
644 self.watches
645 .iter()
646 .map(|(path, watch)| (path, &watch.metadata)),
647 )
648 } else {
649 None
650 };
651 let replaced_path = path.absolute.clone();
652 self.remove_watch(replaced_path.clone(), false)?;
653
654 if let Some((ancestor_path, ancestor_reported_path)) = inherited_recursive_root {
655 let barriers = self.recursive_walk_barriers();
659 let root = WatchPath::from_parts(ancestor_path, ancestor_reported_path);
660 let entries = walk_dirs(
661 replaced_path.clone(),
662 root.clone(),
663 self.follow_links,
664 &barriers,
665 );
666 self.add_watches_for_paths(root, entries, true, true, false)?;
667 }
668 } else if self.watches.get(&path.absolute).is_some_and(|watch| {
669 !dereference && !path_is_dir && watch.is_dir && watch.metadata.is_recursive
670 }) {
671 self.remove_watch(path.absolute.clone(), false)?;
674 }
675 }
676
677 if !requested_is_recursive {
680 return self.add_single_watch(path, false, dereference, true);
681 }
682
683 let barriers = self.recursive_walk_barriers();
684 let entries = walk_dirs(
685 path.absolute.clone(),
686 path.clone(),
687 self.follow_links,
688 &barriers,
689 );
690
691 self.add_watches_for_paths(path, entries, is_recursive, dereference, watch_self)
692 }
693
694 fn recursive_walk_barriers(&self) -> HashSet<PathBuf> {
695 self.watches
696 .iter()
697 .filter(|(_, watch)| {
698 watch.metadata.is_user_watch && !watch.dereference && !watch.is_dir
699 })
700 .map(|(path, _)| path.clone())
701 .collect()
702 }
703
704 fn add_watches_for_paths<I>(
705 &mut self,
706 root: WatchPath,
707 paths: I,
708 is_recursive: bool,
709 dereference: bool,
710 mut watch_self: bool,
711 ) -> Result<()>
712 where
713 I: IntoIterator<Item = Result<WatchPath>>,
714 {
715 let mut last_failed = None;
716 let mut roots = WalkRoots::new(root);
717
718 for path in paths {
719 let path = path.map(|path| {
720 let existing = self
721 .watches
722 .get(&path.absolute)
723 .map(|watch| &watch.metadata);
724 roots.entry(path.absolute, existing)
725 });
726 let entry_dereference = if watch_self { dereference } else { true };
729 match path.and_then(|path| {
731 self.add_single_watch(path, is_recursive, entry_dereference, watch_self)
732 }) {
733 Ok(()) => last_failed = None,
734 Err(err) if watch_self => return Err(err),
736 Err(err) if last_failed.is_some() && last_failed.as_ref() == err.paths.first() => {}
740 Err(err) => {
741 last_failed = err.paths.first().cloned();
742 match WalkFailure::of(&err.kind) {
743 WalkFailure::Skip => {}
744 WalkFailure::Report => self.event_handler.handle_event(Err(err)),
745 WalkFailure::Abort => return Err(err),
746 }
747 }
748 }
749 watch_self = false;
750 }
751
752 Ok(())
753 }
754
755 fn add_single_watch(
756 &mut self,
757 path: WatchPath,
758 is_recursive: bool,
759 requested_dereference: bool,
760 watch_self: bool,
761 ) -> Result<()> {
762 let mut watchmask = event_kind_mask_to_watch_mask(self.event_kind_mask, is_recursive);
764
765 if watch_self {
766 watchmask.insert(WatchMask::DELETE_SELF);
767 watchmask.insert(WatchMask::MOVE_SELF);
768 }
769
770 let existing_watch = self.watches.get(&path.absolute);
771 let dereference = if watch_self {
773 requested_dereference
774 } else {
775 existing_watch
776 .filter(|watch| watch.metadata.is_user_watch)
777 .map_or(requested_dereference, |watch| watch.dereference)
778 };
779 let previous_descriptor = existing_watch.map(|watch| watch.watch_descriptor.clone());
780
781 let mut add_mask = watchmask;
782 if let Some(watch) = existing_watch {
783 watchmask.insert(watch.watch_mask);
784 add_mask = watchmask | WatchMask::MASK_ADD;
785 }
786 if !dereference {
787 add_mask.insert(WatchMask::DONT_FOLLOW);
788 }
789
790 if let Some(ref mut inotify) = self.inotify {
791 log::trace!("adding inotify watch: {}", path.absolute.display());
792
793 match inotify.watches().add(&path.absolute, add_mask) {
794 Err(e) => {
795 Err(if e.raw_os_error() == Some(libc::ENOSPC) {
796 Error::new(ErrorKind::MaxFilesWatch)
798 } else if e.kind() == std::io::ErrorKind::NotFound {
799 Error::new(ErrorKind::PathNotFound)
800 } else {
801 Error::io(e)
802 }
803 .add_path(path.requested))
804 }
805 Ok(w) => {
806 debug_assert!(!watchmask.intersects(RESOLUTION_FLAGS));
807 let is_dir = match watch_metadata(&path.absolute, dereference) {
808 Ok(metadata) => metadata.is_dir(),
809 Err(e) => {
810 let _ = inotify.watches().remove(w.clone());
813 return Err(Error::io_watch(e).add_path(path.requested));
814 }
815 };
816 let metadata = WatchMetadata::new(
817 &path,
818 is_recursive,
819 watch_self,
820 existing_watch.map(|watch| &watch.metadata),
821 );
822
823 if let Some(previous) = previous_descriptor.filter(|previous| *previous != w) {
826 let still_watched = self
828 .watches
829 .iter()
830 .find(|(other, watch)| {
831 *other != &path.absolute && watch.watch_descriptor == previous
832 })
833 .map(|(other, _)| other.clone());
834 match still_watched {
835 Some(other) => {
836 self.paths.insert(previous, other);
837 }
838 None => {
839 self.paths.remove(&previous);
840 Self::remove_single_descriptor(&mut inotify.watches(), previous);
841 }
842 }
843 }
844
845 self.watches.insert(
846 path.absolute.clone(),
847 Watch {
848 watch_descriptor: w.clone(),
849 watch_mask: watchmask,
850 is_dir,
851 dereference,
852 metadata,
853 },
854 );
855 self.paths.insert(w, path.absolute);
856 Ok(())
857 }
858 }
859 } else {
860 Ok(())
861 }
862 }
863
864 fn remove_watch(&mut self, path: PathBuf, remove_recursive: bool) -> Result<()> {
865 let preserved_roots = preserved_watch_roots(
866 &path,
867 remove_recursive,
868 self.watches
869 .iter()
870 .map(|(path, watch)| (path, &watch.metadata)),
871 );
872
873 let watch = self
874 .watches
875 .remove(&path)
876 .ok_or_else(|| Error::watch_not_found().add_path(path.clone()))?;
877 log::trace!("removing inotify watch for {path:?}, remove_recursive: {remove_recursive:?}");
878
879 let mut removed_descriptors = vec![watch.watch_descriptor];
880 if watch.metadata.is_recursive || remove_recursive {
881 let mut remove_list = Vec::new();
882 let mut reset_list = Vec::new();
883 for candidate in self.watches.keys() {
884 if candidate.starts_with(&path) {
885 if let Some(user_is_recursive) =
886 preserved_watch_mode(candidate, &preserved_roots)
887 {
888 if !user_is_recursive
889 || is_preserved_watch_root(candidate, &preserved_roots)
890 {
891 reset_list.push(candidate.clone());
892 }
893 continue;
894 }
895
896 remove_list.push(candidate.clone());
897 }
898 }
899
900 for path in remove_list {
901 if let Some(watch) = self.watches.remove(&path) {
902 removed_descriptors.push(watch.watch_descriptor);
903 }
904 }
905 for path in reset_list {
906 if let Some(watch) = self.watches.get_mut(&path) {
907 watch.metadata.is_recursive = watch.metadata.user_is_recursive;
908 }
909 }
910 }
911
912 self.release_descriptors(removed_descriptors);
913 Ok(())
914 }
915
916 fn release_descriptors<I>(&mut self, descriptors: I)
918 where
919 I: IntoIterator<Item = WatchDescriptor>,
920 {
921 let descriptors: HashSet<_> = descriptors.into_iter().collect();
922 let mut remaining_owners = HashMap::new();
923 for (path, watch) in &self.watches {
924 if descriptors.contains(&watch.watch_descriptor) {
925 remaining_owners
926 .entry(watch.watch_descriptor.clone())
927 .or_insert_with(|| path.clone());
928 }
929 }
930
931 for descriptor in descriptors {
932 if let Some(path) = remaining_owners.remove(&descriptor) {
933 self.paths.insert(descriptor, path);
934 continue;
935 }
936
937 self.paths.remove(&descriptor);
938 if let Some(ref mut inotify) = self.inotify {
939 Self::remove_single_descriptor(&mut inotify.watches(), descriptor);
940 }
941 }
942 }
943
944 fn remove_watch_without_root_os_call(
949 &mut self,
950 path: PathBuf,
951 remove_recursive: bool,
952 ) -> Result<()> {
953 let preserved_roots = preserved_watch_roots(
954 &path,
955 remove_recursive,
956 self.watches
957 .iter()
958 .map(|(path, watch)| (path, &watch.metadata)),
959 );
960
961 match self.watches.remove(&path) {
962 None => return Err(Error::watch_not_found().add_path(path)),
963 Some(watch) => {
964 self.paths.remove(&watch.watch_descriptor);
965
966 if watch.metadata.is_recursive || remove_recursive {
967 let mut inotify_watches =
968 self.inotify.as_mut().map(|inotify| inotify.watches());
969 let mut remove_list = Vec::new();
970 let mut reset_list = Vec::new();
971 for (w, p) in &self.paths {
972 if p.starts_with(&path) {
973 if let Some(user_is_recursive) =
974 preserved_watch_mode(p, &preserved_roots)
975 {
976 if !user_is_recursive
977 || is_preserved_watch_root(p, &preserved_roots)
978 {
979 reset_list.push(p.clone());
980 }
981 continue;
982 }
983
984 if let Some(inotify_watches) = inotify_watches.as_mut() {
987 Self::remove_single_descriptor(inotify_watches, w.clone());
988 }
989 self.watches.remove(p);
990 remove_list.push(w.clone());
991 }
992 }
993 for w in remove_list {
994 self.paths.remove(&w);
995 }
996 for p in reset_list {
997 if let Some(watch) = self.watches.get_mut(&p) {
998 watch.metadata.is_recursive = watch.metadata.user_is_recursive;
999 }
1000 }
1001 }
1002 }
1003 }
1004
1005 Ok(())
1006 }
1007
1008 fn remove_single_descriptor(watches: &mut inotify::Watches, wd: WatchDescriptor) {
1014 if let Err(err) = watches.remove(wd) {
1015 if err.raw_os_error() == Some(libc::EINVAL) {
1016 log::trace!("watch descriptor was already removed from inotify: {err:?}");
1017 } else {
1018 log::warn!("unable to remove watch descriptor from inotify: {err:?}");
1019 }
1020 }
1021 }
1022
1023 fn remove_all_watches(&mut self) -> Result<()> {
1024 if let Some(ref mut inotify) = self.inotify {
1025 let mut inotify_watches = inotify.watches();
1026 for (w, p) in &self.paths {
1027 inotify_watches
1028 .remove(w.clone())
1029 .map_err(|e| Error::io(e).add_path(p.into()))?;
1030 }
1031 self.watches.clear();
1032 self.paths.clear();
1033 }
1034 Ok(())
1035 }
1036}
1037
1038#[derive(Debug, PartialEq, Eq)]
1040enum WalkFailure {
1041 Skip,
1043 Report,
1045 Abort,
1047}
1048
1049impl WalkFailure {
1050 fn of(kind: &ErrorKind) -> Self {
1051 match kind {
1052 ErrorKind::PathNotFound => Self::Skip,
1054 ErrorKind::MaxFilesWatch => Self::Abort,
1055 _ => Self::Report,
1056 }
1057 }
1058}
1059
1060fn walk_dirs(
1066 from: PathBuf,
1067 root: WatchPath,
1068 follow_links: bool,
1069 barriers: &HashSet<PathBuf>,
1070) -> impl Iterator<Item = Result<WatchPath>> {
1071 enum Step {
1072 Visit(WatchPath),
1073 Read(WatchPath),
1074 Leave,
1075 Error(Error),
1076 }
1077
1078 let mut pending = vec![Step::Visit(root.child(from))];
1079 let mut ancestors = Vec::new();
1080
1081 std::iter::from_fn(move || {
1082 loop {
1083 match pending.pop()? {
1084 Step::Visit(path) => {
1085 if barriers.contains(&path.absolute) {
1087 continue;
1088 }
1089 let mut metadata = match symlink_metadata(&path.absolute) {
1091 Ok(metadata) => metadata,
1092 Err(err) => {
1093 return Some(Err(Error::io_watch(err).add_path(path.requested)));
1094 }
1095 };
1096 let is_symlink = metadata.file_type().is_symlink();
1097 if is_symlink && (follow_links || ancestors.is_empty()) {
1098 metadata = match std::fs::metadata(&path.absolute) {
1099 Ok(metadata) => metadata,
1100 Err(err) => {
1101 return Some(Err(Error::io_watch(err).add_path(path.requested)));
1102 }
1103 };
1104 }
1105 if !metadata.is_dir() {
1106 continue;
1107 }
1108 let identity = (metadata.dev(), metadata.ino());
1109 if is_symlink && ancestors.contains(&identity) {
1111 return Some(Err(
1112 Error::generic("symbolic link loop").add_path(path.requested)
1113 ));
1114 }
1115 ancestors.push(identity);
1116 pending.push(Step::Leave);
1117 pending.push(Step::Read(path.clone()));
1119 return Some(Ok(path));
1120 }
1121 Step::Read(path) => {
1122 let entries = match read_dir(&path.absolute) {
1123 Ok(entries) => entries,
1124 Err(err) => {
1125 return Some(Err(Error::io_watch(err).add_path(path.requested)));
1126 }
1127 };
1128 let start = pending.len();
1129 for entry in entries {
1131 let entry = match entry {
1132 Ok(entry) => entry,
1133 Err(err) => {
1134 pending.push(Step::Error(
1135 Error::io_watch(err).add_path(path.requested.clone()),
1136 ));
1137 continue;
1138 }
1139 };
1140 let absolute = entry.path();
1141 if barriers.contains(&absolute) {
1142 continue;
1143 }
1144 match entry.file_type() {
1145 Ok(kind) if kind.is_dir() || (follow_links && kind.is_symlink()) => {
1146 pending.push(Step::Visit(root.child(absolute)));
1147 }
1148 Ok(_) => {}
1149 Err(err) => pending.push(Step::Error(
1150 Error::io_watch(err).add_path(root.child(absolute).requested),
1151 )),
1152 }
1153 }
1154 pending[start..].reverse();
1155 }
1156 Step::Leave => {
1157 ancestors.pop();
1158 }
1159 Step::Error(err) => return Some(Err(err)),
1160 }
1161 }
1162 })
1163}
1164
1165impl INotifyWatcher {
1166 fn from_event_handler(event_handler: Box<dyn EventHandler>, config: &Config) -> Result<Self> {
1167 let inotify = Inotify::init()?;
1168 let event_loop = EventLoop::new(inotify, event_handler, config)?;
1169 let channel = event_loop.event_loop_tx.clone();
1170 let waker = event_loop.event_loop_waker.clone();
1171 event_loop.run();
1172 Ok(INotifyWatcher { channel, waker })
1173 }
1174
1175 fn watch_inner(&mut self, path: &Path, config: WatchPathConfig) -> Result<()> {
1176 let pb = WatchPath::new(path)?;
1177 let (tx, rx) = unbounded();
1178 let msg = EventLoopMsg::AddWatch(pb, config, tx);
1179
1180 self.channel.send(msg)?;
1181 self.waker.wake()?;
1182 rx.recv().map_err(Error::from)?
1183 }
1184
1185 fn unwatch_inner(&mut self, path: &Path) -> Result<()> {
1186 let pb = absolute_path(path)?;
1187 let (tx, rx) = unbounded();
1188 let msg = EventLoopMsg::RemoveWatch(pb, tx);
1189
1190 self.channel.send(msg)?;
1191 self.waker.wake()?;
1192 rx.recv().map_err(Error::from)?
1193 }
1194
1195 fn watched_paths_inner(&self) -> Result<Vec<(PathBuf, RecursiveMode)>> {
1196 let (tx, rx) = unbounded();
1197 self.channel.send(EventLoopMsg::GetWatchedPaths(tx))?;
1198 self.waker.wake()?;
1199 rx.recv().map_err(Error::from)
1200 }
1201}
1202
1203impl Watcher for INotifyWatcher {
1204 fn new<F: EventHandler>(event_handler: F, config: Config) -> Result<Self> {
1206 Self::from_event_handler(Box::new(event_handler), &config)
1207 }
1208
1209 fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
1210 self.watch_inner(path, WatchPathConfig::new(recursive_mode))
1211 }
1212
1213 fn watch_with(&mut self, path: &Path, config: WatchPathConfig) -> Result<()> {
1214 self.watch_inner(path, config)
1215 }
1216
1217 fn unwatch(&mut self, path: &Path) -> Result<()> {
1218 self.unwatch_inner(path)
1219 }
1220
1221 fn configure(&mut self, config: Config) -> Result<bool> {
1222 let (tx, rx) = bounded(1);
1223 self.channel.send(EventLoopMsg::Configure(config, tx))?;
1224 self.waker.wake()?;
1225 rx.recv()?
1226 }
1227
1228 fn watched_paths(&self) -> Result<Vec<(PathBuf, RecursiveMode)>> {
1229 self.watched_paths_inner()
1230 }
1231
1232 fn kind() -> crate::WatcherKind {
1233 crate::WatcherKind::Inotify
1234 }
1235}
1236
1237impl Drop for INotifyWatcher {
1238 fn drop(&mut self) {
1239 self.channel.send(EventLoopMsg::Shutdown).unwrap();
1241 self.waker.wake().unwrap();
1242 }
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247 use std::{
1248 path::{Path, PathBuf},
1249 sync::{Arc, atomic::AtomicBool, mpsc},
1250 thread::{self, available_parallelism},
1251 time::Duration,
1252 };
1253
1254 use super::inotify_sys::WatchMask;
1255 use super::{
1256 Config, Error, ErrorKind, Event, EventKind, EventLoop, INotifyWatcher, RecursiveMode,
1257 Result, WatchPath, WatchPathConfig, Watcher,
1258 };
1259 use notify_types::event::{EventKindMask, RemoveKind};
1260
1261 use crate::test::*;
1262
1263 fn watcher_with_data_events() -> (TestWatcher<INotifyWatcher>, Receiver) {
1265 channel_with_config(
1266 ChannelConfig::default().with_watcher_config(
1267 Config::default().with_event_kinds(EventKindMask::MODIFY_DATA),
1268 ),
1269 )
1270 }
1271
1272 fn recursive_watch() -> WatchPathConfig {
1273 WatchPathConfig::new(RecursiveMode::Recursive)
1274 }
1275
1276 fn non_recursive_watch() -> WatchPathConfig {
1277 WatchPathConfig::new(RecursiveMode::NonRecursive)
1278 }
1279
1280 fn watcher() -> (TestWatcher<INotifyWatcher>, Receiver) {
1281 channel()
1282 }
1283
1284 fn test_event_loop_with_config(config: &Config) -> EventLoop {
1285 let inotify = super::inotify_sys::Inotify::init().unwrap();
1286 EventLoop::new(inotify, Box::new(|_| {}), config).unwrap()
1287 }
1288
1289 fn test_event_loop() -> EventLoop {
1290 test_event_loop_with_config(&Config::default())
1291 }
1292
1293 fn watcher_with_all_events() -> (TestWatcher<INotifyWatcher>, Receiver) {
1296 channel_with_config(
1297 ChannelConfig::default()
1298 .with_timeout(std::time::Duration::from_secs(1))
1299 .with_watcher_config(Config::default().with_event_kinds(EventKindMask::ALL)),
1300 )
1301 }
1302
1303 #[test]
1304 fn inotify_watcher_is_send_and_sync() {
1305 fn check<T: Send + Sync>() {}
1306 check::<INotifyWatcher>();
1307 }
1308
1309 #[test]
1310 fn native_error_type_on_missing_path() {
1311 let mut watcher = INotifyWatcher::new(|_| {}, Config::default()).unwrap();
1312
1313 let result = watcher.watch(
1314 &PathBuf::from("/some/non/existant/path"),
1315 RecursiveMode::NonRecursive,
1316 );
1317
1318 assert!(matches!(
1319 result,
1320 Err(Error {
1321 paths: _,
1322 kind: ErrorKind::PathNotFound
1323 })
1324 ))
1325 }
1326
1327 #[test]
1328 fn stored_watch_mask_keeps_no_resolution_flags() {
1329 let tmpdir = tempfile::tempdir().unwrap();
1330 let root = tmpdir.path().to_path_buf();
1331 let child = root.join("child");
1332 std::fs::create_dir(&child).unwrap();
1333
1334 let inotify = super::inotify_sys::Inotify::init().unwrap();
1335 let mut event_loop = EventLoop::new(inotify, Box::new(|_| {}), &Config::default()).unwrap();
1336
1337 event_loop
1338 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
1339 .expect("watch recursively");
1340 event_loop
1341 .add_watch(WatchPath::new(&root).unwrap(), non_recursive_watch(), true)
1342 .expect("rewatch non-recursively");
1343
1344 for (path, watch) in &event_loop.watches {
1345 assert!(
1346 !watch.watch_mask.intersects(super::RESOLUTION_FLAGS),
1347 "{path:?} stored {:?}",
1348 watch.watch_mask
1349 );
1350 }
1351 }
1352
1353 #[test]
1355 fn recursive_watch_ignores_missing_subdir_during_initial_scan() {
1356 use std::fs;
1357
1358 let tmpdir = tempfile::tempdir().unwrap();
1359 let root = tmpdir.path().to_path_buf();
1360 let disappearing = root.join("disappearing");
1361 fs::create_dir(&disappearing).unwrap();
1362 fs::remove_dir_all(&disappearing).unwrap();
1363
1364 let mut event_loop = test_event_loop();
1365
1366 let result = event_loop.add_watches_for_paths(
1369 WatchPath::new(&root).unwrap(),
1370 [root, disappearing]
1371 .into_iter()
1372 .map(|path| Ok(WatchPath::new(&path).unwrap())),
1373 true,
1374 true,
1375 true,
1376 );
1377 assert!(
1378 result.is_ok(),
1379 "expected recursive watch to succeed, got: {result:?}"
1380 );
1381 }
1382
1383 #[test]
1385 fn walk_failure_policy() {
1386 use super::WalkFailure;
1387 use std::io;
1388
1389 assert_eq!(WalkFailure::of(&ErrorKind::PathNotFound), WalkFailure::Skip);
1390 assert_eq!(
1391 WalkFailure::of(&ErrorKind::MaxFilesWatch),
1392 WalkFailure::Abort
1393 );
1394 assert_eq!(
1395 WalkFailure::of(&ErrorKind::Io(io::ErrorKind::PermissionDenied.into())),
1396 WalkFailure::Report
1397 );
1398 }
1399
1400 fn unwatchable_dir(path: &Path) -> bool {
1403 use std::fs;
1404 use std::os::unix::fs::PermissionsExt;
1405
1406 fs::create_dir(path).unwrap();
1407 fs::set_permissions(path, fs::Permissions::from_mode(0o000)).unwrap();
1408 fs::read_dir(path).is_err()
1409 }
1410
1411 fn make_readable(path: &Path) {
1413 use std::fs;
1414 use std::os::unix::fs::PermissionsExt;
1415
1416 fs::set_permissions(path, fs::Permissions::from_mode(0o755)).unwrap();
1417 }
1418
1419 fn wait_for(
1423 rx: &mpsc::Receiver<Result<Event>>,
1424 poke: impl Fn(),
1425 accept: impl Fn(&Result<Event>) -> bool,
1426 ) -> Option<Result<Event>> {
1427 use std::time::Instant;
1428
1429 let deadline = Instant::now() + Duration::from_secs(5);
1430 while Instant::now() < deadline {
1431 poke();
1432 if let Ok(event) = rx.recv_timeout(Duration::from_millis(200)) {
1433 if accept(&event) {
1434 return Some(event);
1435 }
1436 }
1437 }
1438 None
1439 }
1440
1441 #[test]
1443 fn recursive_watch_survives_an_unwatchable_subdir() {
1444 use std::fs;
1445 use std::sync::Mutex;
1446
1447 let tmpdir = tempfile::tempdir().unwrap();
1448 let root = tmpdir.path().to_path_buf();
1449 let unwatchable = root.join("unwatchable");
1450 let readable = root.join("readable");
1451 let deeper = readable.join("deeper");
1452 fs::create_dir_all(&deeper).unwrap();
1453 if !unwatchable_dir(&unwatchable) {
1454 return; }
1456
1457 let errors = Arc::new(Mutex::new(Vec::new()));
1458 let collected = errors.clone();
1459 let inotify = super::inotify_sys::Inotify::init().unwrap();
1460 let mut event_loop = EventLoop::new(
1461 inotify,
1462 Box::new(move |event: Result<Event>| {
1463 if let Err(e) = event {
1464 collected.lock().unwrap().push(e);
1465 }
1466 }),
1467 &Config::default(),
1468 )
1469 .unwrap();
1470
1471 let result = event_loop.add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true);
1472 make_readable(&unwatchable);
1474
1475 assert!(
1476 result.is_ok(),
1477 "expected the watch to succeed, got: {result:?}"
1478 );
1479 assert!(event_loop.watches.contains_key(&readable));
1480 assert!(event_loop.watches.contains_key(&deeper));
1481 let errors = errors.lock().unwrap();
1482 assert_eq!(
1484 errors
1485 .iter()
1486 .filter(|e| e.paths.contains(&unwatchable))
1487 .count(),
1488 1,
1489 "expected the unwatchable directory to be reported once, got: {errors:?}"
1490 );
1491 }
1492
1493 #[test]
1497 fn recursive_watch_reports_a_walk_error() {
1498 use std::fs;
1499 use std::os::unix::fs::symlink;
1500 use std::sync::Mutex;
1501
1502 let tmpdir = tempfile::tempdir().unwrap();
1503 let root = tmpdir.path().to_path_buf();
1504 let readable = root.join("readable");
1505 fs::create_dir(&readable).unwrap();
1506 symlink(&root, root.join("loop")).unwrap();
1507
1508 let errors = Arc::new(Mutex::new(Vec::new()));
1509 let collected = errors.clone();
1510 let inotify = super::inotify_sys::Inotify::init().unwrap();
1511 let mut event_loop = EventLoop::new(
1512 inotify,
1513 Box::new(move |event: Result<Event>| {
1514 if let Err(e) = event {
1515 collected.lock().unwrap().push(e);
1516 }
1517 }),
1518 &Config::default(),
1519 )
1520 .unwrap();
1521
1522 let result = event_loop.add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true);
1523
1524 assert!(
1525 result.is_ok(),
1526 "expected the watch to succeed, got: {result:?}"
1527 );
1528 assert!(event_loop.watches.contains_key(&readable));
1529 let errors = errors.lock().unwrap();
1530 assert!(
1531 errors
1532 .iter()
1533 .any(|e| e.paths.iter().any(|p| p.ends_with("loop"))),
1534 "expected the symlink loop to be reported, got: {errors:?}"
1535 );
1536 }
1537
1538 #[test]
1539 fn recursive_walk_reports_loops_without_skipping_directory_aliases() {
1540 let tmpdir = tempfile::tempdir().unwrap();
1541 let root = tmpdir.path().join("root");
1542 let destination = tmpdir.path().join("destination");
1543 std::fs::create_dir(&root).unwrap();
1544 std::fs::create_dir_all(destination.join("nested")).unwrap();
1545 std::os::unix::fs::symlink(".", destination.join("loop")).unwrap();
1546 for alias in ["a", "b"] {
1547 std::os::unix::fs::symlink(&destination, root.join(alias)).unwrap();
1548 }
1549
1550 let reported = PathBuf::from("relative-root");
1551 let barriers = Default::default();
1552 let entries = super::walk_dirs(
1553 root.clone(),
1554 WatchPath::from_parts(root, reported.clone()),
1555 true,
1556 &barriers,
1557 );
1558 let mut watched = Vec::new();
1559 let mut failures = Vec::new();
1560 for entry in entries {
1561 match entry {
1562 Ok(path) => watched.push(path.requested),
1563 Err(err) => failures.extend(err.paths),
1564 }
1565 }
1566 watched.sort();
1567 failures.sort();
1568 assert_eq!(
1569 watched,
1570 ["", "a", "a/nested", "b", "b/nested"].map(|path| reported.join(path))
1571 );
1572 assert_eq!(failures, [reported.join("a/loop"), reported.join("b/loop")]);
1573 }
1574
1575 #[test]
1576 fn recursive_walk_visits_directory_aliases_under_a_parent_link() {
1577 let tmpdir = tempfile::tempdir().unwrap();
1578 let root = tmpdir.path().join("child");
1579 std::fs::create_dir_all(root.join("nested")).unwrap();
1580 std::os::unix::fs::symlink("..", root.join("parent")).unwrap();
1581
1582 let reported = PathBuf::from("relative-root");
1583 let barriers = Default::default();
1584 let entries = super::walk_dirs(
1585 root.clone(),
1586 WatchPath::from_parts(root, reported.clone()),
1587 true,
1588 &barriers,
1589 );
1590 let mut watched = Vec::new();
1591 let mut failures = Vec::new();
1592 for entry in entries {
1593 match entry {
1594 Ok(path) => watched.push(path.requested),
1595 Err(err) => failures.extend(err.paths),
1596 }
1597 }
1598 watched.sort();
1599 assert_eq!(
1601 watched,
1602 [
1603 "",
1604 "nested",
1605 "parent",
1606 "parent/child",
1607 "parent/child/nested"
1608 ]
1609 .map(|path| reported.join(path))
1610 );
1611 assert_eq!(failures, [reported.join("parent/child/parent")]);
1612 }
1613
1614 #[test]
1615 fn recursive_walk_honors_follow_symlinks_after_directory_replacement() {
1616 for follow_links in [false, true] {
1617 let tmpdir = tempfile::tempdir().unwrap();
1618 let root = tmpdir.path().join("root");
1619 let children = ["a", "b", "c"].map(|name| root.join(name));
1620 for child in &children {
1621 std::fs::create_dir_all(child).unwrap();
1622 }
1623 let destination = tmpdir.path().join("destination");
1624 std::fs::create_dir_all(destination.join("nested")).unwrap();
1625
1626 let barriers = Default::default();
1627 let mut entries = super::walk_dirs(
1628 root.clone(),
1629 WatchPath::new(&root).unwrap(),
1630 follow_links,
1631 &barriers,
1632 );
1633 assert_eq!(entries.next().unwrap().unwrap().absolute, root);
1634 let first = entries.next().unwrap().unwrap().absolute;
1635
1636 let replaced = children
1638 .iter()
1639 .find(|path| **path != first)
1640 .unwrap()
1641 .clone();
1642 std::fs::remove_dir(&replaced).unwrap();
1643 std::os::unix::fs::symlink(&destination, &replaced).unwrap();
1644
1645 let mut remaining: Vec<_> = entries.map(|entry| entry.unwrap().absolute).collect();
1646 let mut expected: Vec<_> = children
1647 .into_iter()
1648 .filter(|path| *path != first && (follow_links || *path != replaced))
1649 .collect();
1650 if follow_links {
1651 expected.push(replaced.join("nested"));
1652 }
1653 remaining.sort();
1654 expected.sort();
1655 assert_eq!(remaining, expected, "follow_links = {follow_links}");
1656 }
1657 }
1658
1659 #[test]
1661 fn watch_failure_after_a_directory_appears_is_reported() {
1662 use std::fs;
1663
1664 let tmpdir = tempfile::tempdir().unwrap();
1665 let root = tmpdir.path().join("root");
1666 let staging = tmpdir.path().join("staging");
1667 fs::create_dir(&root).unwrap();
1668 fs::create_dir_all(staging.join("readable")).unwrap();
1669 if !unwatchable_dir(&staging.join("unwatchable")) {
1670 return; }
1672
1673 let (tx, rx) = mpsc::channel();
1674 let mut watcher = INotifyWatcher::new(
1675 move |event| {
1676 let _ = tx.send(event);
1677 },
1678 Config::default(),
1679 )
1680 .unwrap();
1681 watcher.watch(&root, RecursiveMode::Recursive).unwrap();
1682
1683 let appearing = root.join("appearing");
1685 fs::rename(&staging, &appearing).unwrap();
1686
1687 let reported = wait_for(&rx, || {}, Result::is_err);
1688 make_readable(&appearing.join("unwatchable"));
1689 let reported = reported
1690 .expect("expected the unwatchable directory to be reported")
1691 .unwrap_err();
1692 assert!(
1693 reported.paths.iter().any(|p| p.ends_with("unwatchable")),
1694 "expected the unwatchable directory to be named, got: {reported:?}"
1695 );
1696
1697 let file = appearing.join("readable").join("file");
1700 assert!(
1701 wait_for(
1702 &rx,
1703 || fs::write(&file, "x").unwrap(),
1704 |event| matches!(event, Ok(event) if event.paths.contains(&file))
1705 )
1706 .is_some(),
1707 "expected a change under the sibling directory to be reported"
1708 );
1709 }
1710
1711 #[test]
1712 fn rewatching_same_path_replaces_recursive_state() {
1713 let tmpdir = tempfile::tempdir().unwrap();
1714 let root = tmpdir.path().to_path_buf();
1715 let child = root.join("child");
1716 std::fs::create_dir(&child).unwrap();
1717
1718 let mut event_loop = test_event_loop();
1719
1720 event_loop
1721 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
1722 .expect("watch recursively");
1723 assert!(event_loop.watches.contains_key(&child));
1724
1725 event_loop
1726 .add_watch(WatchPath::new(&root).unwrap(), non_recursive_watch(), true)
1727 .expect("rewatch non-recursively");
1728
1729 let watch = event_loop.watches.get(&root).expect("root watch");
1730 assert!(watch.metadata.is_user_watch);
1731 assert!(!watch.metadata.user_is_recursive);
1732 assert!(!watch.metadata.is_recursive);
1733 assert!(!event_loop.watches.contains_key(&child));
1734 }
1735
1736 #[test]
1737 fn rewatching_child_preserves_recursive_parent_state() {
1738 let tmpdir = tempfile::tempdir().unwrap();
1739 let root = tmpdir.path().to_path_buf();
1740 let child = root.join("child");
1741 let grandchild = child.join("grandchild");
1742 std::fs::create_dir_all(&grandchild).unwrap();
1743
1744 let mut event_loop = test_event_loop();
1745
1746 event_loop
1747 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
1748 .expect("watch root recursively");
1749 event_loop
1750 .add_watch(WatchPath::new(&child).unwrap(), non_recursive_watch(), true)
1751 .expect("watch child non-recursively");
1752 event_loop
1753 .add_watch(
1754 WatchPath::from_parts(child.clone(), PathBuf::from("reported-child")),
1755 non_recursive_watch(),
1756 true,
1757 )
1758 .expect("rewatch child non-recursively");
1759
1760 let child_watch = event_loop.watches.get(&child).expect("child watch");
1761 assert!(child_watch.metadata.is_user_watch);
1762 assert!(!child_watch.metadata.user_is_recursive);
1763 assert!(child_watch.metadata.is_recursive);
1764 assert_eq!(
1765 child_watch.metadata.reported_path,
1766 PathBuf::from("reported-child")
1767 );
1768
1769 let grandchild_watch = event_loop
1770 .watches
1771 .get(&grandchild)
1772 .expect("grandchild still covered by recursive parent");
1773 assert!(!grandchild_watch.metadata.is_user_watch);
1774 assert!(grandchild_watch.metadata.is_recursive);
1775 }
1776
1777 #[test]
1778 fn nested_recursive_watch_keeps_its_spelling_under_an_outer_watch() {
1779 let tmpdir = tempfile::tempdir().unwrap();
1780 let root = tmpdir.path().to_path_buf();
1781 let child = root.join("child");
1782 let grandchild = child.join("grandchild");
1783 std::fs::create_dir_all(&grandchild).unwrap();
1784
1785 let mut event_loop = test_event_loop();
1786
1787 event_loop
1788 .add_watch(
1789 WatchPath::from_parts(child.clone(), PathBuf::from("reported-child")),
1790 recursive_watch(),
1791 true,
1792 )
1793 .expect("watch child recursively under its own spelling");
1794 event_loop
1795 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
1796 .expect("watch root recursively");
1797
1798 let reported = |event_loop: &EventLoop, path: &Path| {
1799 let watch = event_loop.watches.get(path).expect("watch");
1800 watch.metadata.reported_path.clone()
1801 };
1802 assert_eq!(reported(&event_loop, &root), root);
1803 assert_eq!(reported(&event_loop, &child), Path::new("reported-child"));
1804 assert_eq!(
1805 reported(&event_loop, &grandchild),
1806 Path::new("reported-child/grandchild")
1807 );
1808
1809 event_loop
1810 .remove_watch(root.clone(), false)
1811 .expect("unwatch root");
1812
1813 assert_eq!(
1814 reported(&event_loop, &grandchild),
1815 Path::new("reported-child/grandchild")
1816 );
1817 }
1818
1819 #[test]
1820 fn late_create_event_keeps_the_spelling_of_a_watch_added_since() {
1821 let tmpdir = tempfile::tempdir().unwrap();
1822 let root = tmpdir.path().to_path_buf();
1823 let child = root.join("child");
1824 let grandchild = child.join("grandchild");
1825
1826 let mut event_loop = test_event_loop();
1827 event_loop
1828 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
1829 .expect("watch root recursively");
1830 std::fs::create_dir_all(&grandchild).unwrap();
1831 event_loop
1832 .add_watch(
1833 WatchPath::from_parts(child.clone(), PathBuf::from("reported-child")),
1834 recursive_watch(),
1835 true,
1836 )
1837 .expect("watch child before its create event is handled");
1838
1839 event_loop.handle_inotify();
1840
1841 let watch = event_loop.watches.get(&grandchild).expect("grandchild");
1842 assert_eq!(
1843 watch.metadata.reported_path,
1844 Path::new("reported-child/grandchild")
1845 );
1846 }
1847
1848 #[test]
1849 fn rewatching_carved_out_child_does_not_restore_parent_recursive_state() {
1850 let tmpdir = tempfile::tempdir().unwrap();
1851 let root = tmpdir.path().to_path_buf();
1852 let child = root.join("child");
1853 let grandchild = child.join("grandchild");
1854 std::fs::create_dir_all(&grandchild).unwrap();
1855
1856 let mut event_loop = test_event_loop();
1857
1858 event_loop
1859 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
1860 .expect("watch root recursively");
1861 event_loop
1862 .remove_watch(child.clone(), false)
1863 .expect("carve out child");
1864 event_loop
1865 .add_watch(WatchPath::new(&child).unwrap(), non_recursive_watch(), true)
1866 .expect("watch child non-recursively");
1867 event_loop
1868 .add_watch(
1869 WatchPath::from_parts(child.clone(), PathBuf::from("reported-child")),
1870 non_recursive_watch(),
1871 true,
1872 )
1873 .expect("rewatch child non-recursively");
1874
1875 let child_watch = event_loop.watches.get(&child).expect("child watch");
1876 assert!(child_watch.metadata.is_user_watch);
1877 assert!(!child_watch.metadata.user_is_recursive);
1878 assert!(!child_watch.metadata.is_recursive);
1879 assert_eq!(
1880 child_watch.metadata.reported_path,
1881 PathBuf::from("reported-child")
1882 );
1883 assert!(!event_loop.watches.contains_key(&grandchild));
1884 }
1885
1886 #[test]
1894 #[ignore = "requires changing sysctl fs.inotify.max_user_watches while test is running"]
1895 fn recursive_watch_calls_handler_if_creating_a_file_raises_max_files_watch() {
1896 use std::time::Duration;
1897
1898 let tmpdir = tempfile::tempdir().unwrap();
1899 let (tx, rx) = std::sync::mpsc::channel();
1900 let (proc_changed_tx, proc_changed_rx) = std::sync::mpsc::channel();
1901 let proc_path = Path::new("/proc/sys/fs/inotify/max_user_watches");
1902 let mut watcher = INotifyWatcher::new(
1903 move |result: Result<Event>| match result {
1904 Ok(event) => {
1905 if event.paths.first().is_some_and(|path| path == proc_path) {
1906 proc_changed_tx.send(()).unwrap();
1907 }
1908 }
1909 Err(e) => tx.send(e).unwrap(),
1910 },
1911 Config::default(),
1912 )
1913 .unwrap();
1914
1915 watcher
1916 .watch(tmpdir.path(), RecursiveMode::Recursive)
1917 .unwrap();
1918 watcher
1919 .watch(proc_path, RecursiveMode::NonRecursive)
1920 .unwrap();
1921
1922 proc_changed_rx
1924 .recv_timeout(Duration::from_secs(30))
1925 .unwrap();
1926
1927 let child_dir = tmpdir.path().join("child");
1928 std::fs::create_dir(child_dir).unwrap();
1929
1930 let result = rx.recv_timeout(Duration::from_millis(500));
1931
1932 assert!(
1933 matches!(
1934 &result,
1935 Ok(Error {
1936 kind: ErrorKind::MaxFilesWatch,
1937 paths: _,
1938 })
1939 ),
1940 "expected {:?}, found: {:#?}",
1941 ErrorKind::MaxFilesWatch,
1942 result
1943 );
1944 }
1945
1946 #[test]
1948 fn race_condition_on_unwatch_and_pending_events_with_deleted_descriptor() {
1949 let tmpdir = tempfile::tempdir().expect("tmpdir");
1950 let (tx, rx) = mpsc::channel();
1951 let mut inotify = INotifyWatcher::new(
1954 move |e: Result<Event>| {
1955 let e = match e {
1956 Ok(e) if e.paths.is_empty() => e,
1957 Ok(_) | Err(_) => return,
1958 };
1959 let _ = tx.send(e);
1960 },
1961 Config::default().with_event_kinds(EventKindMask::CORE),
1962 )
1963 .expect("inotify creation");
1964
1965 let dir_path = tmpdir.path();
1966 let file_path = dir_path.join("foo");
1967 std::fs::File::create(&file_path).unwrap();
1968
1969 let stop = Arc::new(AtomicBool::new(false));
1970
1971 let handles: Vec<_> = (0..available_parallelism().unwrap().get().max(4))
1972 .map(|_| {
1973 let file_path = file_path.clone();
1974 let stop = stop.clone();
1975 thread::spawn(move || {
1976 while !stop.load(std::sync::atomic::Ordering::Relaxed) {
1977 let _ = std::fs::File::open(&file_path).unwrap();
1978 }
1979 })
1980 })
1981 .collect();
1982
1983 let non_recursive = RecursiveMode::NonRecursive;
1984 for _ in 0..(handles.len() * 4) {
1985 inotify.watch(dir_path, non_recursive).unwrap();
1986 inotify.unwatch(dir_path).unwrap();
1987 }
1988
1989 stop.store(true, std::sync::atomic::Ordering::Relaxed);
1990 handles
1991 .into_iter()
1992 .for_each(|handle| handle.join().ok().unwrap_or_default());
1993
1994 drop(inotify);
1995
1996 let events: Vec<_> = rx.into_iter().map(|e| format!("{e:?}")).collect();
1997
1998 const LOG_LEN: usize = 10;
1999 let events_len = events.len();
2000 assert!(
2001 events.is_empty(),
2002 "expected no events without path, but got {events_len}. first 10: {:#?}",
2003 &events[..LOG_LEN.min(events_len)]
2004 );
2005 }
2006
2007 #[test]
2009 fn remove_a_subdir_in_a_recursively_watched_parent() {
2010 let tmpdir = tempfile::tempdir().expect("tmpdir");
2011 let subdirectory_path_1 = tmpdir.path().join("subdir");
2012 let subdirectory_path_2 = subdirectory_path_1.join("nested");
2013 std::fs::create_dir(&subdirectory_path_1).expect("unable to create a subdir");
2014 std::fs::create_dir(&subdirectory_path_2).expect("unable to create a nested dir");
2015
2016 let mut watcher =
2017 INotifyWatcher::new(|_| (), Config::default()).expect("unable to create watcher");
2018 watcher
2019 .watch(tmpdir.path(), RecursiveMode::Recursive)
2020 .expect("unable to watch");
2021 std::fs::remove_dir_all(&subdirectory_path_1).expect("unable to remove a subdir");
2022 let unwatch_result = watcher.unwatch(tmpdir.path());
2023
2024 assert!(
2025 matches!(unwatch_result, Ok(())),
2026 "error: {unwatch_result:#?}"
2027 );
2028 }
2029
2030 #[cfg_attr(
2034 target_os = "freebsd",
2035 ignore = "FreeBSD 15.1 does not generate IN_IGNORED unconditionally"
2036 )]
2037 #[test]
2038 fn ignored_event_removes_watch_when_remove_events_are_filtered_out() {
2039 let tmpdir = tempfile::tempdir().expect("tmpdir");
2040 let root = tmpdir.path().to_path_buf();
2041 let child = root.join("child");
2042 std::fs::create_dir(&child).expect("create child");
2043
2044 let config = Config::default().with_event_kinds(EventKindMask::MODIFY_DATA);
2048 let mut event_loop = test_event_loop_with_config(&config);
2049 event_loop
2050 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
2051 .expect("watch recursively");
2052
2053 let child_descriptor = event_loop
2054 .watches
2055 .get(&child)
2056 .expect("child watch")
2057 .watch_descriptor
2058 .clone();
2059
2060 std::fs::remove_dir(&child).expect("remove child");
2061 event_loop.handle_inotify();
2062
2063 assert!(event_loop.watches.contains_key(&root));
2064 assert!(
2065 !event_loop.watches.contains_key(&child),
2066 "IGNORED must remove the stale child watch"
2067 );
2068 assert!(
2069 !event_loop.paths.contains_key(&child_descriptor),
2070 "IGNORED must remove the stale descriptor-to-path mapping"
2071 );
2072 }
2073
2074 #[test]
2075 fn duplicate_watch_removals_are_coalesced() {
2076 let tmpdir = tempfile::tempdir().expect("tmpdir");
2077 let path = tmpdir.path().to_path_buf();
2078 let mut event_loop = test_event_loop();
2079 event_loop
2080 .add_watch(WatchPath::new(&path).unwrap(), non_recursive_watch(), true)
2081 .expect("add_watch");
2082
2083 let mut queued = std::collections::BTreeMap::new();
2086 for removal in [
2087 super::WatchRemoval::WithOsCall,
2088 super::WatchRemoval::DescriptorAlreadyRemoved,
2089 ] {
2090 super::queue_watch_removal(&path, None, &event_loop.watches, &mut queued, removal);
2091 }
2092
2093 assert_eq!(
2094 queued.into_iter().collect::<Vec<_>>(),
2095 vec![(path, super::WatchRemoval::DescriptorAlreadyRemoved)]
2096 );
2097 }
2098
2099 #[test]
2100 fn deleting_parent_still_removes_live_moved_out_descendant_descriptor() {
2101 let tmpdir = tempfile::tempdir().expect("tmpdir");
2102 let root = tmpdir.path().join("root");
2103 let child = root.join("child");
2104 let moved_child = tmpdir.path().join("moved-child");
2105 std::fs::create_dir_all(&child).expect("create watched tree");
2106
2107 let config = Config::default().with_event_kinds(EventKindMask::MODIFY_DATA);
2111 let mut event_loop = test_event_loop_with_config(&config);
2112 event_loop
2113 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
2114 .expect("watch recursively");
2115
2116 let child_descriptor = event_loop
2117 .watches
2118 .get(&child)
2119 .expect("child watch")
2120 .watch_descriptor
2121 .clone();
2122
2123 std::fs::rename(&child, &moved_child).expect("move child out of watched root");
2124 std::fs::remove_dir(&root).expect("remove watched root");
2125 event_loop.handle_inotify();
2126
2127 assert!(event_loop.watches.is_empty());
2128 assert!(event_loop.paths.is_empty());
2129
2130 let remove_result = event_loop
2134 .inotify
2135 .as_mut()
2136 .expect("inotify instance")
2137 .watches()
2138 .remove(child_descriptor);
2139 assert_eq!(
2140 remove_result.unwrap_err().raw_os_error(),
2141 Some(libc::EINVAL),
2142 "the moved-out descendant descriptor must already have been removed"
2143 );
2144 }
2145
2146 #[test]
2147 fn unmount_event_maps_to_remove_other_with_unmount_info() {
2148 let path = PathBuf::from("/tmp/notify-unmount");
2149 let event = super::unmount_event(path.clone());
2150
2151 assert_eq!(event.kind, EventKind::Remove(RemoveKind::Other));
2152 assert_eq!(event.paths, vec![path]);
2153 assert_eq!(event.info(), Some("unmount"));
2154 }
2155
2156 #[test]
2157 fn descriptor_removed_cleanup_removes_internal_state() {
2158 let tmpdir = tempfile::tempdir().unwrap();
2159 let watched = tmpdir.path().join("watched");
2160 std::fs::create_dir(&watched).unwrap();
2161
2162 let mut event_loop = test_event_loop();
2163
2164 event_loop
2165 .add_watch(
2166 WatchPath::new(&watched).unwrap(),
2167 non_recursive_watch(),
2168 true,
2169 )
2170 .expect("add_watch");
2171
2172 event_loop
2173 .remove_watch_without_root_os_call(watched.clone(), true)
2174 .expect("remove_watch_without_root_os_call");
2175
2176 let result = event_loop.remove_watch(watched.clone(), false);
2177 assert!(
2178 matches!(
2179 result,
2180 Err(Error {
2181 kind: ErrorKind::WatchNotFound,
2182 ..
2183 })
2184 ),
2185 "expected WatchNotFound, got: {result:?}"
2186 );
2187 }
2188
2189 #[test]
2190 fn create_file() {
2191 let tmpdir = testdir();
2192 let (mut watcher, mut rx) = watcher_with_all_events();
2193 watcher.watch_recursively(&tmpdir);
2194
2195 let path = tmpdir.path().join("entry");
2196 std::fs::File::create_new(&path).expect("create");
2197
2198 rx.wait_ordered([
2201 expected(&path).create_file(),
2202 expected(&path).access_open_any(),
2203 expected(&path).access_close_write(),
2204 ]);
2205 }
2206
2207 #[test]
2208 fn write_file() {
2209 let tmpdir = testdir();
2210 let (mut watcher, mut rx) = watcher_with_all_events();
2211
2212 let path = tmpdir.path().join("entry");
2213 std::fs::File::create_new(&path).expect("create");
2214
2215 watcher.watch_recursively(&tmpdir);
2216 std::fs::write(&path, b"123").expect("write");
2217
2218 rx.wait_ordered([
2221 expected(&path).access_open_any(),
2222 expected(&path).modify_data_any().multiple(),
2223 expected(&path).access_close_write(),
2224 ]);
2225 }
2226
2227 #[test]
2228 fn chmod_file() {
2229 let tmpdir = testdir();
2230 let (mut watcher, mut rx) = watcher();
2231
2232 let path = tmpdir.path().join("entry");
2233 let file = std::fs::File::create_new(&path).expect("create");
2234 let mut permissions = file.metadata().expect("metadata").permissions();
2235 permissions.set_readonly(true);
2236
2237 watcher.watch_recursively(&tmpdir);
2238 file.set_permissions(permissions).expect("set_permissions");
2239
2240 rx.wait_ordered([expected(&path).modify_meta_any()]);
2243 }
2244
2245 #[test]
2246 fn rename_file() {
2247 let tmpdir = testdir();
2248 let (mut watcher, mut rx) = watcher();
2249
2250 let path = tmpdir.path().join("entry");
2251 std::fs::File::create_new(&path).expect("create");
2252
2253 watcher.watch_recursively(&tmpdir);
2254 let new_path = tmpdir.path().join("renamed");
2255
2256 std::fs::rename(&path, &new_path).expect("rename");
2257
2258 rx.wait_ordered([
2261 expected(&path).rename_from(),
2262 expected(&new_path).rename_to(),
2263 expected([path, new_path]).rename_both(),
2264 ])
2265 .ensure_trackers_len(1);
2266 }
2267
2268 #[test]
2269 fn delete_file() {
2270 let tmpdir = testdir();
2271 let (mut watcher, mut rx) = watcher();
2272 let file = tmpdir.path().join("file");
2273 std::fs::write(&file, "").expect("write");
2274
2275 watcher.watch_nonrecursively(&tmpdir);
2276
2277 std::fs::remove_file(&file).expect("remove");
2278
2279 rx.wait_ordered_exact([expected(&file).remove_file()]);
2280 }
2281
2282 #[test]
2283 fn delete_self_file() {
2284 let tmpdir = testdir();
2285 let (mut watcher, mut rx) = watcher();
2286 let file = tmpdir.path().join("file");
2287 std::fs::write(&file, "").expect("write");
2288
2289 watcher.watch_nonrecursively(&file);
2290
2291 std::fs::remove_file(&file).expect("remove");
2292
2293 rx.wait_ordered_exact([
2294 expected(&file).modify_meta_any(),
2295 expected(&file).remove_file(),
2296 ]);
2297 }
2298
2299 #[test]
2300 fn delete_self_dir() {
2301 let tmpdir = testdir();
2302 let dir = tmpdir.path().join("dir");
2303 std::fs::create_dir(&dir).expect("create");
2304
2305 let (mut watcher, mut rx) = watcher();
2306 watcher.watch_nonrecursively(&dir);
2307
2308 std::fs::remove_dir(&dir).expect("remove");
2309
2310 rx.wait_unordered([expected(&dir).remove_folder()]);
2311 }
2312
2313 #[test]
2314 fn create_write_overwrite() {
2315 let tmpdir = testdir();
2316 let (mut watcher, mut rx) = watcher_with_all_events();
2317 let overwritten_file = tmpdir.path().join("overwritten_file");
2318 let overwriting_file = tmpdir.path().join("overwriting_file");
2319 std::fs::write(&overwritten_file, "123").expect("write1");
2320
2321 watcher.watch_nonrecursively(&tmpdir);
2322
2323 std::fs::File::create(&overwriting_file).expect("create");
2324 std::fs::write(&overwriting_file, "321").expect("write2");
2325 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
2326
2327 rx.wait_ordered([
2330 expected(&overwriting_file).create_file(),
2331 expected(&overwriting_file).access_open_any(),
2332 expected(&overwriting_file).access_close_write(),
2333 expected(&overwriting_file).access_open_any(),
2334 expected(&overwriting_file).modify_data_any().multiple(),
2335 expected(&overwriting_file).access_close_write(),
2336 expected(&overwriting_file).rename_from(),
2337 expected(&overwritten_file).rename_to(),
2338 expected([overwriting_file, overwritten_file]).rename_both(),
2339 ])
2340 .ensure_trackers_len(1);
2341 }
2342
2343 #[test]
2344 fn create_dir() {
2345 let tmpdir = testdir();
2346 let (mut watcher, mut rx) = watcher();
2347 watcher.watch_recursively(&tmpdir);
2348
2349 let path = tmpdir.path().join("entry");
2350 std::fs::create_dir(&path).expect("create");
2351
2352 rx.wait_ordered([expected(&path).create_folder()]);
2355 }
2356
2357 #[test]
2358 fn chmod_dir() {
2359 let tmpdir = testdir();
2360 let (mut watcher, mut rx) = watcher();
2361
2362 let path = tmpdir.path().join("entry");
2363 std::fs::create_dir(&path).expect("create_dir");
2364 let mut permissions = std::fs::metadata(&path).expect("metadata").permissions();
2365 permissions.set_readonly(true);
2366
2367 watcher.watch_recursively(&tmpdir);
2368 std::fs::set_permissions(&path, permissions).expect("set_permissions");
2369
2370 rx.wait_ordered([
2373 expected(&path).access_open_any().optional(),
2374 expected(&path).modify_meta_any(),
2375 expected(&path).modify_meta_any(),
2376 ]);
2377 }
2378
2379 #[test]
2380 fn rename_dir() {
2381 let tmpdir = testdir();
2382 let (mut watcher, mut rx) = watcher();
2383
2384 let path = tmpdir.path().join("entry");
2385 let new_path = tmpdir.path().join("new_path");
2386 std::fs::create_dir(&path).expect("create_dir");
2387
2388 watcher.watch_recursively(&tmpdir);
2389
2390 std::fs::rename(&path, &new_path).expect("rename");
2391
2392 rx.wait_ordered([
2395 expected(&path).access_open_any().optional(),
2396 expected(&path).rename_from(),
2397 expected(&new_path).rename_to(),
2398 expected([path, new_path]).rename_both(),
2399 ])
2400 .ensure_trackers_len(1);
2401 }
2402
2403 #[test]
2404 fn delete_dir() {
2405 let tmpdir = testdir();
2406 let (mut watcher, mut rx) = watcher();
2407
2408 let path = tmpdir.path().join("entry");
2409 std::fs::create_dir(&path).expect("create_dir");
2410
2411 watcher.watch_recursively(&tmpdir);
2412 std::fs::remove_dir(&path).expect("remove");
2413
2414 rx.wait_ordered([
2417 expected(&path).access_open_any().optional(),
2418 expected(&path).remove_folder(),
2419 ]);
2420 }
2421
2422 #[test]
2423 fn rename_dir_twice() {
2424 let tmpdir = testdir();
2425 let (mut watcher, mut rx) = watcher();
2426
2427 let path = tmpdir.path().join("entry");
2428 let new_path = tmpdir.path().join("new_path");
2429 let new_path2 = tmpdir.path().join("new_path2");
2430 std::fs::create_dir(&path).expect("create_dir");
2431
2432 watcher.watch_recursively(&tmpdir);
2433 std::fs::rename(&path, &new_path).expect("rename");
2434 std::fs::rename(&new_path, &new_path2).expect("rename2");
2435
2436 rx.wait_ordered([
2439 expected(&path).access_open_any().optional(),
2440 expected(&path).rename_from(),
2441 expected(&new_path).rename_to(),
2442 expected([&path, &new_path]).rename_both(),
2443 expected(&new_path).access_open_any().optional(),
2444 expected(&new_path).rename_from(),
2445 expected(&new_path2).rename_to(),
2446 expected([&new_path, &new_path2]).rename_both(),
2447 ])
2448 .ensure_trackers_len(2);
2449 }
2450
2451 #[test]
2452 fn move_out_of_watched_dir() {
2453 let tmpdir = testdir();
2454 let subdir = tmpdir.path().join("subdir");
2455 let (mut watcher, mut rx) = watcher();
2456
2457 let path = subdir.join("entry");
2458 std::fs::create_dir_all(&subdir).expect("create_dir_all");
2459 std::fs::File::create_new(&path).expect("create");
2460
2461 watcher.watch_recursively(&subdir);
2462 let new_path = tmpdir.path().join("entry");
2463
2464 std::fs::rename(&path, &new_path).expect("rename");
2465
2466 let event = loop {
2469 let event = rx.recv();
2470 if !matches!(event.kind, EventKind::Access(_)) {
2471 break event;
2472 }
2473 };
2474 let tracker = event.attrs.tracker();
2475 assert_eq!(event, expected(&path).rename_from());
2476 assert!(tracker.is_some(), "tracker is none: {event:#?}");
2477 }
2478
2479 #[test]
2480 fn create_write_write_rename_write_remove() {
2481 let tmpdir = testdir();
2482 let (mut watcher, mut rx) = watcher_with_all_events();
2483
2484 let file1 = tmpdir.path().join("entry");
2485 let file2 = tmpdir.path().join("entry2");
2486 std::fs::File::create_new(&file2).expect("create file2");
2487 let new_path = tmpdir.path().join("renamed");
2488
2489 watcher.watch_recursively(&tmpdir);
2490 std::fs::write(&file1, "123").expect("write 1");
2491 std::fs::write(&file2, "321").expect("write 2");
2492 std::fs::rename(&file1, &new_path).expect("rename");
2493 std::fs::write(&new_path, b"1").expect("write 3");
2494 std::fs::remove_file(&new_path).expect("remove");
2495
2496 rx.wait_ordered([
2499 expected(&file1).create_file(),
2500 expected(&file1).access_open_any(),
2501 expected(&file1).modify_data_any().multiple(),
2502 expected(&file1).access_close_write(),
2503 expected(&file2).access_open_any(),
2504 expected(&file2).modify_data_any().multiple(),
2505 expected(&file2).access_close_write(),
2506 expected(&file1).access_open_any().optional(),
2507 expected(&file1).rename_from(),
2508 expected(&new_path).rename_to(),
2509 expected([&file1, &new_path]).rename_both(),
2510 expected(&new_path).access_open_any(),
2511 expected(&new_path).modify_data_any().multiple(),
2512 expected(&new_path).access_close_write(),
2513 expected(&new_path).remove_file(),
2514 ]);
2515 }
2516
2517 #[test]
2518 fn rename_twice() {
2519 let tmpdir = testdir();
2520 let (mut watcher, mut rx) = watcher();
2521
2522 let path = tmpdir.path().join("entry");
2523 std::fs::File::create_new(&path).expect("create");
2524
2525 watcher.watch_recursively(&tmpdir);
2526 let new_path1 = tmpdir.path().join("renamed1");
2527 let new_path2 = tmpdir.path().join("renamed2");
2528
2529 std::fs::rename(&path, &new_path1).expect("rename1");
2530 std::fs::rename(&new_path1, &new_path2).expect("rename2");
2531
2532 rx.wait_ordered([
2535 expected(&path).access_open_any().optional(),
2536 expected(&path).rename_from(),
2537 expected(&new_path1).rename_to(),
2538 expected([&path, &new_path1]).rename_both(),
2539 expected(&new_path1).access_open_any().optional(),
2540 expected(&new_path1).rename_from(),
2541 expected(&new_path2).rename_to(),
2542 expected([&new_path1, &new_path2]).rename_both(),
2543 ])
2544 .ensure_trackers_len(2);
2545 }
2546
2547 #[test]
2548 fn set_file_mtime() {
2549 let tmpdir = testdir();
2550 let (mut watcher, mut rx) = watcher();
2551
2552 let path = tmpdir.path().join("entry");
2553 let file = std::fs::File::create_new(&path).expect("create");
2554
2555 watcher.watch_recursively(&tmpdir);
2556
2557 file.set_modified(
2558 std::time::SystemTime::now()
2559 .checked_sub(Duration::from_secs(60 * 60))
2560 .expect("time"),
2561 )
2562 .expect("set_time");
2563
2564 let event = loop {
2567 let event = rx.recv();
2568 if !matches!(event.kind, EventKind::Access(_)) {
2569 break event;
2570 }
2571 };
2572 assert_eq!(event, expected(&path).modify());
2574 }
2575
2576 #[test]
2577 fn write_file_non_recursive_watch() {
2578 let tmpdir = testdir();
2579 let (mut watcher, mut rx) = watcher_with_all_events();
2580
2581 let path = tmpdir.path().join("entry");
2582 std::fs::File::create_new(&path).expect("create");
2583
2584 watcher.watch_nonrecursively(&path);
2585
2586 std::fs::write(&path, b"123").expect("write");
2587
2588 rx.wait_ordered([
2591 expected(&path).access_open_any(),
2592 expected(&path).modify_data_any().multiple(),
2593 expected(&path).access_close_write(),
2594 ]);
2595 }
2596
2597 #[test]
2598 fn watch_recursively_then_unwatch_child_stops_events_from_child() {
2599 let tmpdir = testdir();
2600 let (mut watcher, mut rx) = watcher_with_all_events();
2601
2602 let subdir = tmpdir.path().join("subdir");
2603 let file = subdir.join("file");
2604 std::fs::create_dir(&subdir).expect("create");
2605
2606 watcher.watch_recursively(&tmpdir);
2607
2608 std::fs::File::create(&file).expect("create");
2609
2610 rx.wait_ordered([
2613 expected(&subdir).access_open_any().optional(),
2614 expected(&file).create_file(),
2615 expected(&file).access_open_any(),
2616 expected(&file).access_close_write(),
2617 ]);
2618
2619 watcher.watcher.unwatch(&subdir).expect("unwatch");
2620
2621 std::fs::write(&file, b"123").expect("write");
2622
2623 std::fs::remove_dir_all(&subdir).expect("remove_dir_all");
2624
2625 rx.wait_ordered([
2628 expected(&subdir).access_open_any().optional(),
2629 expected(&subdir).remove_folder(),
2630 ]);
2631 }
2632
2633 #[test]
2634 fn write_to_a_hardlink_pointed_to_the_watched_file_triggers_an_event() {
2635 let tmpdir = testdir();
2636 let (mut watcher, mut rx) = watcher_with_all_events();
2637
2638 let subdir = tmpdir.path().join("subdir");
2639 let file = subdir.join("file");
2640 let hardlink = tmpdir.path().join("hardlink");
2641
2642 std::fs::create_dir(&subdir).expect("create");
2643 std::fs::write(&file, "").expect("file");
2644 std::fs::hard_link(&file, &hardlink).expect("hardlink");
2645
2646 watcher.watch_nonrecursively(&file);
2647
2648 std::fs::write(&hardlink, "123123").expect("write to the hard link");
2649
2650 rx.wait_ordered([
2653 expected(&file).access_open_any(),
2654 expected(&file).modify_data_any().multiple(),
2655 expected(&file).access_close_write(),
2656 ]);
2657 }
2658
2659 #[test]
2661 #[cfg(not(target_os = "freebsd"))]
2662 fn write_to_a_hardlink_pointed_to_the_file_in_the_watched_dir_doesnt_trigger_an_event() {
2663 let tmpdir = testdir();
2664 let (mut watcher, mut rx) = watcher();
2665
2666 let subdir = tmpdir.path().join("subdir");
2667 let file = subdir.join("file");
2668 let hardlink = tmpdir.path().join("hardlink");
2669
2670 std::fs::create_dir(&subdir).expect("create");
2671 std::fs::write(&file, "").expect("file");
2672 std::fs::hard_link(&file, &hardlink).expect("hardlink");
2673
2674 watcher.watch_nonrecursively(&subdir);
2675
2676 std::fs::write(&hardlink, "123123").expect("write to the hard link");
2677
2678 let events: Vec<_> = rx
2681 .iter()
2682 .filter(|e| !matches!(e.kind, EventKind::Access(_)))
2683 .collect();
2684 assert!(events.is_empty(), "unexpected events: {events:#?}");
2685 }
2686
2687 #[test]
2688 #[ignore = "see https://github.com/notify-rs/notify/issues/727"]
2689 fn recursive_creation() {
2690 let tmpdir = testdir();
2691 let nested1 = tmpdir.path().join("1");
2692 let nested2 = tmpdir.path().join("1/2");
2693 let nested3 = tmpdir.path().join("1/2/3");
2694 let nested4 = tmpdir.path().join("1/2/3/4");
2695 let nested5 = tmpdir.path().join("1/2/3/4/5");
2696 let nested6 = tmpdir.path().join("1/2/3/4/5/6");
2697 let nested7 = tmpdir.path().join("1/2/3/4/5/6/7");
2698 let nested8 = tmpdir.path().join("1/2/3/4/5/6/7/8");
2699 let nested9 = tmpdir.path().join("1/2/3/4/5/6/7/8/9");
2700
2701 let (mut watcher, mut rx) = watcher();
2702
2703 watcher.watch_recursively(&tmpdir);
2704
2705 std::fs::create_dir_all(&nested9).expect("create_dir_all");
2706 rx.wait_ordered([
2707 expected(&nested1).create_folder(),
2708 expected(&nested2).create_folder(),
2709 expected(&nested3).create_folder(),
2710 expected(&nested4).create_folder(),
2711 expected(&nested5).create_folder(),
2712 expected(&nested6).create_folder(),
2713 expected(&nested7).create_folder(),
2714 expected(&nested8).create_folder(),
2715 expected(&nested9).create_folder(),
2716 ]);
2717 }
2718
2719 #[test]
2725 fn event_kind_mask_core_config_no_access_events() {
2726 let tmpdir = testdir();
2727 let (mut watcher, mut rx) = channel_with_config::<INotifyWatcher>(
2729 ChannelConfig::default()
2730 .with_timeout(std::time::Duration::from_secs(1))
2731 .with_watcher_config(Config::default().with_event_kinds(EventKindMask::CORE)),
2732 );
2733 watcher.watch_recursively(&tmpdir);
2734
2735 let path = tmpdir.path().join("entry");
2736 std::fs::File::create_new(&path).expect("create");
2737
2738 rx.wait_ordered_exact([expected(&path).create_file()])
2740 .ensure_no_tail();
2741 }
2742
2743 #[test]
2745 fn event_kind_mask_all_config_produces_access_events() {
2746 let tmpdir = testdir();
2747 let (mut watcher, mut rx) = watcher_with_all_events();
2748 watcher.watch_recursively(&tmpdir);
2749
2750 let path = tmpdir.path().join("entry");
2751 std::fs::File::create_new(&path).expect("create");
2752
2753 rx.wait_ordered([
2756 expected(&path).create_file(),
2757 expected(&path).access_open_any(),
2758 expected(&path).access_close_write(),
2759 ]);
2760 }
2761
2762 #[test]
2764 fn event_kind_mask_create_remove_only_no_modify() {
2765 let tmpdir = testdir();
2766 let mask = EventKindMask::CREATE | EventKindMask::REMOVE;
2767 let (mut watcher, mut rx) = channel_with_config::<INotifyWatcher>(
2768 ChannelConfig::default()
2769 .with_timeout(std::time::Duration::from_secs(1))
2770 .with_watcher_config(Config::default().with_event_kinds(mask)),
2771 );
2772 watcher.watch_recursively(&tmpdir);
2773
2774 let path = tmpdir.path().join("entry");
2775 std::fs::write(&path, b"123").expect("write");
2776
2777 rx.wait_ordered_exact([expected(&path).create_file()])
2779 .ensure_no_tail();
2780 }
2781
2782 #[test]
2784 fn event_kind_mask_to_watch_mask_core() {
2785 use super::event_kind_mask_to_watch_mask;
2786
2787 let mask = EventKindMask::CORE;
2788 let watch_mask = event_kind_mask_to_watch_mask(mask, false);
2789
2790 assert!(watch_mask.intersects(WatchMask::CREATE));
2792 assert!(watch_mask.intersects(WatchMask::MOVED_TO));
2793 assert!(watch_mask.intersects(WatchMask::DELETE));
2794 assert!(watch_mask.intersects(WatchMask::MOVED_FROM));
2795 assert!(watch_mask.intersects(WatchMask::MODIFY));
2796 assert!(watch_mask.intersects(WatchMask::ATTRIB));
2797 assert!(watch_mask.intersects(WatchMask::MOVE_SELF));
2798
2799 assert!(!watch_mask.intersects(WatchMask::OPEN));
2802 assert!(!watch_mask.intersects(WatchMask::CLOSE_WRITE));
2803 assert!(!watch_mask.intersects(WatchMask::CLOSE_NOWRITE));
2804 }
2805
2806 #[test]
2807 fn event_kind_mask_to_watch_mask_all() {
2808 use super::event_kind_mask_to_watch_mask;
2809
2810 let mask = EventKindMask::ALL;
2811 let watch_mask = event_kind_mask_to_watch_mask(mask, false);
2812
2813 assert!(watch_mask.intersects(WatchMask::OPEN));
2815 assert!(watch_mask.intersects(WatchMask::CLOSE_WRITE));
2816 assert!(watch_mask.intersects(WatchMask::CLOSE_NOWRITE));
2817 }
2818
2819 #[test]
2820 fn event_kind_mask_to_watch_mask_empty() {
2821 use super::event_kind_mask_to_watch_mask;
2822
2823 let mask = EventKindMask::empty();
2824 let watch_mask = event_kind_mask_to_watch_mask(mask, false);
2825
2826 assert!(watch_mask.is_empty());
2828 }
2829
2830 #[test]
2831 fn event_kind_mask_to_watch_mask_access_only() {
2832 use super::event_kind_mask_to_watch_mask;
2833
2834 let mask = EventKindMask::ACCESS_OPEN | EventKindMask::ACCESS_CLOSE;
2836 let watch_mask = event_kind_mask_to_watch_mask(mask, false);
2837
2838 assert!(watch_mask.intersects(WatchMask::OPEN));
2839 assert!(watch_mask.intersects(WatchMask::CLOSE_WRITE));
2840 assert!(!watch_mask.intersects(WatchMask::CLOSE_NOWRITE));
2841
2842 assert!(!watch_mask.intersects(WatchMask::CREATE));
2844 assert!(!watch_mask.intersects(WatchMask::DELETE));
2845 assert!(!watch_mask.intersects(WatchMask::MODIFY));
2846 assert!(!watch_mask.intersects(WatchMask::ATTRIB));
2847 }
2848
2849 #[test]
2850 fn event_kind_mask_to_watch_mask_all_access() {
2851 use super::event_kind_mask_to_watch_mask;
2852
2853 let mask = EventKindMask::ALL_ACCESS;
2855 let watch_mask = event_kind_mask_to_watch_mask(mask, false);
2856
2857 assert!(watch_mask.intersects(WatchMask::OPEN));
2858 assert!(watch_mask.intersects(WatchMask::CLOSE_WRITE));
2859 assert!(watch_mask.intersects(WatchMask::CLOSE_NOWRITE));
2860 }
2861
2862 #[test]
2863 fn event_kind_mask_to_watch_mask_recursive_includes_create() {
2864 use super::event_kind_mask_to_watch_mask;
2865
2866 let watch_mask = event_kind_mask_to_watch_mask(EventKindMask::MODIFY_DATA, true);
2868 assert!(watch_mask.intersects(WatchMask::CREATE));
2869 assert!(watch_mask.intersects(WatchMask::MOVED_TO));
2870 assert!(watch_mask.intersects(WatchMask::MODIFY));
2871
2872 let watch_mask = event_kind_mask_to_watch_mask(EventKindMask::empty(), true);
2874 assert!(watch_mask.intersects(WatchMask::CREATE));
2875 assert!(watch_mask.intersects(WatchMask::MOVED_TO));
2876 assert!(!watch_mask.intersects(WatchMask::MODIFY));
2877
2878 let watch_mask = event_kind_mask_to_watch_mask(EventKindMask::MODIFY_DATA, false);
2880 assert!(!watch_mask.intersects(WatchMask::CREATE));
2881 assert!(!watch_mask.intersects(WatchMask::MOVED_TO));
2882 assert!(watch_mask.intersects(WatchMask::MODIFY));
2883 }
2884
2885 #[test]
2887 fn recursive_watch_tracks_subdirs_without_create_mask() {
2888 let tmpdir = testdir();
2889 let (mut watcher, mut rx) = channel_with_config::<INotifyWatcher>(
2890 ChannelConfig::default()
2891 .with_timeout(std::time::Duration::from_secs(2))
2892 .with_watcher_config(
2893 Config::default().with_event_kinds(EventKindMask::MODIFY_DATA),
2894 ),
2895 );
2896 watcher.watch_recursively(&tmpdir);
2897
2898 let subdir = tmpdir.path().join("subdir");
2899 std::fs::create_dir(&subdir).expect("create subdir");
2900
2901 std::thread::sleep(std::time::Duration::from_millis(50));
2903
2904 let file_path = subdir.join("file.txt");
2905 let mut file = std::fs::File::create_new(&file_path).expect("create file");
2906
2907 use std::io::Write;
2908 file.write_all(b"hello").expect("write");
2909 file.flush().expect("flush");
2910 drop(file);
2911
2912 rx.wait_ordered_exact([expected(&file_path).modify_data()])
2914 .ensure_no_tail();
2915 }
2916
2917 #[test]
2919 fn recursive_watch_of_a_symlinked_dir_without_dereference_still_walks() {
2920 let tmpdir = testdir();
2921 let (mut watcher, mut rx) = watcher_with_data_events();
2922 let destination = tmpdir.path().join("destination");
2923 std::fs::create_dir(&destination).expect("create");
2924 let file = destination.join("file");
2925 std::fs::write(&file, "").expect("write");
2926 let link = tmpdir.path().join("link");
2927 std::os::unix::fs::symlink(&destination, &link).expect("symlink");
2928
2929 watcher
2930 .watcher
2931 .watch_with(&link, recursive_watch().with_dereference_symlinks(false))
2932 .expect("watch link recursively");
2933
2934 std::fs::write(&file, "123").expect("write");
2935
2936 rx.wait_ordered([expected(link.join("file")).modify_data_any().multiple()]);
2937 }
2938
2939 #[test]
2941 fn watching_a_dangling_symlink_without_dereference() {
2942 let tmpdir = tempfile::tempdir().unwrap();
2943 let link = tmpdir.path().join("link");
2944 std::os::unix::fs::symlink(tmpdir.path().join("missing"), &link).unwrap();
2945
2946 let inotify = super::inotify_sys::Inotify::init().unwrap();
2947 let mut event_loop = EventLoop::new(inotify, Box::new(|_| {}), &Config::default()).unwrap();
2948
2949 event_loop
2950 .add_watch(
2951 WatchPath::new(&link).unwrap(),
2952 non_recursive_watch().with_dereference_symlinks(false),
2953 true,
2954 )
2955 .expect("watch dangling symlink");
2956
2957 let watch = event_loop.watches.get(&link).expect("link watch");
2958 assert!(!watch.is_dir);
2959 assert!(!watch.dereference);
2960 }
2961
2962 #[test]
2963 fn watching_a_dangling_symlink_with_dereference_is_not_found() {
2964 let tmpdir = tempfile::tempdir().unwrap();
2965 let link = tmpdir.path().join("link");
2966 std::os::unix::fs::symlink(tmpdir.path().join("missing"), &link).unwrap();
2967
2968 let inotify = super::inotify_sys::Inotify::init().unwrap();
2969 let mut event_loop = EventLoop::new(inotify, Box::new(|_| {}), &Config::default()).unwrap();
2970
2971 let result =
2972 event_loop.add_watch(WatchPath::new(&link).unwrap(), non_recursive_watch(), true);
2973
2974 assert!(matches!(
2975 result,
2976 Err(Error {
2977 paths: _,
2978 kind: ErrorKind::PathNotFound
2979 })
2980 ))
2981 }
2982
2983 #[test]
2984 fn recursive_watch_of_a_symlink_to_a_file_still_follows_it() {
2985 let tmpdir = tempfile::tempdir().unwrap();
2986 let destination = tmpdir.path().join("destination");
2987 let link = tmpdir.path().join("link");
2988 std::fs::write(&destination, "").unwrap();
2989 std::os::unix::fs::symlink(&destination, &link).unwrap();
2990
2991 let inotify = super::inotify_sys::Inotify::init().unwrap();
2992 let mut event_loop = EventLoop::new(inotify, Box::new(|_| {}), &Config::default()).unwrap();
2993
2994 event_loop
2995 .add_watch(WatchPath::new(&link).unwrap(), recursive_watch(), true)
2996 .expect("watch link recursively");
2997
2998 let watch = event_loop.watches.get(&link).expect("link watch");
2999 assert!(watch.dereference);
3000 assert!(!watch.watch_mask.intersects(super::RESOLUTION_FLAGS));
3001 }
3002
3003 #[test]
3004 fn a_recursive_walk_stops_at_an_explicit_non_dereferenced_link() {
3005 let tmpdir = tempfile::tempdir().unwrap();
3006 let outside = tempfile::tempdir().unwrap();
3007 let root = tmpdir.path().to_path_buf();
3008 let destination = outside.path().join("destination");
3009 let nested = destination.join("nested");
3010 let link = root.join("link");
3011 std::fs::create_dir_all(&nested).unwrap();
3012 std::os::unix::fs::symlink(&destination, &link).unwrap();
3013
3014 let inotify = super::inotify_sys::Inotify::init().unwrap();
3015 let mut event_loop = EventLoop::new(inotify, Box::new(|_| {}), &Config::default()).unwrap();
3016
3017 event_loop
3018 .add_watch(
3019 WatchPath::new(&link).unwrap(),
3020 non_recursive_watch().with_dereference_symlinks(false),
3021 true,
3022 )
3023 .expect("watch link");
3024 event_loop
3025 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
3026 .expect("watch root recursively");
3027
3028 let watch = event_loop.watches.get(&link).expect("link watch");
3029 assert!(!watch.dereference);
3030 assert!(watch.metadata.is_user_watch);
3031 assert!(
3032 !event_loop.watches.contains_key(&link.join("nested")),
3033 "the recursive walk crossed an explicit non-dereferenced link"
3034 );
3035 }
3036
3037 #[test]
3038 fn recursive_watch_does_not_report_errors_for_an_explicit_non_dereferenced_link() {
3039 let tmpdir = tempfile::tempdir().unwrap();
3040 let root = tmpdir.path().to_path_buf();
3041 let link = root.join("loop");
3042 std::os::unix::fs::symlink(".", &link).unwrap();
3043
3044 let (tx, rx) = mpsc::channel::<Result<Event>>();
3045 let inotify = super::inotify_sys::Inotify::init().unwrap();
3046 let mut event_loop = EventLoop::new(inotify, Box::new(tx), &Config::default()).unwrap();
3047
3048 event_loop
3049 .add_watch(
3050 WatchPath::new(&link).unwrap(),
3051 non_recursive_watch().with_dereference_symlinks(false),
3052 true,
3053 )
3054 .expect("watch link itself");
3055 event_loop
3056 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
3057 .expect("watch root recursively");
3058
3059 assert!(event_loop.watches.contains_key(&root));
3060 let watch = event_loop.watches.get(&link).expect("link remains watched");
3061 assert!(!watch.dereference);
3062
3063 let errors: Vec<_> = rx.try_iter().filter_map(Result::err).collect();
3065 assert!(
3066 errors.is_empty(),
3067 "the excluded link must not produce walk errors: {errors:?}"
3068 );
3069 }
3070
3071 #[test]
3072 fn recursive_watch_only_reports_unreadable_links_that_are_not_excluded() {
3073 let tmpdir = tempfile::tempdir().unwrap();
3074 let root = tmpdir.path().join("root");
3075 let destination = tmpdir.path().join("unreadable");
3076 std::fs::create_dir(&root).unwrap();
3077 if !unwatchable_dir(&destination) {
3078 return;
3079 }
3080 let excluded = root.join("excluded");
3081 let included = root.join("included");
3082 std::os::unix::fs::symlink(&destination, &excluded).unwrap();
3083 std::os::unix::fs::symlink(&destination, &included).unwrap();
3084
3085 let (tx, rx) = mpsc::channel::<Result<Event>>();
3086 let inotify = super::inotify_sys::Inotify::init().unwrap();
3087 let mut event_loop = EventLoop::new(inotify, Box::new(tx), &Config::default()).unwrap();
3088 event_loop
3089 .add_watch(
3090 WatchPath::new(&excluded).unwrap(),
3091 non_recursive_watch().with_dereference_symlinks(false),
3092 true,
3093 )
3094 .expect("watch excluded link itself");
3095
3096 let result = event_loop.add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true);
3097 make_readable(&destination);
3098 result.expect("watch root recursively");
3099
3100 let errors: Vec<_> = rx.try_iter().filter_map(Result::err).collect();
3101 assert_eq!(errors.len(), 1, "unexpected walk errors: {errors:?}");
3102 assert_eq!(errors[0].paths, [included]);
3103 assert!(matches!(
3104 &errors[0].kind,
3105 ErrorKind::Io(err) if err.kind() == std::io::ErrorKind::PermissionDenied
3106 ));
3107 }
3108
3109 #[test]
3110 fn recursive_watch_follows_only_root_symlink_when_follow_symlinks_is_disabled() {
3111 let tmpdir = tempfile::tempdir().unwrap();
3112 let destination = tmpdir.path().join("destination");
3113 std::fs::create_dir_all(destination.join("child")).unwrap();
3114 std::os::unix::fs::symlink("child", destination.join("alias")).unwrap();
3115 std::os::unix::fs::symlink(".", destination.join("loop")).unwrap();
3116 let root = tmpdir.path().join("root");
3117 std::os::unix::fs::symlink(&destination, &root).unwrap();
3118
3119 let (tx, rx) = mpsc::channel::<Result<Event>>();
3120 let inotify = super::inotify_sys::Inotify::init().unwrap();
3121 let mut event_loop = EventLoop::new(
3122 inotify,
3123 Box::new(tx),
3124 &Config::default().with_follow_symlinks(false),
3125 )
3126 .unwrap();
3127 event_loop
3128 .add_watch(WatchPath::new(&root).unwrap(), recursive_watch(), true)
3129 .expect("watch root recursively");
3130
3131 assert_eq!(event_loop.watches.len(), 2);
3132 assert!(event_loop.watches[&root].metadata.is_user_watch);
3133 assert!(event_loop.watches.contains_key(&root.join("child")));
3134 let errors: Vec<_> = rx.try_iter().filter_map(Result::err).collect();
3135 assert!(errors.is_empty(), "unexpected walk errors: {errors:?}");
3136 }
3137
3138 #[test]
3139 fn watch_with_dereference_disabled_reports_the_link_not_its_destination() {
3140 let tmpdir = testdir();
3141 let (mut watcher, mut rx) = watcher_with_data_events();
3142 let destination = tmpdir.path().join("destination");
3143 let link = tmpdir.path().join("link");
3144 std::fs::write(&destination, "").expect("write");
3145 std::os::unix::fs::symlink(&destination, &link).expect("symlink");
3146
3147 watcher
3148 .watcher
3149 .watch_with(
3150 &link,
3151 non_recursive_watch().with_dereference_symlinks(false),
3152 )
3153 .expect("watch link");
3154 watcher.watch_nonrecursively(&destination);
3155
3156 std::fs::write(&destination, "123").expect("write");
3157
3158 rx.wait_ordered_exact([expected(&destination).modify_data_any().multiple()]);
3159 }
3160
3161 #[test]
3163 fn watching_a_link_itself_keeps_the_ancestors_recursive_coverage() {
3164 let tmpdir = testdir();
3165 let (mut watcher, mut rx) = watcher_with_data_events();
3166 let destination = tmpdir.path().join("destination");
3167 std::fs::create_dir(&destination).expect("create");
3168 let file = destination.join("file");
3169 std::fs::write(&file, "").expect("write");
3170 let link = tmpdir.path().join("link");
3171 std::os::unix::fs::symlink(&destination, &link).expect("symlink");
3172
3173 watcher.watch_nonrecursively(&link);
3174 watcher.watch_recursively(tmpdir.path());
3175 watcher
3176 .watcher
3177 .watch_with(
3178 &link,
3179 non_recursive_watch().with_dereference_symlinks(false),
3180 )
3181 .expect("watch the link itself");
3182
3183 std::fs::write(&file, "123").expect("write");
3184
3185 let reported: std::collections::HashSet<_> =
3186 rx.iter().flat_map(|event| event.paths).collect();
3187 assert_eq!(
3188 reported,
3189 std::collections::HashSet::from([file]),
3190 "the ancestor recursive watch must keep reporting the destination by its real path"
3191 );
3192 }
3193
3194 #[test]
3196 fn watching_a_link_itself_drops_a_destination_reached_only_through_it() {
3197 let outside = testdir();
3198 let destination = outside.path().join("destination");
3199 let nested = destination.join("nested");
3200 std::fs::create_dir_all(&nested).expect("create");
3201 let file = nested.join("file");
3202 std::fs::write(&file, "").expect("write");
3203
3204 let tmpdir = testdir();
3205 let (mut watcher, mut rx) = watcher_with_data_events();
3206 let link = tmpdir.path().join("link");
3207 std::os::unix::fs::symlink(&destination, &link).expect("symlink");
3208
3209 watcher.watch_recursively(tmpdir.path());
3210 watcher
3211 .watcher
3212 .watch_with(
3213 &link,
3214 non_recursive_watch().with_dereference_symlinks(false),
3215 )
3216 .expect("watch the link itself");
3217
3218 std::fs::write(&file, "123").expect("write");
3219
3220 let reported: Vec<_> = rx.iter().flat_map(|event| event.paths).collect();
3221 assert!(
3222 reported.is_empty(),
3223 "the destination is still reported after the caller asked not to follow the link: \
3224 {reported:?}"
3225 );
3226 }
3227}