1use super::event::*;
8use super::{Config, Error, ErrorKind, EventHandler, RecursiveMode, Result, WatchMode, Watcher};
9use crate::bimap::BiHashMap;
10use crate::{BoundSender, Receiver, Sender, TargetMode, bounded, unbounded};
11use inotify as inotify_sys;
12use inotify_sys::{EventMask, Inotify, WatchDescriptor, WatchMask};
13use std::collections::HashMap;
14#[cfg(test)]
15use std::collections::HashSet;
16use std::env;
17use std::fs::metadata;
18use std::os::unix::fs::MetadataExt;
19use std::os::unix::io::AsRawFd;
20use std::path::{Path, PathBuf};
21use std::sync::Arc;
22use std::thread;
23use walkdir::WalkDir;
24
25const INOTIFY: mio::Token = mio::Token(0);
26const MESSAGE: mio::Token = mio::Token(1);
27
28struct EventLoop {
35 running: bool,
36 poll: mio::Poll,
37 event_loop_waker: Arc<mio::Waker>,
38 event_loop_tx: Sender<EventLoopMsg>,
39 event_loop_rx: Receiver<EventLoopMsg>,
40 inotify: Option<Inotify>,
41 event_handler: Box<dyn EventHandler>,
42 watches: HashMap<PathBuf, WatchMode>,
43 watch_handles: BiHashMap<WatchDescriptor, PathBuf, (bool, bool)>,
44 rename_event: Option<Event>,
45 follow_links: bool,
46}
47
48#[derive(Debug)]
50pub struct INotifyWatcher {
51 channel: Sender<EventLoopMsg>,
52 waker: Arc<mio::Waker>,
53}
54
55enum EventLoopMsg {
56 AddWatch(PathBuf, WatchMode, Sender<Result<()>>),
57 RemoveWatch(PathBuf, Sender<Result<()>>),
58 Shutdown,
59 Configure(Config, BoundSender<Result<bool>>),
60 #[cfg(test)]
61 GetWatchHandles(BoundSender<HashSet<PathBuf>>),
62}
63
64#[inline]
65fn add_watch_by_event(
66 path: &PathBuf,
67 is_file_without_hardlinks: bool,
68 watches: &HashMap<PathBuf, WatchMode>,
69 add_watches: &mut Vec<(PathBuf, bool, bool)>,
70) {
71 if let Some(watch_mode) = watches.get(path) {
72 add_watches.push((
73 path.to_owned(),
74 watch_mode.recursive_mode.is_recursive(),
75 is_file_without_hardlinks,
76 ));
77 return;
78 }
79
80 let Some(parent) = path.parent() else {
81 return;
82 };
83 if let Some(watch_mode) = watches.get(parent) {
84 add_watches.push((
85 path.to_owned(),
86 watch_mode.recursive_mode.is_recursive(),
87 is_file_without_hardlinks,
88 ));
89 return;
90 }
91
92 let mut current = parent;
93 while let Some(parent) = current.parent() {
94 if let Some(watch_mode) = watches.get(parent)
95 && watch_mode.recursive_mode == RecursiveMode::Recursive
96 {
97 add_watches.push((path.to_owned(), true, is_file_without_hardlinks));
98 return;
99 }
100 current = parent;
101 }
102}
103
104#[inline]
105fn remove_watch_by_event(
106 path: &PathBuf,
107 watch_handles: &BiHashMap<WatchDescriptor, PathBuf, (bool, bool)>,
108 remove_watches: &mut Vec<PathBuf>,
109) {
110 if watch_handles.contains_right(path) {
111 remove_watches.push(path.to_owned());
112 }
113}
114
115impl EventLoop {
116 pub fn new(
117 inotify: Inotify,
118 event_handler: Box<dyn EventHandler>,
119 follow_links: bool,
120 ) -> Result<Self> {
121 let (event_loop_tx, event_loop_rx) = unbounded::<EventLoopMsg>();
122 let poll = mio::Poll::new()?;
123
124 let event_loop_waker = Arc::new(mio::Waker::new(poll.registry(), MESSAGE)?);
125
126 let inotify_fd = inotify.as_raw_fd();
127 let mut evented_inotify = mio::unix::SourceFd(&inotify_fd);
128 poll.registry()
129 .register(&mut evented_inotify, INOTIFY, mio::Interest::READABLE)?;
130
131 let event_loop = EventLoop {
132 running: true,
133 poll,
134 event_loop_waker,
135 event_loop_tx,
136 event_loop_rx,
137 inotify: Some(inotify),
138 event_handler,
139 watches: HashMap::new(),
140 watch_handles: BiHashMap::new(),
141 rename_event: None,
142 follow_links,
143 };
144 Ok(event_loop)
145 }
146
147 pub fn run(self) {
149 let result = thread::Builder::new()
150 .name("notify-rs inotify loop".to_string())
151 .spawn(|| self.event_loop_thread());
152 if let Err(e) = result {
153 tracing::error!(?e, "failed to start inotify event loop thread");
154 }
155 }
156
157 fn event_loop_thread(mut self) {
158 let mut events = mio::Events::with_capacity(16);
159 loop {
160 match self.poll.poll(&mut events, None) {
162 Err(ref e) if matches!(e.kind(), std::io::ErrorKind::Interrupted) => {
163 }
166 Err(e) => panic!("poll failed: {}", e),
167 Ok(()) => {}
168 }
169
170 for event in &events {
172 self.handle_event(event);
173 }
174
175 if !self.running {
177 break;
178 }
179 }
180 }
181
182 fn handle_event(&mut self, event: &mio::event::Event) {
184 match event.token() {
185 MESSAGE => {
186 self.handle_messages()
188 }
189 INOTIFY => {
190 self.handle_inotify()
192 }
193 _ => unreachable!(),
194 }
195 }
196
197 fn handle_messages(&mut self) {
198 while let Ok(msg) = self.event_loop_rx.try_recv() {
199 match msg {
200 EventLoopMsg::AddWatch(path, watch_mode, tx) => {
201 let result = tx.send(self.add_watch(path, watch_mode));
202 if let Err(e) = result {
203 tracing::error!(?e, "failed to send AddWatch result");
204 }
205 }
206 EventLoopMsg::RemoveWatch(path, tx) => {
207 let result = tx.send(self.remove_watch(path));
208 if let Err(e) = result {
209 tracing::error!(?e, "failed to send RemoveWatch result");
210 }
211 }
212 EventLoopMsg::Shutdown => {
213 let result = self.remove_all_watches();
214 if let Err(e) = result {
215 tracing::error!(?e, "failed to remove all watches on shutdown");
216 }
217 if let Some(inotify) = self.inotify.take() {
218 let result = inotify.close();
219 if let Err(e) = result {
220 tracing::error!(?e, "failed to close inotify instance on shutdown");
221 }
222 }
223 self.running = false;
224 break;
225 }
226 EventLoopMsg::Configure(config, tx) => {
227 self.configure_raw_mode(config, tx);
228 }
229 #[cfg(test)]
230 EventLoopMsg::GetWatchHandles(tx) => {
231 let handles: HashSet<PathBuf> = self
232 .watch_handles
233 .iter()
234 .map(|(_, path, _)| path.clone())
235 .collect();
236 tx.send(handles).unwrap();
237 }
238 }
239 }
240 }
241
242 fn configure_raw_mode(&mut self, _config: Config, tx: BoundSender<Result<bool>>) {
243 tx.send(Ok(false))
244 .expect("configuration channel disconnected");
245 }
246
247 fn is_watched_path(watches: &HashMap<PathBuf, WatchMode>, path: &Path) -> bool {
248 if watches.contains_key(path) {
249 return true;
250 }
251
252 let Some(parent) = path.parent() else {
253 return false;
254 };
255 if watches.contains_key(parent) {
256 return true;
257 }
258
259 let mut current = parent;
260 while let Some(parent) = current.parent() {
261 if let Some(watch_mode) = watches.get(parent)
262 && watch_mode.recursive_mode == RecursiveMode::Recursive
263 {
264 return true;
265 }
266 current = parent;
267 }
268 false
269 }
270
271 fn handle_inotify(&mut self) {
272 let mut add_watches = Vec::new();
273 let mut remove_watches = Vec::new();
274
275 if let Some(ref mut inotify) = self.inotify {
276 let mut buffer = [0; 1024];
277 loop {
279 match inotify.read_events(&mut buffer) {
280 Ok(events) => {
281 let mut num_events = 0;
282 for event in events {
283 tracing::trace!(?event, "inotify event received");
284
285 num_events += 1;
286 if event.mask.contains(EventMask::Q_OVERFLOW) {
287 let ev = Ok(Event::new(EventKind::Other).set_flag(Flag::Rescan));
288 self.event_handler.handle_event(ev);
289 }
290
291 let path = match event.name {
292 Some(name) => self
293 .watch_handles
294 .get_by_left(&event.wd)
295 .map(|(root, _)| root.join(name)),
296 None => self
297 .watch_handles
298 .get_by_left(&event.wd)
299 .map(|(root, _)| root.clone()),
300 };
301
302 let path = match path {
303 Some(path) => path,
304 None => {
305 tracing::debug!(
306 ?event,
307 "inotify event with unknown descriptor"
308 );
309 continue;
310 }
311 };
312
313 let mut evs = Vec::new();
314
315 if event.mask.contains(EventMask::MOVED_FROM) {
316 remove_watch_by_event(
317 &path,
318 &self.watch_handles,
319 &mut remove_watches,
320 );
321
322 let event = Event::new(EventKind::Modify(ModifyKind::Name(
323 RenameMode::From,
324 )))
325 .add_path(path.clone())
326 .set_tracker(event.cookie as usize);
327
328 self.rename_event = Some(event.clone());
329
330 if Self::is_watched_path(&self.watches, &path) {
331 evs.push(event);
332 }
333 } else if event.mask.contains(EventMask::MOVED_TO) {
334 if Self::is_watched_path(&self.watches, &path) {
335 evs.push(
336 Event::new(EventKind::Modify(ModifyKind::Name(
337 RenameMode::To,
338 )))
339 .set_tracker(event.cookie as usize)
340 .add_path(path.clone()),
341 );
342
343 let trackers_match =
344 self.rename_event.as_ref().and_then(|e| e.tracker())
345 == Some(event.cookie as usize);
346
347 if trackers_match {
348 let rename_event = self.rename_event.take().unwrap(); let from_path = rename_event.paths.first();
350 if from_path.is_none_or(|from_path| {
351 Self::is_watched_path(&self.watches, from_path)
352 }) {
353 evs.push(
354 Event::new(EventKind::Modify(ModifyKind::Name(
355 RenameMode::Both,
356 )))
357 .set_tracker(event.cookie as usize)
358 .add_some_path(from_path.cloned())
359 .add_path(path.clone()),
360 );
361 }
362 }
363 }
364
365 let is_file_without_hardlinks =
366 !event.mask.contains(EventMask::ISDIR)
367 && metadata(&path)
368 .map(|m| m.is_file_without_hardlinks())
369 .unwrap_or_default();
370 add_watch_by_event(
371 &path,
372 is_file_without_hardlinks,
373 &self.watches,
374 &mut add_watches,
375 );
376 }
377 if event.mask.contains(EventMask::MOVE_SELF) {
378 remove_watch_by_event(
379 &path,
380 &self.watch_handles,
381 &mut remove_watches,
382 );
383 if Self::is_watched_path(&self.watches, &path) {
384 evs.push(
385 Event::new(EventKind::Modify(ModifyKind::Name(
386 RenameMode::From,
387 )))
388 .add_path(path.clone()),
389 );
390 }
394 }
395 if event.mask.contains(EventMask::CREATE) {
396 let is_dir = event.mask.contains(EventMask::ISDIR);
397 if Self::is_watched_path(&self.watches, &path) {
398 evs.push(
399 Event::new(EventKind::Create(if is_dir {
400 CreateKind::Folder
401 } else {
402 CreateKind::File
403 }))
404 .add_path(path.clone()),
405 );
406 }
407 let is_file_without_hardlinks = !is_dir
408 && metadata(&path)
409 .map(|m| m.is_file_without_hardlinks())
410 .unwrap_or_default();
411 add_watch_by_event(
412 &path,
413 is_file_without_hardlinks,
414 &self.watches,
415 &mut add_watches,
416 );
417 }
418 if event.mask.contains(EventMask::DELETE) {
419 if Self::is_watched_path(&self.watches, &path) {
420 evs.push(
421 Event::new(EventKind::Remove(
422 if event.mask.contains(EventMask::ISDIR) {
423 RemoveKind::Folder
424 } else {
425 RemoveKind::File
426 },
427 ))
428 .add_path(path.clone()),
429 );
430 }
431 remove_watch_by_event(
432 &path,
433 &self.watch_handles,
434 &mut remove_watches,
435 );
436 }
437 if event.mask.contains(EventMask::DELETE_SELF) {
438 let remove_kind = match self.watch_handles.get_by_right(&path) {
439 Some((_, (_, true))) => RemoveKind::Folder,
440 Some((_, (_, false))) => RemoveKind::File,
441 None => RemoveKind::Other,
442 };
443 if Self::is_watched_path(&self.watches, &path) {
444 evs.push(
445 Event::new(EventKind::Remove(remove_kind))
446 .add_path(path.clone()),
447 );
448 }
449 remove_watch_by_event(
450 &path,
451 &self.watch_handles,
452 &mut remove_watches,
453 );
454 }
455 if event.mask.contains(EventMask::MODIFY)
456 && Self::is_watched_path(&self.watches, &path)
457 {
458 evs.push(
459 Event::new(EventKind::Modify(ModifyKind::Data(
460 DataChange::Any,
461 )))
462 .add_path(path.clone()),
463 );
464 }
465 if event.mask.contains(EventMask::CLOSE_WRITE)
466 && Self::is_watched_path(&self.watches, &path)
467 {
468 evs.push(
469 Event::new(EventKind::Access(AccessKind::Close(
470 AccessMode::Write,
471 )))
472 .add_path(path.clone()),
473 );
474 }
475 if event.mask.contains(EventMask::CLOSE_NOWRITE)
476 && Self::is_watched_path(&self.watches, &path)
477 {
478 evs.push(
479 Event::new(EventKind::Access(AccessKind::Close(
480 AccessMode::Read,
481 )))
482 .add_path(path.clone()),
483 );
484 }
485 if event.mask.contains(EventMask::ATTRIB)
486 && Self::is_watched_path(&self.watches, &path)
487 {
488 evs.push(
489 Event::new(EventKind::Modify(ModifyKind::Metadata(
490 MetadataKind::Any,
491 )))
492 .add_path(path.clone()),
493 );
494 }
495 if event.mask.contains(EventMask::OPEN)
496 && Self::is_watched_path(&self.watches, &path)
497 {
498 evs.push(
499 Event::new(EventKind::Access(AccessKind::Open(
500 AccessMode::Any,
501 )))
502 .add_path(path.clone()),
503 );
504 }
505
506 for ev in evs {
507 self.event_handler.handle_event(Ok(ev));
508 }
509 }
510
511 if num_events == 0 {
513 break;
514 }
515 }
516 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
517 break;
519 }
520 Err(e) => {
521 self.event_handler.handle_event(Err(Error::io(e)));
522 }
523 }
524 }
525 }
526
527 tracing::trace!(
528 ?add_watches,
529 ?remove_watches,
530 "processing inotify watch changes"
531 );
532
533 for path in remove_watches {
534 if self
535 .watches
536 .get(&path)
537 .is_some_and(|watch_mode| watch_mode.target_mode == TargetMode::NoTrack)
538 {
539 self.watches.remove(&path);
540 }
541 self.remove_maybe_recursive_watch(path, true).ok();
542 }
543
544 for (path, is_recursive, is_file_without_hardlinks) in add_watches {
545 if let Err(add_watch_error) =
546 self.add_maybe_recursive_watch(path, is_recursive, is_file_without_hardlinks, false)
547 {
548 if let ErrorKind::MaxFilesWatch = add_watch_error.kind {
552 self.event_handler.handle_event(Err(add_watch_error));
553
554 break;
558 }
559 }
560 }
561 }
562
563 #[tracing::instrument(level = "trace", skip(self))]
564 fn add_watch(&mut self, path: PathBuf, watch_mode: WatchMode) -> Result<()> {
565 if let Some(existing) = self.watches.get(&path) {
566 let need_upgrade_to_recursive = match existing.recursive_mode {
567 RecursiveMode::Recursive => false,
568 RecursiveMode::NonRecursive => {
569 watch_mode.recursive_mode == RecursiveMode::Recursive
570 }
571 };
572 let need_to_watch_parent_newly = match existing.target_mode {
573 TargetMode::TrackPath => false,
574 TargetMode::NoTrack => watch_mode.target_mode == TargetMode::TrackPath,
575 };
576 tracing::trace!(
577 ?need_upgrade_to_recursive,
578 ?need_to_watch_parent_newly,
579 "upgrading existing watch for path: {}",
580 path.display()
581 );
582 if need_to_watch_parent_newly && let Some(parent) = path.parent() {
583 self.add_single_watch(parent.to_path_buf(), true, false)?;
584 }
585 if !need_upgrade_to_recursive {
586 return Ok(());
587 }
588
589 if metadata(&path).map_err(Error::io)?.is_dir() {
591 self.add_maybe_recursive_watch(path.clone(), true, false, true)?;
592 }
593 self.watches
594 .get_mut(&path)
595 .unwrap()
596 .upgrade_with(watch_mode);
597 return Ok(());
598 }
599
600 if watch_mode.target_mode == TargetMode::TrackPath
601 && let Some(parent) = path.parent()
602 {
603 self.add_single_watch(parent.to_path_buf(), true, false)?;
604 }
605
606 let meta = match metadata(&path).map_err(Error::io_watch) {
607 Ok(metadata) => metadata,
608 Err(err) => {
609 if watch_mode.target_mode == TargetMode::TrackPath
610 && matches!(err.kind, ErrorKind::PathNotFound)
611 {
612 self.watches.insert(path, watch_mode);
613 return Ok(());
614 }
615 return Err(err);
616 }
617 };
618
619 self.add_maybe_recursive_watch(
620 path.clone(),
621 watch_mode.recursive_mode.is_recursive() && meta.is_dir(),
624 meta.is_file_without_hardlinks(),
625 watch_mode.target_mode != TargetMode::TrackPath, )?;
627
628 self.watches.insert(path, watch_mode);
629
630 Ok(())
631 }
632
633 #[tracing::instrument(level = "trace", skip(self))]
634 fn add_maybe_recursive_watch(
635 &mut self,
636 path: PathBuf,
637 is_recursive: bool,
638 is_file_without_hardlinks: bool,
639 mut watch_self: bool,
640 ) -> Result<()> {
641 if is_recursive {
642 for entry in WalkDir::new(&path)
643 .follow_links(self.follow_links)
644 .into_iter()
645 .filter_map(filter_dir)
646 {
647 self.add_single_watch(entry.into_path(), false, watch_self)?;
648 watch_self = false;
649 }
650 } else {
651 self.add_single_watch(path.clone(), is_file_without_hardlinks, watch_self)?;
652 }
653 Ok(())
654 }
655
656 #[tracing::instrument(level = "trace", skip(self))]
657 fn add_single_watch(
658 &mut self,
659 path: PathBuf,
660 is_file_without_hardlinks: bool,
661 watch_self: bool,
662 ) -> Result<()> {
663 if let Some((_, &(old_watch_self, _))) = self.watch_handles.get_by_right(&path)
664 && (old_watch_self || !watch_self)
666 {
667 tracing::trace!(
668 "watch handle already exists and no need to upgrade: {}",
669 path.display()
670 );
671 return Ok(());
672 }
673
674 if is_file_without_hardlinks
675 && let Some(parent) = path.parent()
676 && self.watch_handles.get_by_right(parent).is_some()
677 {
678 tracing::trace!(
679 "parent dir watch handle already exists and is a file without hardlinks: {}",
680 path.display()
681 );
682 return Ok(());
683 }
684
685 let mut watchmask = WatchMask::ATTRIB
686 | WatchMask::CREATE
687 | WatchMask::OPEN
688 | WatchMask::DELETE
689 | WatchMask::CLOSE_WRITE
690 | WatchMask::MODIFY
691 | WatchMask::MOVED_FROM
692 | WatchMask::MOVED_TO;
693 if watch_self {
694 watchmask.insert(WatchMask::DELETE_SELF);
695 watchmask.insert(WatchMask::MOVE_SELF);
696 }
697
698 if let Some(ref mut inotify) = self.inotify {
699 tracing::trace!("adding inotify watch: {}", path.display());
700
701 match inotify.watches().add(&path, watchmask) {
702 Err(e) => {
703 Err(if e.raw_os_error() == Some(libc::ENOSPC) {
704 Error::new(ErrorKind::MaxFilesWatch)
706 } else if e.kind() == std::io::ErrorKind::NotFound {
707 Error::new(ErrorKind::PathNotFound)
708 } else {
709 Error::io(e)
710 }
711 .add_path(path))
712 }
713 Ok(w) => {
714 watchmask.remove(WatchMask::MASK_ADD);
715 let is_dir = metadata(&path).map_err(Error::io)?.is_dir();
716 self.watch_handles.insert(w, path, (watch_self, is_dir));
717 Ok(())
718 }
719 }
720 } else {
721 Ok(())
722 }
723 }
724
725 #[tracing::instrument(level = "trace", skip(self))]
726 fn remove_watch(&mut self, path: PathBuf) -> Result<()> {
727 match self.watches.remove(&path) {
728 None => return Err(Error::watch_not_found().add_path(path)),
729 Some(watch_mode) => {
730 self.remove_maybe_recursive_watch(path, watch_mode.recursive_mode.is_recursive())?;
731 }
732 }
733 Ok(())
734 }
735
736 #[tracing::instrument(level = "trace", skip(self))]
737 fn remove_maybe_recursive_watch(&mut self, path: PathBuf, is_recursive: bool) -> Result<()> {
738 let Some(ref mut inotify) = self.inotify else {
739 return Ok(());
740 };
741 let mut inotify_watches = inotify.watches();
742
743 if let Some((handle, _)) = self.watch_handles.remove_by_right(&path) {
744 tracing::trace!("removing inotify watch: {}", path.display());
745
746 inotify_watches
747 .remove(handle.clone())
748 .map_err(|e| Error::io(e).add_path(path.clone()))?;
749 }
750
751 if is_recursive {
752 let mut remove_list = Vec::new();
753 for (w, p, _) in &self.watch_handles {
754 if p.starts_with(&path) {
755 inotify_watches
756 .remove(w.clone())
757 .map_err(|e| Error::io(e).add_path(p.into()))?;
758 remove_list.push(w.clone());
759 }
760 }
761 for w in remove_list {
762 self.watch_handles.remove_by_left(&w);
763 }
764 }
765 Ok(())
766 }
767
768 fn remove_all_watches(&mut self) -> Result<()> {
769 if let Some(ref mut inotify) = self.inotify {
770 let mut inotify_watches = inotify.watches();
771 for (w, p, _) in &self.watch_handles {
772 inotify_watches
773 .remove(w.clone())
774 .map_err(|e| Error::io(e).add_path(p.into()))?;
775 }
776 self.watch_handles.clear();
777 self.watches.clear();
778 }
779 Ok(())
780 }
781}
782
783fn filter_dir(e: walkdir::Result<walkdir::DirEntry>) -> Option<walkdir::DirEntry> {
785 if let Ok(e) = e
786 && let Ok(metadata) = e.metadata()
787 && metadata.is_dir()
788 {
789 return Some(e);
790 }
791 None
792}
793
794impl INotifyWatcher {
795 fn from_event_handler(
796 event_handler: Box<dyn EventHandler>,
797 follow_links: bool,
798 ) -> Result<Self> {
799 let inotify = Inotify::init()?;
800 let event_loop = EventLoop::new(inotify, event_handler, follow_links)?;
801 let channel = event_loop.event_loop_tx.clone();
802 let waker = event_loop.event_loop_waker.clone();
803 event_loop.run();
804 Ok(INotifyWatcher { channel, waker })
805 }
806
807 fn watch_inner(&mut self, path: &Path, watch_mode: WatchMode) -> Result<()> {
808 let pb = if path.is_absolute() {
809 path.to_owned()
810 } else {
811 let p = env::current_dir().map_err(Error::io)?;
812 p.join(path)
813 };
814 let (tx, rx) = unbounded();
815 let msg = EventLoopMsg::AddWatch(pb, watch_mode, tx);
816
817 self.channel.send(msg).unwrap();
819 self.waker.wake().unwrap();
820 rx.recv().unwrap()
821 }
822
823 fn unwatch_inner(&mut self, path: &Path) -> Result<()> {
824 let pb = if path.is_absolute() {
825 path.to_owned()
826 } else {
827 let p = env::current_dir().map_err(Error::io)?;
828 p.join(path)
829 };
830 let (tx, rx) = unbounded();
831 let msg = EventLoopMsg::RemoveWatch(pb, tx);
832
833 self.channel.send(msg).unwrap();
835 self.waker.wake().unwrap();
836 rx.recv().unwrap()
837 }
838}
839
840impl Watcher for INotifyWatcher {
841 #[tracing::instrument(level = "debug", skip(event_handler))]
843 fn new<F: EventHandler>(event_handler: F, config: Config) -> Result<Self> {
844 Self::from_event_handler(Box::new(event_handler), config.follow_symlinks())
845 }
846
847 #[tracing::instrument(level = "debug", skip(self))]
848 fn watch(&mut self, path: &Path, watch_mode: WatchMode) -> Result<()> {
849 self.watch_inner(path, watch_mode)
850 }
851
852 #[tracing::instrument(level = "debug", skip(self))]
853 fn unwatch(&mut self, path: &Path) -> Result<()> {
854 self.unwatch_inner(path)
855 }
856
857 #[tracing::instrument(level = "debug", skip(self))]
858 fn configure(&mut self, config: Config) -> Result<bool> {
859 let (tx, rx) = bounded(1);
860 self.channel.send(EventLoopMsg::Configure(config, tx))?;
861 self.waker.wake()?;
862 rx.recv()?
863 }
864
865 fn kind() -> crate::WatcherKind {
866 crate::WatcherKind::Inotify
867 }
868
869 #[cfg(test)]
870 fn get_watch_handles(&self) -> std::collections::HashSet<std::path::PathBuf> {
871 let (tx, rx) = bounded(1);
872 self.channel
873 .send(EventLoopMsg::GetWatchHandles(tx))
874 .unwrap();
875 self.waker.wake().unwrap();
876 rx.recv().unwrap()
877 }
878}
879
880impl Drop for INotifyWatcher {
881 fn drop(&mut self) {
882 self.channel.send(EventLoopMsg::Shutdown).unwrap();
884 self.waker.wake().unwrap();
885 }
886}
887
888trait MetadataNotifyExt {
889 fn is_file_without_hardlinks(&self) -> bool;
890}
891
892impl MetadataNotifyExt for std::fs::Metadata {
893 #[inline]
894 fn is_file_without_hardlinks(&self) -> bool {
895 self.is_file() && self.nlink() == 1
896 }
897}
898
899#[cfg(test)]
900mod tests {
901 use std::{
902 collections::HashSet,
903 path::{Path, PathBuf},
904 sync::{Arc, atomic::AtomicBool, mpsc},
905 thread::{self, available_parallelism},
906 time::Duration,
907 };
908
909 use super::{Config, Error, ErrorKind, Event, INotifyWatcher, Result, Watcher};
910
911 use crate::{RecursiveMode, TargetMode, config::WatchMode, test::*};
912
913 fn watcher() -> (TestWatcher<INotifyWatcher>, Receiver) {
914 channel()
915 }
916
917 #[test]
918 fn inotify_watcher_is_send_and_sync() {
919 fn check<T: Send + Sync>() {}
920 check::<INotifyWatcher>();
921 }
922
923 #[test]
924 fn native_error_type_on_missing_path() {
925 let mut watcher = INotifyWatcher::new(|_| {}, Config::default()).unwrap();
926
927 let result = watcher.watch(
928 &PathBuf::from("/some/non/existant/path"),
929 WatchMode::non_recursive(),
930 );
931
932 assert!(matches!(
933 result,
934 Err(Error {
935 paths: _,
936 kind: ErrorKind::PathNotFound
937 })
938 ))
939 }
940
941 #[test]
949 #[ignore = "requires changing sysctl fs.inotify.max_user_watches while test is running"]
950 fn recursive_watch_calls_handler_if_creating_a_file_raises_max_files_watch() {
951 use std::time::Duration;
952
953 let tmpdir = tempfile::tempdir().unwrap();
954 let (tx, rx) = std::sync::mpsc::channel();
955 let (proc_changed_tx, proc_changed_rx) = std::sync::mpsc::channel();
956 let proc_path = Path::new("/proc/sys/fs/inotify/max_user_watches");
957 let mut watcher = INotifyWatcher::new(
958 move |result: Result<Event>| match result {
959 Ok(event) => {
960 if event.paths.first().is_some_and(|path| path == proc_path) {
961 proc_changed_tx.send(()).unwrap();
962 }
963 }
964 Err(e) => tx.send(e).unwrap(),
965 },
966 Config::default(),
967 )
968 .unwrap();
969
970 watcher
971 .watch(tmpdir.path(), WatchMode::recursive())
972 .unwrap();
973 watcher
974 .watch(proc_path, WatchMode::non_recursive())
975 .unwrap();
976
977 proc_changed_rx
979 .recv_timeout(Duration::from_secs(30))
980 .unwrap();
981
982 let child_dir = tmpdir.path().join("child");
983 std::fs::create_dir(child_dir).unwrap();
984
985 let result = rx.recv_timeout(Duration::from_millis(500));
986
987 assert!(
988 matches!(
989 &result,
990 Ok(Error {
991 kind: ErrorKind::MaxFilesWatch,
992 paths: _,
993 })
994 ),
995 "expected {:?}, found: {:#?}",
996 ErrorKind::MaxFilesWatch,
997 result
998 );
999 }
1000
1001 #[test]
1003 fn race_condition_on_unwatch_and_pending_events_with_deleted_descriptor() {
1004 let tmpdir = tempfile::tempdir().expect("tmpdir");
1005 let (tx, rx) = mpsc::channel();
1006 let mut inotify = INotifyWatcher::new(
1007 move |e: Result<Event>| {
1008 let e = match e {
1009 Ok(e) if e.paths.is_empty() => e,
1010 Ok(_) | Err(_) => return,
1011 };
1012 let _ = tx.send(e);
1013 },
1014 Config::default(),
1015 )
1016 .expect("inotify creation");
1017
1018 let dir_path = tmpdir.path();
1019 let file_path = dir_path.join("foo");
1020 std::fs::File::create(&file_path).unwrap();
1021
1022 let stop = Arc::new(AtomicBool::new(false));
1023
1024 let handles: Vec<_> = (0..available_parallelism().unwrap().get().max(4))
1025 .map(|_| {
1026 let file_path = file_path.clone();
1027 let stop = stop.clone();
1028 thread::spawn(move || {
1029 while !stop.load(std::sync::atomic::Ordering::Relaxed) {
1030 let _ = std::fs::File::open(&file_path).unwrap();
1031 }
1032 })
1033 })
1034 .collect();
1035
1036 let non_recursive = WatchMode::non_recursive();
1037 for _ in 0..(handles.len() * 4) {
1038 inotify.watch(dir_path, non_recursive).unwrap();
1039 inotify.unwatch(dir_path).unwrap();
1040 }
1041
1042 stop.store(true, std::sync::atomic::Ordering::Relaxed);
1043 handles
1044 .into_iter()
1045 .for_each(|handle| handle.join().ok().unwrap_or_default());
1046
1047 drop(inotify);
1048
1049 let events: Vec<_> = rx.into_iter().map(|e| format!("{e:?}")).collect();
1050
1051 const LOG_LEN: usize = 10;
1052 let events_len = events.len();
1053 assert!(
1054 events.is_empty(),
1055 "expected no events without path, but got {events_len}. first 10: {:#?}",
1056 &events[..LOG_LEN.min(events_len)]
1057 );
1058 }
1059
1060 #[test]
1061 fn create_file() {
1062 let tmpdir = testdir();
1063 let (mut watcher, mut rx) = watcher();
1064 watcher.watch_recursively(&tmpdir);
1065
1066 let path = tmpdir.path().join("entry");
1067 std::fs::File::create_new(&path).expect("create");
1068
1069 rx.wait_ordered_exact([
1070 expected(tmpdir.path()).access_open_any().optional(),
1071 expected(&path).create_file(),
1072 expected(&path).access_open_any(),
1073 expected(&path).access_close_write(),
1074 ]);
1075 assert_eq!(
1076 watcher.get_watch_handles(),
1077 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1078 );
1079 }
1080
1081 #[test]
1082 fn create_self_file() {
1083 let tmpdir = testdir();
1084 let (mut watcher, mut rx) = watcher();
1085
1086 let path = tmpdir.path().join("entry");
1087
1088 watcher.watch_nonrecursively(&path);
1089
1090 std::fs::File::create_new(&path).expect("create");
1091
1092 rx.wait_ordered_exact([
1093 expected(&path).create_file(),
1094 expected(&path).access_open_any(),
1095 expected(&path).access_close_write(),
1096 ]);
1097 assert_eq!(
1098 watcher.get_watch_handles(),
1099 HashSet::from([tmpdir.to_path_buf()])
1100 );
1101 }
1102
1103 #[test]
1104 fn create_self_file_no_track() {
1105 let tmpdir = testdir();
1106 let (mut watcher, _) = watcher();
1107
1108 let path = tmpdir.path().join("entry");
1109
1110 let result = watcher.watcher.watch(
1111 &path,
1112 WatchMode {
1113 recursive_mode: RecursiveMode::NonRecursive,
1114 target_mode: TargetMode::NoTrack,
1115 },
1116 );
1117 assert!(matches!(
1118 result,
1119 Err(Error {
1120 paths: _,
1121 kind: ErrorKind::PathNotFound
1122 })
1123 ));
1124 }
1125
1126 #[test]
1127 #[ignore = "TODO: not implemented"]
1128 fn create_self_file_nested() {
1129 let tmpdir = testdir();
1130 let (mut watcher, mut rx) = watcher();
1131
1132 let path = tmpdir.path().join("entry/nested");
1133
1134 watcher.watch_nonrecursively(&path);
1135
1136 std::fs::create_dir_all(path.parent().unwrap()).expect("create");
1137 std::fs::File::create_new(&path).expect("create");
1138
1139 rx.wait_ordered_exact([
1140 expected(&path).create_file(),
1141 expected(&path).access_open_any(),
1142 expected(&path).access_close_write(),
1143 ]);
1144 assert_eq!(
1145 watcher.get_watch_handles(),
1146 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1147 );
1148 }
1149
1150 #[test]
1151 fn create_file_nested_in_recursive_watch() {
1152 let tmpdir = testdir();
1153 let (mut watcher, mut rx) = watcher();
1154
1155 let nested1_dir = tmpdir.path().join("nested1");
1156 let nested2_dir = nested1_dir.join("nested2");
1157 std::fs::create_dir_all(&nested2_dir).expect("create_dir");
1158
1159 watcher.watch_recursively(&tmpdir);
1160
1161 let path = nested2_dir.join("entry");
1162 std::fs::File::create_new(&path).expect("create");
1163
1164 rx.wait_ordered_exact([
1165 expected(tmpdir.path()).access_open_any().optional(),
1166 expected(&nested1_dir).access_open_any().optional(),
1167 expected(&nested2_dir).access_open_any().optional(),
1168 expected(&path).create_file(),
1169 expected(&path).access_open_any(),
1170 expected(&path).access_close_write(),
1171 ]);
1172 assert_eq!(
1173 watcher.get_watch_handles(),
1174 HashSet::from([
1175 tmpdir.parent_path_buf(),
1176 tmpdir.to_path_buf(),
1177 nested1_dir,
1178 nested2_dir
1179 ])
1180 )
1181 }
1182
1183 #[test]
1184 fn write_file() {
1185 let tmpdir = testdir();
1186 let (mut watcher, mut rx) = watcher();
1187
1188 let path = tmpdir.path().join("entry");
1189 std::fs::File::create_new(&path).expect("create");
1190
1191 watcher.watch_recursively(&tmpdir);
1192 std::fs::write(&path, b"123").expect("write");
1193
1194 rx.wait_ordered_exact([
1195 expected(tmpdir.path()).access_open_any().optional(),
1196 expected(&path).access_open_any(),
1197 expected(&path).modify_data_any().multiple(),
1198 expected(&path).access_close_write(),
1199 ])
1200 .ensure_no_tail();
1201 assert_eq!(
1202 watcher.get_watch_handles(),
1203 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1204 );
1205 }
1206
1207 #[test]
1208 fn chmod_file() {
1209 let tmpdir = testdir();
1210 let (mut watcher, mut rx) = watcher();
1211
1212 let path = tmpdir.path().join("entry");
1213 let file = std::fs::File::create_new(&path).expect("create");
1214 let mut permissions = file.metadata().expect("metadata").permissions();
1215 permissions.set_readonly(true);
1216
1217 watcher.watch_recursively(&tmpdir);
1218 file.set_permissions(permissions).expect("set_permissions");
1219
1220 rx.wait_ordered_exact([
1221 expected(tmpdir.path()).access_open_any().optional(),
1222 expected(&path).modify_meta_any(),
1223 ]);
1224 assert_eq!(
1225 watcher.get_watch_handles(),
1226 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1227 );
1228 }
1229
1230 #[test]
1231 fn rename_file() {
1232 let tmpdir = testdir();
1233 let (mut watcher, mut rx) = watcher();
1234
1235 let path = tmpdir.path().join("entry");
1236 std::fs::File::create_new(&path).expect("create");
1237
1238 watcher.watch_recursively(&tmpdir);
1239 let new_path = tmpdir.path().join("renamed");
1240
1241 std::fs::rename(&path, &new_path).expect("rename");
1242
1243 rx.wait_ordered_exact([
1244 expected(tmpdir.path()).access_open_any().optional(),
1245 expected(&path).rename_from(),
1246 expected(&new_path).rename_to(),
1247 expected([path, new_path]).rename_both(),
1248 ])
1249 .ensure_trackers_len(1)
1250 .ensure_no_tail();
1251 assert_eq!(
1252 watcher.get_watch_handles(),
1253 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1254 );
1255 }
1256
1257 #[test]
1258 fn rename_self_file() {
1259 let tmpdir = testdir();
1260 let (mut watcher, mut rx) = watcher();
1261
1262 let path = tmpdir.path().join("entry");
1263 std::fs::File::create_new(&path).expect("create");
1264
1265 watcher.watch_nonrecursively(&path);
1266 let new_path = tmpdir.path().join("renamed");
1267
1268 std::fs::rename(&path, &new_path).expect("rename");
1269
1270 rx.wait_ordered_exact([expected(&path).rename_from()])
1271 .ensure_no_tail();
1272 assert_eq!(
1273 watcher.get_watch_handles(),
1274 HashSet::from([tmpdir.to_path_buf()])
1275 );
1276
1277 std::fs::rename(&new_path, &path).expect("rename2");
1278
1279 rx.wait_ordered_exact([expected(&path).rename_to()])
1280 .ensure_no_tail();
1281 assert_eq!(
1282 watcher.get_watch_handles(),
1283 HashSet::from([tmpdir.to_path_buf()])
1284 );
1285 }
1286
1287 #[test]
1288 fn rename_self_file_no_track() {
1289 let tmpdir = testdir();
1290 let (mut watcher, mut rx) = watcher();
1291
1292 let path = tmpdir.path().join("entry");
1293 std::fs::File::create_new(&path).expect("create");
1294
1295 watcher.watch(
1296 &path,
1297 WatchMode {
1298 recursive_mode: RecursiveMode::NonRecursive,
1299 target_mode: TargetMode::NoTrack,
1300 },
1301 );
1302
1303 let new_path = tmpdir.path().join("renamed");
1304
1305 std::fs::rename(&path, &new_path).expect("rename");
1306
1307 rx.wait_ordered_exact([expected(&path).rename_from()])
1308 .ensure_no_tail();
1309 assert_eq!(watcher.get_watch_handles(), HashSet::from([]));
1310
1311 let result = watcher.watcher.watch(
1312 &path,
1313 WatchMode {
1314 recursive_mode: RecursiveMode::NonRecursive,
1315 target_mode: TargetMode::NoTrack,
1316 },
1317 );
1318 assert!(matches!(
1319 result,
1320 Err(Error {
1321 paths: _,
1322 kind: ErrorKind::PathNotFound
1323 })
1324 ));
1325 }
1326
1327 #[test]
1328 fn delete_file() {
1329 let tmpdir = testdir();
1330 let (mut watcher, mut rx) = watcher();
1331 let file = tmpdir.path().join("file");
1332 std::fs::write(&file, "").expect("write");
1333
1334 watcher.watch_nonrecursively(&tmpdir);
1335
1336 std::fs::remove_file(&file).expect("remove");
1337
1338 rx.wait_ordered_exact([
1339 expected(tmpdir.path()).access_open_any().optional(),
1340 expected(&file).remove_file(),
1341 ]);
1342 assert_eq!(
1343 watcher.get_watch_handles(),
1344 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1345 );
1346 }
1347
1348 #[test]
1349 fn delete_self_file() {
1350 let tmpdir = testdir();
1351 let (mut watcher, mut rx) = watcher();
1352 let file = tmpdir.path().join("file");
1353 std::fs::write(&file, "").expect("write");
1354
1355 watcher.watch_nonrecursively(&file);
1356
1357 std::fs::remove_file(&file).expect("remove");
1358
1359 rx.wait_ordered_exact([expected(&file).remove_file()]);
1360 assert_eq!(
1361 watcher.get_watch_handles(),
1362 HashSet::from([tmpdir.to_path_buf()])
1363 );
1364
1365 std::fs::write(&file, "").expect("write");
1366
1367 rx.wait_ordered_exact([expected(&file).create_file()]);
1368 assert_eq!(
1369 watcher.get_watch_handles(),
1370 HashSet::from([tmpdir.to_path_buf()])
1371 );
1372 }
1373
1374 #[test]
1375 fn delete_self_file_no_track() {
1376 let tmpdir = testdir();
1377 let (mut watcher, mut rx) = watcher();
1378 let file = tmpdir.path().join("file");
1379 std::fs::write(&file, "").expect("write");
1380
1381 watcher.watch(
1382 &file,
1383 WatchMode {
1384 recursive_mode: RecursiveMode::NonRecursive,
1385 target_mode: TargetMode::NoTrack,
1386 },
1387 );
1388
1389 std::fs::remove_file(&file).expect("remove");
1390
1391 rx.wait_ordered_exact([
1392 expected(&file).modify_meta_any(),
1393 expected(&file).remove_file(),
1394 ]);
1395 assert_eq!(watcher.get_watch_handles(), HashSet::from([]));
1396
1397 std::fs::write(&file, "").expect("write");
1398
1399 rx.ensure_empty_with_wait();
1400 }
1401
1402 #[test]
1403 fn create_write_overwrite() {
1404 let tmpdir = testdir();
1405 let (mut watcher, mut rx) = watcher();
1406 let overwritten_file = tmpdir.path().join("overwritten_file");
1407 let overwriting_file = tmpdir.path().join("overwriting_file");
1408 std::fs::write(&overwritten_file, "123").expect("write1");
1409
1410 watcher.watch_nonrecursively(&tmpdir);
1411
1412 std::fs::File::create(&overwriting_file).expect("create");
1413 std::fs::write(&overwriting_file, "321").expect("write2");
1414 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
1415
1416 rx.wait_ordered_exact([
1417 expected(tmpdir.path()).access_open_any().optional(),
1418 expected(&overwriting_file).create_file(),
1419 expected(&overwriting_file).access_open_any(),
1420 expected(&overwriting_file).access_close_write(),
1421 expected(&overwriting_file).access_open_any(),
1422 expected(&overwriting_file).modify_data_any().multiple(),
1423 expected(&overwriting_file).access_close_write().multiple(),
1424 expected(&overwriting_file).rename_from(),
1425 expected(&overwritten_file).rename_to(),
1426 expected([&overwriting_file, &overwritten_file]).rename_both(),
1427 ])
1428 .ensure_no_tail()
1429 .ensure_trackers_len(1);
1430 assert_eq!(
1431 watcher.get_watch_handles(),
1432 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1433 );
1434 }
1435
1436 #[test]
1437 fn create_self_write_overwrite() {
1438 let tmpdir = testdir();
1439 let (mut watcher, mut rx) = watcher();
1440 let overwritten_file = tmpdir.path().join("overwritten_file");
1441 let overwriting_file = tmpdir.path().join("overwriting_file");
1442 std::fs::write(&overwritten_file, "123").expect("write1");
1443
1444 watcher.watch_nonrecursively(&overwritten_file);
1445
1446 std::fs::File::create(&overwriting_file).expect("create");
1447 std::fs::write(&overwriting_file, "321").expect("write2");
1448 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
1449
1450 rx.wait_ordered_exact([expected(&overwritten_file).rename_to()])
1451 .ensure_no_tail()
1452 .ensure_trackers_len(1);
1453 assert_eq!(
1454 watcher.get_watch_handles(),
1455 HashSet::from([tmpdir.to_path_buf()])
1456 );
1457 }
1458
1459 #[test]
1460 fn create_self_write_overwrite_no_track() {
1461 let tmpdir = testdir();
1462 let (mut watcher, mut rx) = watcher();
1463 let overwritten_file = tmpdir.path().join("overwritten_file");
1464 let overwriting_file = tmpdir.path().join("overwriting_file");
1465 std::fs::write(&overwritten_file, "123").expect("write1");
1466
1467 watcher.watch(
1468 &overwritten_file,
1469 WatchMode {
1470 recursive_mode: RecursiveMode::NonRecursive,
1471 target_mode: TargetMode::NoTrack,
1472 },
1473 );
1474
1475 std::fs::File::create(&overwriting_file).expect("create");
1476 std::fs::write(&overwriting_file, "321").expect("write2");
1477 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
1478
1479 rx.wait_ordered_exact([
1480 expected(&overwritten_file).modify_meta_any(),
1481 expected(&overwritten_file).remove_file(),
1482 ])
1483 .ensure_no_tail()
1484 .ensure_trackers_len(0);
1485 assert_eq!(watcher.get_watch_handles(), HashSet::from([]));
1486 }
1487
1488 #[test]
1489 fn create_dir() {
1490 let tmpdir = testdir();
1491 let (mut watcher, mut rx) = watcher();
1492 watcher.watch_recursively(&tmpdir);
1493
1494 let path = tmpdir.path().join("entry");
1495 std::fs::create_dir(&path).expect("create");
1496
1497 rx.wait_ordered_exact([
1498 expected(tmpdir.path()).access_open_any().optional(),
1499 expected(&path).create_folder(),
1500 ]);
1501 assert_eq!(
1502 watcher.get_watch_handles(),
1503 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf(), path])
1504 );
1505 }
1506
1507 #[test]
1508 fn chmod_dir() {
1509 let tmpdir = testdir();
1510 let (mut watcher, mut rx) = watcher();
1511
1512 let path = tmpdir.path().join("entry");
1513 std::fs::create_dir(&path).expect("create_dir");
1514 let mut permissions = std::fs::metadata(&path).expect("metadata").permissions();
1515 permissions.set_readonly(true);
1516
1517 watcher.watch_recursively(&tmpdir);
1518 std::fs::set_permissions(&path, permissions).expect("set_permissions");
1519
1520 rx.wait_ordered_exact([
1521 expected(tmpdir.path()).access_open_any().optional(),
1522 expected(&path).access_open_any().optional(),
1523 expected(&path).modify_meta_any(),
1524 expected(&path).modify_meta_any(),
1525 ])
1526 .ensure_no_tail();
1527 assert_eq!(
1528 watcher.get_watch_handles(),
1529 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf(), path])
1530 );
1531 }
1532
1533 #[test]
1534 fn rename_dir() {
1535 let tmpdir = testdir();
1536 let (mut watcher, mut rx) = watcher();
1537
1538 let path = tmpdir.path().join("entry");
1539 let new_path = tmpdir.path().join("new_path");
1540 std::fs::create_dir(&path).expect("create_dir");
1541
1542 watcher.watch_recursively(&tmpdir);
1543
1544 std::fs::rename(&path, &new_path).expect("rename");
1545
1546 rx.wait_ordered_exact([
1547 expected(tmpdir.path()).access_open_any().optional(),
1548 expected(&path).access_open_any().optional(),
1549 expected(&path).rename_from(),
1550 expected(&new_path).rename_to(),
1551 expected([&path, &new_path]).rename_both(),
1552 ])
1553 .ensure_trackers_len(1);
1554 assert_eq!(
1555 watcher.get_watch_handles(),
1556 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf(), new_path])
1557 );
1558 }
1559
1560 #[test]
1561 fn delete_dir() {
1562 let tmpdir = testdir();
1563 let (mut watcher, mut rx) = watcher();
1564
1565 let path = tmpdir.path().join("entry");
1566 std::fs::create_dir(&path).expect("create_dir");
1567
1568 watcher.watch_recursively(&tmpdir);
1569 std::fs::remove_dir(&path).expect("remove");
1570
1571 rx.wait_ordered_exact([
1572 expected(tmpdir.path()).access_open_any().optional(),
1573 expected(&path).access_open_any().optional(),
1574 expected(&path).remove_folder(),
1575 ])
1576 .ensure_no_tail();
1577 assert_eq!(
1578 watcher.get_watch_handles(),
1579 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1580 );
1581 }
1582
1583 #[test]
1584 fn delete_self_dir() {
1585 let tmpdir = testdir();
1586 let (mut watcher, mut rx) = watcher();
1587
1588 let path = tmpdir.path().join("entry");
1589 std::fs::create_dir(&path).expect("create_dir");
1590
1591 watcher.watch_recursively(&path);
1592 std::fs::remove_dir(&path).expect("remove");
1593
1594 rx.wait_ordered_exact([
1595 expected(&path).access_open_any().optional(),
1596 expected(&path).remove_folder(),
1597 expected(&path).access_open_any().optional(),
1598 ])
1599 .ensure_no_tail();
1600 assert_eq!(
1601 watcher.get_watch_handles(),
1602 HashSet::from([tmpdir.to_path_buf()])
1603 );
1604
1605 std::fs::create_dir(&path).expect("create_dir2");
1606
1607 rx.wait_ordered_exact([
1608 expected(&path).access_open_any().optional(),
1609 expected(&path).create_folder(),
1610 expected(&path).access_open_any().optional(),
1611 ])
1612 .ensure_no_tail();
1613 assert_eq!(
1614 watcher.get_watch_handles(),
1615 HashSet::from([tmpdir.to_path_buf(), path.clone()])
1616 );
1617 }
1618
1619 #[test]
1620 fn delete_self_dir_no_track() {
1621 let tmpdir = testdir();
1622 let (mut watcher, mut rx) = watcher();
1623
1624 let path = tmpdir.path().join("entry");
1625 std::fs::create_dir(&path).expect("create_dir");
1626
1627 watcher
1628 .watcher
1629 .watch(
1630 &path,
1631 WatchMode {
1632 recursive_mode: RecursiveMode::Recursive,
1633 target_mode: TargetMode::NoTrack,
1634 },
1635 )
1636 .expect("watch");
1637 std::fs::remove_dir(&path).expect("remove");
1638
1639 rx.wait_ordered_exact([expected(&path).remove_folder()])
1640 .ensure_no_tail();
1641 assert_eq!(watcher.get_watch_handles(), HashSet::from([]));
1642
1643 std::fs::create_dir(&path).expect("create_dir2");
1644
1645 rx.ensure_empty_with_wait();
1646 }
1647
1648 #[test]
1649 fn rename_dir_twice() {
1650 let tmpdir = testdir();
1651 let (mut watcher, mut rx) = watcher();
1652
1653 let path = tmpdir.path().join("entry");
1654 let new_path = tmpdir.path().join("new_path");
1655 let new_path2 = tmpdir.path().join("new_path2");
1656 std::fs::create_dir(&path).expect("create_dir");
1657
1658 watcher.watch_recursively(&tmpdir);
1659 std::fs::rename(&path, &new_path).expect("rename");
1660 std::fs::rename(&new_path, &new_path2).expect("rename2");
1661
1662 rx.wait_ordered_exact([
1663 expected(tmpdir.path()).access_open_any().optional(),
1664 expected(&path).access_open_any().optional(),
1665 expected(&path).rename_from(),
1666 expected(&new_path).rename_to(),
1667 expected([&path, &new_path]).rename_both(),
1668 expected(&new_path).access_open_any().optional(),
1669 expected(&new_path).rename_from(),
1670 expected(&new_path2).rename_to(),
1671 expected([&new_path, &new_path2]).rename_both(),
1672 ])
1673 .ensure_trackers_len(2);
1674 assert_eq!(
1675 watcher.get_watch_handles(),
1676 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf(), new_path2])
1677 );
1678 }
1679
1680 #[test]
1681 fn move_out_of_watched_dir() {
1682 let tmpdir = testdir();
1683 let subdir = tmpdir.path().join("subdir");
1684 let (mut watcher, mut rx) = watcher();
1685
1686 let path = subdir.join("entry");
1687 std::fs::create_dir_all(&subdir).expect("create_dir_all");
1688 std::fs::File::create_new(&path).expect("create");
1689
1690 watcher.watch_recursively(&subdir);
1691 let new_path = tmpdir.path().join("entry");
1692
1693 std::fs::rename(&path, &new_path).expect("rename");
1694
1695 rx.wait_ordered_exact([
1696 expected(&subdir).access_open_any(),
1697 expected(&path).rename_from(),
1698 ])
1699 .ensure_trackers_len(1)
1700 .ensure_no_tail();
1701 assert_eq!(
1702 watcher.get_watch_handles(),
1703 HashSet::from([tmpdir.to_path_buf(), subdir])
1704 );
1705 }
1706
1707 #[test]
1708 fn create_write_write_rename_write_remove() {
1709 let tmpdir = testdir();
1710 let (mut watcher, mut rx) = watcher();
1711
1712 let file1 = tmpdir.path().join("entry");
1713 let file2 = tmpdir.path().join("entry2");
1714 std::fs::File::create_new(&file2).expect("create file2");
1715 let new_path = tmpdir.path().join("renamed");
1716
1717 watcher.watch_recursively(&tmpdir);
1718 std::fs::write(&file1, "123").expect("write 1");
1719 std::fs::write(&file2, "321").expect("write 2");
1720 std::fs::rename(&file1, &new_path).expect("rename");
1721 std::fs::write(&new_path, b"1").expect("write 3");
1722 std::fs::remove_file(&new_path).expect("remove");
1723
1724 rx.wait_ordered_exact([
1725 expected(tmpdir.path()).access_open_any().optional(),
1726 expected(&file1).create_file(),
1727 expected(&file1).access_open_any(),
1728 expected(&file1).modify_data_any().multiple(),
1729 expected(&file1).access_close_write(),
1730 expected(&file2).access_open_any(),
1731 expected(&file2).modify_data_any().multiple(),
1732 expected(&file2).access_close_write(),
1733 expected(&file1).access_open_any().optional(),
1734 expected(&file1).rename_from(),
1735 expected(&new_path).rename_to(),
1736 expected([&file1, &new_path]).rename_both(),
1737 expected(&new_path).access_open_any(),
1738 expected(&new_path).modify_data_any().multiple(),
1739 expected(&new_path).access_close_write(),
1740 expected(&new_path).remove_file(),
1741 ]);
1742 assert_eq!(
1743 watcher.get_watch_handles(),
1744 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1745 );
1746 }
1747
1748 #[test]
1749 fn rename_twice() {
1750 let tmpdir = testdir();
1751 let (mut watcher, mut rx) = watcher();
1752
1753 let path = tmpdir.path().join("entry");
1754 std::fs::File::create_new(&path).expect("create");
1755
1756 watcher.watch_recursively(&tmpdir);
1757 let new_path1 = tmpdir.path().join("renamed1");
1758 let new_path2 = tmpdir.path().join("renamed2");
1759
1760 std::fs::rename(&path, &new_path1).expect("rename1");
1761 std::fs::rename(&new_path1, &new_path2).expect("rename2");
1762
1763 rx.wait_ordered_exact([
1764 expected(tmpdir.path()).access_open_any().optional(),
1765 expected(&path).access_open_any().optional(),
1766 expected(&path).rename_from(),
1767 expected(&new_path1).rename_to(),
1768 expected([&path, &new_path1]).rename_both(),
1769 expected(&new_path1).access_open_any().optional(),
1770 expected(&new_path1).rename_from(),
1771 expected(&new_path2).rename_to(),
1772 expected([&new_path1, &new_path2]).rename_both(),
1773 ])
1774 .ensure_no_tail()
1775 .ensure_trackers_len(2);
1776 assert_eq!(
1777 watcher.get_watch_handles(),
1778 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1779 );
1780 }
1781
1782 #[test]
1783 fn set_file_mtime() {
1784 let tmpdir = testdir();
1785 let (mut watcher, mut rx) = watcher();
1786
1787 let path = tmpdir.path().join("entry");
1788 let file = std::fs::File::create_new(&path).expect("create");
1789
1790 watcher.watch_recursively(&tmpdir);
1791
1792 file.set_modified(
1793 std::time::SystemTime::now()
1794 .checked_sub(Duration::from_secs(60 * 60))
1795 .expect("time"),
1796 )
1797 .expect("set_time");
1798
1799 rx.wait_ordered_exact([
1800 expected(tmpdir.path()).access_open_any().optional(),
1801 expected(&path).modify_data_any(),
1802 ])
1803 .ensure_no_tail();
1804 assert_eq!(
1805 watcher.get_watch_handles(),
1806 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1807 );
1808 }
1809
1810 #[test]
1811 fn write_file_non_recursive_watch() {
1812 let tmpdir = testdir();
1813 let (mut watcher, mut rx) = watcher();
1814
1815 let path = tmpdir.path().join("entry");
1816 std::fs::File::create_new(&path).expect("create");
1817
1818 watcher.watch_nonrecursively(&path);
1819
1820 std::fs::write(&path, b"123").expect("write");
1821
1822 rx.wait_ordered_exact([
1823 expected(&path).access_open_any(),
1824 expected(&path).modify_data_any().multiple(),
1825 expected(&path).access_close_write(),
1826 ])
1827 .ensure_no_tail();
1828 assert_eq!(
1829 watcher.get_watch_handles(),
1830 HashSet::from([tmpdir.to_path_buf()])
1831 );
1832 }
1833
1834 #[test]
1835 fn watch_recursively_then_unwatch_child_stops_events_from_child() {
1836 let tmpdir = testdir();
1837 let (mut watcher, mut rx) = watcher();
1838
1839 let subdir = tmpdir.path().join("subdir");
1840 let file = subdir.join("file");
1841 std::fs::create_dir(&subdir).expect("create");
1842
1843 watcher.watch_recursively(&tmpdir);
1844
1845 std::fs::File::create(&file).expect("create");
1846
1847 rx.wait_ordered_exact([
1848 expected(tmpdir.path()).access_open_any().optional(),
1849 expected(&subdir).access_open_any().optional(),
1850 expected(&file).create_file(),
1851 expected(&file).access_open_any(),
1852 expected(&file).access_close_write(),
1853 ])
1854 .ensure_no_tail();
1855 assert_eq!(
1856 watcher.get_watch_handles(),
1857 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf(), subdir])
1858 );
1859
1860 }
1873
1874 #[test]
1875 fn write_to_a_hardlink_pointed_to_the_watched_file_triggers_an_event() {
1876 let tmpdir = testdir();
1877 let (mut watcher, mut rx) = watcher();
1878
1879 let subdir = tmpdir.path().join("subdir");
1880 let subdir2 = tmpdir.path().join("subdir2");
1881 let file = subdir.join("file");
1882 let hardlink = subdir2.join("hardlink");
1883
1884 std::fs::create_dir(&subdir).expect("create");
1885 std::fs::create_dir(&subdir2).expect("create2");
1886 std::fs::write(&file, "").expect("file");
1887 std::fs::hard_link(&file, &hardlink).expect("hardlink");
1888
1889 watcher.watch_nonrecursively(&file);
1890
1891 std::fs::write(&hardlink, "123123").expect("write to the hard link");
1892
1893 rx.wait_ordered_exact([
1894 expected(&file).access_open_any(),
1895 expected(&file).modify_data_any().multiple(),
1896 expected(&file).access_close_write(),
1897 ]);
1898 assert_eq!(watcher.get_watch_handles(), HashSet::from([subdir, file]));
1899 }
1900
1901 #[test]
1902 fn write_to_a_hardlink_pointed_to_the_watched_file_triggers_an_event_even_if_the_parent_is_watched()
1903 {
1904 let tmpdir = testdir();
1905 let (mut watcher, mut rx) = watcher();
1906
1907 let subdir1 = tmpdir.path().join("subdir1");
1908 let subdir2 = subdir1.join("subdir2");
1909 let file = subdir2.join("file");
1910 let hardlink = tmpdir.path().join("hardlink");
1911
1912 std::fs::create_dir_all(&subdir2).expect("create");
1913 std::fs::write(&file, "").expect("file");
1914 std::fs::hard_link(&file, &hardlink).expect("hardlink");
1915
1916 watcher.watch_nonrecursively(&subdir2);
1917 watcher.watch_nonrecursively(&file);
1918
1919 std::fs::write(&hardlink, "123123").expect("write to the hard link");
1920
1921 rx.wait_ordered_exact([
1922 expected(&subdir2).access_open_any().optional(),
1923 expected(&file).access_open_any(),
1924 expected(&file).modify_data_any().multiple(),
1925 expected(&file).access_close_write(),
1926 ]);
1927 assert_eq!(
1928 watcher.get_watch_handles(),
1929 HashSet::from([subdir1, subdir2, file])
1930 );
1931 }
1932
1933 #[test]
1934 fn write_to_a_hardlink_pointed_to_the_file_in_the_watched_dir_doesnt_trigger_an_event() {
1935 let tmpdir = testdir();
1936 let (mut watcher, mut rx) = watcher();
1937
1938 let subdir = tmpdir.path().join("subdir");
1939 let subdir2 = tmpdir.path().join("subdir2");
1940 let file = subdir.join("file");
1941 let hardlink = subdir2.join("hardlink");
1942
1943 std::fs::create_dir(&subdir).expect("create");
1944 std::fs::create_dir(&subdir2).expect("create");
1945 std::fs::write(&file, "").expect("file");
1946 std::fs::hard_link(&file, &hardlink).expect("hardlink");
1947
1948 watcher.watch_nonrecursively(&subdir);
1949
1950 std::fs::write(&hardlink, "123123").expect("write to the hard link");
1951
1952 rx.wait_ordered_exact([expected(&subdir).access_open_any().optional()])
1953 .ensure_no_tail();
1954 assert_eq!(
1955 watcher.get_watch_handles(),
1956 HashSet::from([tmpdir.to_path_buf(), subdir])
1957 );
1958 }
1959
1960 #[test]
1961 #[ignore = "see https://github.com/notify-rs/notify/issues/727"]
1962 fn recursive_creation() {
1963 let tmpdir = testdir();
1964 let nested1 = tmpdir.path().join("1");
1965 let nested2 = tmpdir.path().join("1/2");
1966 let nested3 = tmpdir.path().join("1/2/3");
1967 let nested4 = tmpdir.path().join("1/2/3/4");
1968 let nested5 = tmpdir.path().join("1/2/3/4/5");
1969 let nested6 = tmpdir.path().join("1/2/3/4/5/6");
1970 let nested7 = tmpdir.path().join("1/2/3/4/5/6/7");
1971 let nested8 = tmpdir.path().join("1/2/3/4/5/6/7/8");
1972 let nested9 = tmpdir.path().join("1/2/3/4/5/6/7/8/9");
1973
1974 let (mut watcher, mut rx) = watcher();
1975
1976 watcher.watch_recursively(&tmpdir);
1977
1978 std::fs::create_dir_all(&nested9).expect("create_dir_all");
1979 rx.wait_ordered([
1980 expected(&nested1).create_folder(),
1981 expected(&nested2).create_folder(),
1982 expected(&nested3).create_folder(),
1983 expected(&nested4).create_folder(),
1984 expected(&nested5).create_folder(),
1985 expected(&nested6).create_folder(),
1986 expected(&nested7).create_folder(),
1987 expected(&nested8).create_folder(),
1988 expected(&nested9).create_folder(),
1989 ]);
1990 assert_eq!(
1991 watcher.get_watch_handles(),
1992 HashSet::from([
1993 tmpdir.to_path_buf(),
1994 nested1,
1995 nested2,
1996 nested3,
1997 nested4,
1998 nested5,
1999 nested6,
2000 nested7,
2001 nested8,
2002 nested9
2003 ])
2004 );
2005 }
2006
2007 #[test]
2008 fn upgrade_to_recursive() {
2009 let tmpdir = testdir();
2010 let (mut watcher, mut rx) = watcher();
2011
2012 let path = tmpdir.path().join("upgrade");
2013 let deep = tmpdir.path().join("upgrade/deep");
2014 let file = tmpdir.path().join("upgrade/deep/file");
2015 std::fs::create_dir_all(&deep).expect("create_dir");
2016
2017 watcher.watch_nonrecursively(&path);
2018 std::fs::File::create_new(&file).expect("create");
2019 std::fs::remove_file(&file).expect("delete");
2020
2021 rx.ensure_empty_with_wait();
2022
2023 watcher.watch_recursively(&path);
2024 std::fs::File::create_new(&file).expect("create");
2025
2026 rx.wait_ordered([
2027 expected(&file).create_file(),
2028 expected(&file).access_open_any(),
2029 expected(&file).access_close_write(),
2030 ])
2031 .ensure_no_tail();
2032 assert_eq!(
2033 watcher.get_watch_handles(),
2034 HashSet::from([tmpdir.to_path_buf(), path, deep])
2035 );
2036 }
2037}