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