1use super::event::*;
8use super::{Config, Error, ErrorKind, EventHandler, RecursiveMode, Result, Watcher};
9use crate::bimap::BiHashMap;
10use crate::{BoundSender, Receiver, Sender, 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::io::AsRawFd;
19use std::path::{Path, PathBuf};
20use std::sync::Arc;
21use std::thread;
22use walkdir::WalkDir;
23
24const INOTIFY: mio::Token = mio::Token(0);
25const MESSAGE: mio::Token = mio::Token(1);
26
27struct EventLoop {
34 running: bool,
35 poll: mio::Poll,
36 event_loop_waker: Arc<mio::Waker>,
37 event_loop_tx: Sender<EventLoopMsg>,
38 event_loop_rx: Receiver<EventLoopMsg>,
39 inotify: Option<Inotify>,
40 event_handler: Box<dyn EventHandler>,
41 watches: HashMap<PathBuf, RecursiveMode>,
42 watch_handles: BiHashMap<WatchDescriptor, PathBuf, (bool, bool)>,
43 rename_event: Option<Event>,
44 follow_links: bool,
45}
46
47#[derive(Debug)]
49pub struct INotifyWatcher {
50 channel: Sender<EventLoopMsg>,
51 waker: Arc<mio::Waker>,
52}
53
54enum EventLoopMsg {
55 AddWatch(PathBuf, RecursiveMode, Sender<Result<()>>),
56 RemoveWatch(PathBuf, Sender<Result<()>>),
57 Shutdown,
58 Configure(Config, BoundSender<Result<bool>>),
59 #[cfg(test)]
60 GetWatchHandles(BoundSender<HashSet<PathBuf>>),
61}
62
63#[inline]
64fn add_watch_by_event(
65 path: &PathBuf,
66 is_dir: bool,
67 watches: &HashMap<PathBuf, RecursiveMode>,
68 add_watches: &mut Vec<(PathBuf, bool, bool)>,
69) {
70 if let Some(recursive_mode) = watches.get(path) {
71 add_watches.push((path.to_owned(), recursive_mode.is_recursive(), is_dir));
72 return;
73 }
74
75 let Some(parent) = path.parent() else {
76 return;
77 };
78 if let Some(recursive_mode) = watches.get(parent) {
79 add_watches.push((path.to_owned(), recursive_mode.is_recursive(), is_dir));
80 return;
81 }
82
83 while let Some(parent) = parent.parent() {
84 if let Some(RecursiveMode::Recursive) = watches.get(parent) {
85 add_watches.push((path.to_owned(), true, is_dir));
86 return;
87 }
88 }
89}
90
91#[inline]
92fn remove_watch_by_event(
93 path: &PathBuf,
94 watch_handles: &BiHashMap<WatchDescriptor, PathBuf, (bool, bool)>,
95 remove_watches: &mut Vec<PathBuf>,
96) {
97 if watch_handles.contains_right(path) {
98 remove_watches.push(path.to_owned());
99 }
100}
101
102impl EventLoop {
103 pub fn new(
104 inotify: Inotify,
105 event_handler: Box<dyn EventHandler>,
106 follow_links: bool,
107 ) -> Result<Self> {
108 let (event_loop_tx, event_loop_rx) = unbounded::<EventLoopMsg>();
109 let poll = mio::Poll::new()?;
110
111 let event_loop_waker = Arc::new(mio::Waker::new(poll.registry(), MESSAGE)?);
112
113 let inotify_fd = inotify.as_raw_fd();
114 let mut evented_inotify = mio::unix::SourceFd(&inotify_fd);
115 poll.registry()
116 .register(&mut evented_inotify, INOTIFY, mio::Interest::READABLE)?;
117
118 let event_loop = EventLoop {
119 running: true,
120 poll,
121 event_loop_waker,
122 event_loop_tx,
123 event_loop_rx,
124 inotify: Some(inotify),
125 event_handler,
126 watches: HashMap::new(),
127 watch_handles: BiHashMap::new(),
128 rename_event: None,
129 follow_links,
130 };
131 Ok(event_loop)
132 }
133
134 pub fn run(self) {
136 let _ = thread::Builder::new()
137 .name("notify-rs inotify loop".to_string())
138 .spawn(|| self.event_loop_thread());
139 }
140
141 fn event_loop_thread(mut self) {
142 let mut events = mio::Events::with_capacity(16);
143 loop {
144 match self.poll.poll(&mut events, None) {
146 Err(ref e) if matches!(e.kind(), std::io::ErrorKind::Interrupted) => {
147 }
150 Err(e) => panic!("poll failed: {}", e),
151 Ok(()) => {}
152 }
153
154 for event in &events {
156 self.handle_event(event);
157 }
158
159 if !self.running {
161 break;
162 }
163 }
164 }
165
166 fn handle_event(&mut self, event: &mio::event::Event) {
168 match event.token() {
169 MESSAGE => {
170 self.handle_messages()
172 }
173 INOTIFY => {
174 self.handle_inotify()
176 }
177 _ => unreachable!(),
178 }
179 }
180
181 fn handle_messages(&mut self) {
182 while let Ok(msg) = self.event_loop_rx.try_recv() {
183 match msg {
184 EventLoopMsg::AddWatch(path, recursive_mode, tx) => {
185 let _ = tx.send(self.add_watch(path, recursive_mode));
186 }
187 EventLoopMsg::RemoveWatch(path, tx) => {
188 let _ = tx.send(self.remove_watch(path));
189 }
190 EventLoopMsg::Shutdown => {
191 let _ = self.remove_all_watches();
192 if let Some(inotify) = self.inotify.take() {
193 let _ = inotify.close();
194 }
195 self.running = false;
196 break;
197 }
198 EventLoopMsg::Configure(config, tx) => {
199 self.configure_raw_mode(config, tx);
200 }
201 #[cfg(test)]
202 EventLoopMsg::GetWatchHandles(tx) => {
203 let handles: HashSet<PathBuf> = self
204 .watch_handles
205 .iter()
206 .map(|(_, path, _)| path.clone())
207 .collect();
208 tx.send(handles).unwrap();
209 }
210 }
211 }
212 }
213
214 fn configure_raw_mode(&mut self, _config: Config, tx: BoundSender<Result<bool>>) {
215 tx.send(Ok(false))
216 .expect("configuration channel disconnected");
217 }
218
219 fn handle_inotify(&mut self) {
220 let mut add_watches = Vec::new();
221 let mut remove_watches = Vec::new();
222
223 if let Some(ref mut inotify) = self.inotify {
224 let mut buffer = [0; 1024];
225 loop {
227 match inotify.read_events(&mut buffer) {
228 Ok(events) => {
229 let mut num_events = 0;
230 for event in events {
231 log::trace!("inotify event: {event:?}");
232
233 num_events += 1;
234 if event.mask.contains(EventMask::Q_OVERFLOW) {
235 let ev = Ok(Event::new(EventKind::Other).set_flag(Flag::Rescan));
236 self.event_handler.handle_event(ev);
237 }
238
239 let path = match event.name {
240 Some(name) => self
241 .watch_handles
242 .get_by_left(&event.wd)
243 .map(|(root, _)| root.join(name)),
244 None => self
245 .watch_handles
246 .get_by_left(&event.wd)
247 .map(|(root, _)| root.clone()),
248 };
249
250 let path = match path {
251 Some(path) => path,
252 None => {
253 log::debug!("inotify event with unknown descriptor: {event:?}");
254 continue;
255 }
256 };
257
258 let mut evs = Vec::new();
259
260 if event.mask.contains(EventMask::MOVED_FROM) {
261 remove_watch_by_event(
262 &path,
263 &self.watch_handles,
264 &mut remove_watches,
265 );
266
267 let event = Event::new(EventKind::Modify(ModifyKind::Name(
268 RenameMode::From,
269 )))
270 .add_path(path.clone())
271 .set_tracker(event.cookie as usize);
272
273 self.rename_event = Some(event.clone());
274
275 evs.push(event);
276 } else if event.mask.contains(EventMask::MOVED_TO) {
277 evs.push(
278 Event::new(EventKind::Modify(ModifyKind::Name(RenameMode::To)))
279 .set_tracker(event.cookie as usize)
280 .add_path(path.clone()),
281 );
282
283 let trackers_match =
284 self.rename_event.as_ref().and_then(|e| e.tracker())
285 == Some(event.cookie as usize);
286
287 if trackers_match {
288 let rename_event = self.rename_event.take().unwrap(); evs.push(
290 Event::new(EventKind::Modify(ModifyKind::Name(
291 RenameMode::Both,
292 )))
293 .set_tracker(event.cookie as usize)
294 .add_some_path(rename_event.paths.first().cloned())
295 .add_path(path.clone()),
296 );
297 }
298 let is_dir = event.mask.contains(EventMask::ISDIR);
299 add_watch_by_event(&path, is_dir, &self.watches, &mut add_watches);
300 }
301 if event.mask.contains(EventMask::MOVE_SELF) {
302 evs.push(
303 Event::new(EventKind::Modify(ModifyKind::Name(
304 RenameMode::From,
305 )))
306 .add_path(path.clone()),
307 );
308 }
312 if event.mask.contains(EventMask::CREATE) {
313 let is_dir = event.mask.contains(EventMask::ISDIR);
314 evs.push(
315 Event::new(EventKind::Create(if is_dir {
316 CreateKind::Folder
317 } else {
318 CreateKind::File
319 }))
320 .add_path(path.clone()),
321 );
322 add_watch_by_event(&path, is_dir, &self.watches, &mut add_watches);
323 }
324 if event.mask.contains(EventMask::DELETE) {
325 evs.push(
326 Event::new(EventKind::Remove(
327 if event.mask.contains(EventMask::ISDIR) {
328 RemoveKind::Folder
329 } else {
330 RemoveKind::File
331 },
332 ))
333 .add_path(path.clone()),
334 );
335 remove_watch_by_event(
336 &path,
337 &self.watch_handles,
338 &mut remove_watches,
339 );
340 }
341 if event.mask.contains(EventMask::DELETE_SELF) {
342 let remove_kind = match self.watch_handles.get_by_right(&path) {
343 Some((_, (_, true))) => RemoveKind::Folder,
344 Some((_, (_, false))) => RemoveKind::File,
345 None => RemoveKind::Other,
346 };
347 evs.push(
348 Event::new(EventKind::Remove(remove_kind))
349 .add_path(path.clone()),
350 );
351 remove_watch_by_event(
352 &path,
353 &self.watch_handles,
354 &mut remove_watches,
355 );
356 }
357 if event.mask.contains(EventMask::MODIFY) {
358 evs.push(
359 Event::new(EventKind::Modify(ModifyKind::Data(
360 DataChange::Any,
361 )))
362 .add_path(path.clone()),
363 );
364 }
365 if event.mask.contains(EventMask::CLOSE_WRITE) {
366 evs.push(
367 Event::new(EventKind::Access(AccessKind::Close(
368 AccessMode::Write,
369 )))
370 .add_path(path.clone()),
371 );
372 }
373 if event.mask.contains(EventMask::CLOSE_NOWRITE) {
374 evs.push(
375 Event::new(EventKind::Access(AccessKind::Close(
376 AccessMode::Read,
377 )))
378 .add_path(path.clone()),
379 );
380 }
381 if event.mask.contains(EventMask::ATTRIB) {
382 evs.push(
383 Event::new(EventKind::Modify(ModifyKind::Metadata(
384 MetadataKind::Any,
385 )))
386 .add_path(path.clone()),
387 );
388 }
389 if event.mask.contains(EventMask::OPEN) {
390 evs.push(
391 Event::new(EventKind::Access(AccessKind::Open(
392 AccessMode::Any,
393 )))
394 .add_path(path.clone()),
395 );
396 }
397
398 for ev in evs {
399 self.event_handler.handle_event(Ok(ev));
400 }
401 }
402
403 if num_events == 0 {
405 break;
406 }
407 }
408 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
409 break;
411 }
412 Err(e) => {
413 self.event_handler.handle_event(Err(Error::io(e)));
414 }
415 }
416 }
417 }
418
419 for path in remove_watches {
420 self.watches.remove(&path);
421 self.remove_maybe_recursive_watch(path, true).ok();
422 }
423
424 for (path, is_recursive, is_dir) in add_watches {
425 if let Err(add_watch_error) =
426 self.add_maybe_recursive_watch(path, is_recursive, is_dir, false)
427 {
428 if let ErrorKind::MaxFilesWatch = add_watch_error.kind {
432 self.event_handler.handle_event(Err(add_watch_error));
433
434 break;
438 }
439 }
440 }
441 }
442
443 fn add_watch(&mut self, path: PathBuf, recursive_mode: RecursiveMode) -> Result<()> {
444 if let Some(existing) = self.watches.get(&path) {
445 let need_upgrade_to_recursive = match *existing {
446 RecursiveMode::Recursive => false,
447 RecursiveMode::NonRecursive => recursive_mode == RecursiveMode::Recursive,
448 };
449 if !need_upgrade_to_recursive {
450 return Ok(());
451 }
452
453 if metadata(&path).map_err(Error::io)?.is_dir() {
455 self.add_maybe_recursive_watch(path.clone(), true, true, true)?;
456 }
457 *self.watches.get_mut(&path).unwrap() = RecursiveMode::Recursive;
458 return Ok(());
459 }
460
461 let is_dir = metadata(&path).map_err(Error::io_watch)?.is_dir();
462 self.add_maybe_recursive_watch(
463 path.clone(),
464 recursive_mode.is_recursive() && is_dir,
467 is_dir,
468 true,
469 )?;
470
471 self.watches.insert(path, recursive_mode);
472
473 Ok(())
474 }
475
476 fn add_maybe_recursive_watch(
477 &mut self,
478 path: PathBuf,
479 is_recursive: bool,
480 is_dir: bool,
481 mut watch_self: bool,
482 ) -> Result<()> {
483 if is_recursive {
484 for entry in WalkDir::new(&path)
485 .follow_links(self.follow_links)
486 .into_iter()
487 .filter_map(filter_dir)
488 {
489 self.add_single_watch(entry.into_path(), true, watch_self)?;
490 watch_self = false;
491 }
492 } else {
493 self.add_single_watch(path.clone(), is_dir, watch_self)?;
494 }
495 Ok(())
496 }
497
498 fn add_single_watch(&mut self, path: PathBuf, is_dir: bool, watch_self: bool) -> Result<()> {
499 if let Some((_, &(old_watch_self, _))) = self.watch_handles.get_by_right(&path)
500 && (old_watch_self || !watch_self)
502 {
503 return Ok(());
504 }
505
506 if !is_dir
507 && let Some(parent) = path.parent()
508 && self.watch_handles.get_by_right(parent).is_some()
509 {
510 return Ok(());
511 }
512
513 let mut watchmask = WatchMask::ATTRIB
514 | WatchMask::CREATE
515 | WatchMask::OPEN
516 | WatchMask::DELETE
517 | WatchMask::CLOSE_WRITE
518 | WatchMask::MODIFY
519 | WatchMask::MOVED_FROM
520 | WatchMask::MOVED_TO;
521 if watch_self {
522 watchmask.insert(WatchMask::DELETE_SELF);
523 watchmask.insert(WatchMask::MOVE_SELF);
524 }
525
526 if let Some(ref mut inotify) = self.inotify {
527 log::trace!("adding inotify watch: {}", path.display());
528
529 match inotify.watches().add(&path, watchmask) {
530 Err(e) => {
531 Err(if e.raw_os_error() == Some(libc::ENOSPC) {
532 Error::new(ErrorKind::MaxFilesWatch)
534 } else if e.kind() == std::io::ErrorKind::NotFound {
535 Error::new(ErrorKind::PathNotFound)
536 } else {
537 Error::io(e)
538 }
539 .add_path(path))
540 }
541 Ok(w) => {
542 watchmask.remove(WatchMask::MASK_ADD);
543 let is_dir = metadata(&path).map_err(Error::io)?.is_dir();
544 self.watch_handles.insert(w, path, (watch_self, is_dir));
545 Ok(())
546 }
547 }
548 } else {
549 Ok(())
550 }
551 }
552
553 fn remove_watch(&mut self, path: PathBuf) -> Result<()> {
554 match self.watches.remove(&path) {
555 None => return Err(Error::watch_not_found().add_path(path)),
556 Some(recursive_mode) => {
557 self.remove_maybe_recursive_watch(path, recursive_mode.is_recursive())?;
558 }
559 }
560 Ok(())
561 }
562
563 fn remove_maybe_recursive_watch(&mut self, path: PathBuf, is_recursive: bool) -> Result<()> {
564 let Some(ref mut inotify) = self.inotify else {
565 return Ok(());
566 };
567 let mut inotify_watches = inotify.watches();
568
569 log::trace!("removing inotify watch: {}", path.display());
570
571 if let Some((handle, _)) = self.watch_handles.remove_by_right(&path) {
572 inotify_watches
573 .remove(handle.clone())
574 .map_err(|e| Error::io(e).add_path(path.clone()))?;
575 }
576
577 if is_recursive {
578 let mut remove_list = Vec::new();
579 for (w, p, _) in &self.watch_handles {
580 if p.starts_with(&path) {
581 inotify_watches
582 .remove(w.clone())
583 .map_err(|e| Error::io(e).add_path(p.into()))?;
584 remove_list.push(w.clone());
585 }
586 }
587 for w in remove_list {
588 self.watch_handles.remove_by_left(&w);
589 }
590 }
591 Ok(())
592 }
593
594 fn remove_all_watches(&mut self) -> Result<()> {
595 if let Some(ref mut inotify) = self.inotify {
596 let mut inotify_watches = inotify.watches();
597 for (w, p, _) in &self.watch_handles {
598 inotify_watches
599 .remove(w.clone())
600 .map_err(|e| Error::io(e).add_path(p.into()))?;
601 }
602 self.watch_handles.clear();
603 self.watches.clear();
604 }
605 Ok(())
606 }
607}
608
609fn filter_dir(e: walkdir::Result<walkdir::DirEntry>) -> Option<walkdir::DirEntry> {
611 if let Ok(e) = e
612 && let Ok(metadata) = e.metadata()
613 && metadata.is_dir()
614 {
615 return Some(e);
616 }
617 None
618}
619
620impl INotifyWatcher {
621 fn from_event_handler(
622 event_handler: Box<dyn EventHandler>,
623 follow_links: bool,
624 ) -> Result<Self> {
625 let inotify = Inotify::init()?;
626 let event_loop = EventLoop::new(inotify, event_handler, follow_links)?;
627 let channel = event_loop.event_loop_tx.clone();
628 let waker = event_loop.event_loop_waker.clone();
629 event_loop.run();
630 Ok(INotifyWatcher { channel, waker })
631 }
632
633 fn watch_inner(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
634 let pb = if path.is_absolute() {
635 path.to_owned()
636 } else {
637 let p = env::current_dir().map_err(Error::io)?;
638 p.join(path)
639 };
640 let (tx, rx) = unbounded();
641 let msg = EventLoopMsg::AddWatch(pb, recursive_mode, tx);
642
643 self.channel.send(msg).unwrap();
645 self.waker.wake().unwrap();
646 rx.recv().unwrap()
647 }
648
649 fn unwatch_inner(&mut self, path: &Path) -> Result<()> {
650 let pb = if path.is_absolute() {
651 path.to_owned()
652 } else {
653 let p = env::current_dir().map_err(Error::io)?;
654 p.join(path)
655 };
656 let (tx, rx) = unbounded();
657 let msg = EventLoopMsg::RemoveWatch(pb, tx);
658
659 self.channel.send(msg).unwrap();
661 self.waker.wake().unwrap();
662 rx.recv().unwrap()
663 }
664}
665
666impl Watcher for INotifyWatcher {
667 fn new<F: EventHandler>(event_handler: F, config: Config) -> Result<Self> {
669 Self::from_event_handler(Box::new(event_handler), config.follow_symlinks())
670 }
671
672 fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
673 self.watch_inner(path, recursive_mode)
674 }
675
676 fn unwatch(&mut self, path: &Path) -> Result<()> {
677 self.unwatch_inner(path)
678 }
679
680 fn configure(&mut self, config: Config) -> Result<bool> {
681 let (tx, rx) = bounded(1);
682 self.channel.send(EventLoopMsg::Configure(config, tx))?;
683 self.waker.wake()?;
684 rx.recv()?
685 }
686
687 fn kind() -> crate::WatcherKind {
688 crate::WatcherKind::Inotify
689 }
690
691 #[cfg(test)]
692 fn get_watch_handles(&self) -> std::collections::HashSet<std::path::PathBuf> {
693 let (tx, rx) = bounded(1);
694 self.channel
695 .send(EventLoopMsg::GetWatchHandles(tx))
696 .unwrap();
697 self.waker.wake().unwrap();
698 rx.recv().unwrap()
699 }
700}
701
702impl Drop for INotifyWatcher {
703 fn drop(&mut self) {
704 self.channel.send(EventLoopMsg::Shutdown).unwrap();
706 self.waker.wake().unwrap();
707 }
708}
709
710#[cfg(test)]
711mod tests {
712 use std::{
713 collections::HashSet,
714 path::{Path, PathBuf},
715 sync::{Arc, atomic::AtomicBool, mpsc},
716 thread::{self, available_parallelism},
717 time::Duration,
718 };
719
720 use super::{Config, Error, ErrorKind, Event, INotifyWatcher, RecursiveMode, Result, Watcher};
721
722 use crate::test::*;
723
724 fn watcher() -> (TestWatcher<INotifyWatcher>, Receiver) {
725 channel()
726 }
727
728 #[test]
729 fn inotify_watcher_is_send_and_sync() {
730 fn check<T: Send + Sync>() {}
731 check::<INotifyWatcher>();
732 }
733
734 #[test]
735 fn native_error_type_on_missing_path() {
736 let mut watcher = INotifyWatcher::new(|_| {}, Config::default()).unwrap();
737
738 let result = watcher.watch(
739 &PathBuf::from("/some/non/existant/path"),
740 RecursiveMode::NonRecursive,
741 );
742
743 assert!(matches!(
744 result,
745 Err(Error {
746 paths: _,
747 kind: ErrorKind::PathNotFound
748 })
749 ))
750 }
751
752 #[test]
760 #[ignore = "requires changing sysctl fs.inotify.max_user_watches while test is running"]
761 fn recursive_watch_calls_handler_if_creating_a_file_raises_max_files_watch() {
762 use std::time::Duration;
763
764 let tmpdir = tempfile::tempdir().unwrap();
765 let (tx, rx) = std::sync::mpsc::channel();
766 let (proc_changed_tx, proc_changed_rx) = std::sync::mpsc::channel();
767 let proc_path = Path::new("/proc/sys/fs/inotify/max_user_watches");
768 let mut watcher = INotifyWatcher::new(
769 move |result: Result<Event>| match result {
770 Ok(event) => {
771 if event.paths.first().is_some_and(|path| path == proc_path) {
772 proc_changed_tx.send(()).unwrap();
773 }
774 }
775 Err(e) => tx.send(e).unwrap(),
776 },
777 Config::default(),
778 )
779 .unwrap();
780
781 watcher
782 .watch(tmpdir.path(), RecursiveMode::Recursive)
783 .unwrap();
784 watcher
785 .watch(proc_path, RecursiveMode::NonRecursive)
786 .unwrap();
787
788 proc_changed_rx
790 .recv_timeout(Duration::from_secs(30))
791 .unwrap();
792
793 let child_dir = tmpdir.path().join("child");
794 std::fs::create_dir(child_dir).unwrap();
795
796 let result = rx.recv_timeout(Duration::from_millis(500));
797
798 assert!(
799 matches!(
800 &result,
801 Ok(Error {
802 kind: ErrorKind::MaxFilesWatch,
803 paths: _,
804 })
805 ),
806 "expected {:?}, found: {:#?}",
807 ErrorKind::MaxFilesWatch,
808 result
809 );
810 }
811
812 #[test]
814 fn race_condition_on_unwatch_and_pending_events_with_deleted_descriptor() {
815 let tmpdir = tempfile::tempdir().expect("tmpdir");
816 let (tx, rx) = mpsc::channel();
817 let mut inotify = INotifyWatcher::new(
818 move |e: Result<Event>| {
819 let e = match e {
820 Ok(e) if e.paths.is_empty() => e,
821 Ok(_) | Err(_) => return,
822 };
823 let _ = tx.send(e);
824 },
825 Config::default(),
826 )
827 .expect("inotify creation");
828
829 let dir_path = tmpdir.path();
830 let file_path = dir_path.join("foo");
831 std::fs::File::create(&file_path).unwrap();
832
833 let stop = Arc::new(AtomicBool::new(false));
834
835 let handles: Vec<_> = (0..available_parallelism().unwrap().get().max(4))
836 .map(|_| {
837 let file_path = file_path.clone();
838 let stop = stop.clone();
839 thread::spawn(move || {
840 while !stop.load(std::sync::atomic::Ordering::Relaxed) {
841 let _ = std::fs::File::open(&file_path).unwrap();
842 }
843 })
844 })
845 .collect();
846
847 let non_recursive = RecursiveMode::NonRecursive;
848 for _ in 0..(handles.len() * 4) {
849 inotify.watch(dir_path, non_recursive).unwrap();
850 inotify.unwatch(dir_path).unwrap();
851 }
852
853 stop.store(true, std::sync::atomic::Ordering::Relaxed);
854 handles
855 .into_iter()
856 .for_each(|handle| handle.join().ok().unwrap_or_default());
857
858 drop(inotify);
859
860 let events: Vec<_> = rx.into_iter().map(|e| format!("{e:?}")).collect();
861
862 const LOG_LEN: usize = 10;
863 let events_len = events.len();
864 assert!(
865 events.is_empty(),
866 "expected no events without path, but got {events_len}. first 10: {:#?}",
867 &events[..LOG_LEN.min(events_len)]
868 );
869 }
870
871 #[test]
872 fn create_file() {
873 let tmpdir = testdir();
874 let (mut watcher, mut rx) = watcher();
875 watcher.watch_recursively(&tmpdir);
876
877 let path = tmpdir.path().join("entry");
878 std::fs::File::create_new(&path).expect("create");
879
880 rx.wait_ordered_exact([
881 expected(&path).create_file(),
882 expected(&path).access_open_any(),
883 expected(&path).access_close_write(),
884 ]);
885 assert_eq!(
886 watcher.get_watch_handles(),
887 HashSet::from([tmpdir.to_path_buf()])
888 );
889 }
890
891 #[test]
892 fn write_file() {
893 let tmpdir = testdir();
894 let (mut watcher, mut rx) = watcher();
895
896 let path = tmpdir.path().join("entry");
897 std::fs::File::create_new(&path).expect("create");
898
899 watcher.watch_recursively(&tmpdir);
900 std::fs::write(&path, b"123").expect("write");
901
902 rx.wait_ordered_exact([
903 expected(&path).access_open_any(),
904 expected(&path).modify_data_any().multiple(),
905 expected(&path).access_close_write(),
906 ])
907 .ensure_no_tail();
908 assert_eq!(
909 watcher.get_watch_handles(),
910 HashSet::from([tmpdir.to_path_buf()])
911 );
912 }
913
914 #[test]
915 fn chmod_file() {
916 let tmpdir = testdir();
917 let (mut watcher, mut rx) = watcher();
918
919 let path = tmpdir.path().join("entry");
920 let file = std::fs::File::create_new(&path).expect("create");
921 let mut permissions = file.metadata().expect("metadata").permissions();
922 permissions.set_readonly(true);
923
924 watcher.watch_recursively(&tmpdir);
925 file.set_permissions(permissions).expect("set_permissions");
926
927 rx.wait_ordered_exact([expected(&path).modify_meta_any()]);
928 assert_eq!(
929 watcher.get_watch_handles(),
930 HashSet::from([tmpdir.to_path_buf()])
931 );
932 }
933
934 #[test]
935 fn rename_file() {
936 let tmpdir = testdir();
937 let (mut watcher, mut rx) = watcher();
938
939 let path = tmpdir.path().join("entry");
940 std::fs::File::create_new(&path).expect("create");
941
942 watcher.watch_recursively(&tmpdir);
943 let new_path = tmpdir.path().join("renamed");
944
945 std::fs::rename(&path, &new_path).expect("rename");
946
947 rx.wait_ordered_exact([
948 expected(&path).rename_from(),
949 expected(&new_path).rename_to(),
950 expected([path, new_path]).rename_both(),
951 ])
952 .ensure_trackers_len(1)
953 .ensure_no_tail();
954 assert_eq!(
955 watcher.get_watch_handles(),
956 HashSet::from([tmpdir.to_path_buf()])
957 );
958 }
959
960 #[test]
961 fn delete_file() {
962 let tmpdir = testdir();
963 let (mut watcher, mut rx) = watcher();
964 let file = tmpdir.path().join("file");
965 std::fs::write(&file, "").expect("write");
966
967 watcher.watch_nonrecursively(&tmpdir);
968
969 std::fs::remove_file(&file).expect("remove");
970
971 rx.wait_ordered_exact([expected(&file).remove_file()]);
972 assert_eq!(
973 watcher.get_watch_handles(),
974 HashSet::from([tmpdir.to_path_buf()])
975 );
976 }
977
978 #[test]
979 fn delete_self_file() {
980 let tmpdir = testdir();
981 let (mut watcher, mut rx) = watcher();
982 let file = tmpdir.path().join("file");
983 std::fs::write(&file, "").expect("write");
984
985 watcher.watch_nonrecursively(&file);
986
987 std::fs::remove_file(&file).expect("remove");
988
989 rx.wait_ordered_exact([
990 expected(&file).modify_meta_any(),
991 expected(&file).remove_file(),
992 ]);
993 assert_eq!(watcher.get_watch_handles(), HashSet::from([]));
994 }
995
996 #[test]
997 fn create_write_overwrite() {
998 let tmpdir = testdir();
999 let (mut watcher, mut rx) = watcher();
1000 let overwritten_file = tmpdir.path().join("overwritten_file");
1001 let overwriting_file = tmpdir.path().join("overwriting_file");
1002 std::fs::write(&overwritten_file, "123").expect("write1");
1003
1004 watcher.watch_nonrecursively(&tmpdir);
1005
1006 std::fs::File::create(&overwriting_file).expect("create");
1007 std::fs::write(&overwriting_file, "321").expect("write2");
1008 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
1009
1010 rx.wait_ordered_exact([
1011 expected(&overwriting_file).create_file(),
1012 expected(&overwriting_file).access_open_any(),
1013 expected(&overwriting_file).access_close_write(),
1014 expected(&overwriting_file).access_open_any(),
1015 expected(&overwriting_file).modify_data_any().multiple(),
1016 expected(&overwriting_file).access_close_write().multiple(),
1017 expected(&overwriting_file).rename_from(),
1018 expected(&overwritten_file).rename_to(),
1019 expected([&overwriting_file, &overwritten_file]).rename_both(),
1020 ])
1021 .ensure_no_tail()
1022 .ensure_trackers_len(1);
1023 assert_eq!(
1024 watcher.get_watch_handles(),
1025 HashSet::from([tmpdir.to_path_buf()])
1026 );
1027 }
1028
1029 #[test]
1030 fn create_dir() {
1031 let tmpdir = testdir();
1032 let (mut watcher, mut rx) = watcher();
1033 watcher.watch_recursively(&tmpdir);
1034
1035 let path = tmpdir.path().join("entry");
1036 std::fs::create_dir(&path).expect("create");
1037
1038 rx.wait_ordered_exact([expected(&path).create_folder()]);
1039 assert_eq!(
1040 watcher.get_watch_handles(),
1041 HashSet::from([tmpdir.to_path_buf(), path])
1042 );
1043 }
1044
1045 #[test]
1046 fn chmod_dir() {
1047 let tmpdir = testdir();
1048 let (mut watcher, mut rx) = watcher();
1049
1050 let path = tmpdir.path().join("entry");
1051 std::fs::create_dir(&path).expect("create_dir");
1052 let mut permissions = std::fs::metadata(&path).expect("metadata").permissions();
1053 permissions.set_readonly(true);
1054
1055 watcher.watch_recursively(&tmpdir);
1056 std::fs::set_permissions(&path, permissions).expect("set_permissions");
1057
1058 rx.wait_ordered_exact([
1059 expected(&path).access_open_any().optional(),
1060 expected(&path).modify_meta_any(),
1061 expected(&path).modify_meta_any(),
1062 ])
1063 .ensure_no_tail();
1064 assert_eq!(
1065 watcher.get_watch_handles(),
1066 HashSet::from([tmpdir.to_path_buf(), path])
1067 );
1068 }
1069
1070 #[test]
1071 fn rename_dir() {
1072 let tmpdir = testdir();
1073 let (mut watcher, mut rx) = watcher();
1074
1075 let path = tmpdir.path().join("entry");
1076 let new_path = tmpdir.path().join("new_path");
1077 std::fs::create_dir(&path).expect("create_dir");
1078
1079 watcher.watch_recursively(&tmpdir);
1080
1081 std::fs::rename(&path, &new_path).expect("rename");
1082
1083 rx.wait_ordered_exact([
1084 expected(&path).access_open_any().optional(),
1085 expected(&path).rename_from(),
1086 expected(&new_path).rename_to(),
1087 expected([&path, &new_path]).rename_both(),
1088 ])
1089 .ensure_trackers_len(1);
1090 assert_eq!(
1091 watcher.get_watch_handles(),
1092 HashSet::from([tmpdir.to_path_buf(), new_path])
1093 );
1094 }
1095
1096 #[test]
1097 fn delete_dir() {
1098 let tmpdir = testdir();
1099 let (mut watcher, mut rx) = watcher();
1100
1101 let path = tmpdir.path().join("entry");
1102 std::fs::create_dir(&path).expect("create_dir");
1103
1104 watcher.watch_recursively(&tmpdir);
1105 std::fs::remove_dir(&path).expect("remove");
1106
1107 rx.wait_ordered_exact([
1108 expected(&path).access_open_any().optional(),
1109 expected(&path).remove_folder(),
1110 ])
1111 .ensure_no_tail();
1112 assert_eq!(
1113 watcher.get_watch_handles(),
1114 HashSet::from([tmpdir.to_path_buf()])
1115 );
1116 }
1117
1118 #[test]
1119 fn rename_dir_twice() {
1120 let tmpdir = testdir();
1121 let (mut watcher, mut rx) = watcher();
1122
1123 let path = tmpdir.path().join("entry");
1124 let new_path = tmpdir.path().join("new_path");
1125 let new_path2 = tmpdir.path().join("new_path2");
1126 std::fs::create_dir(&path).expect("create_dir");
1127
1128 watcher.watch_recursively(&tmpdir);
1129 std::fs::rename(&path, &new_path).expect("rename");
1130 std::fs::rename(&new_path, &new_path2).expect("rename2");
1131
1132 rx.wait_ordered_exact([
1133 expected(&path).access_open_any().optional(),
1134 expected(&path).rename_from(),
1135 expected(&new_path).rename_to(),
1136 expected([&path, &new_path]).rename_both(),
1137 expected(&new_path).access_open_any().optional(),
1138 expected(&new_path).rename_from(),
1139 expected(&new_path2).rename_to(),
1140 expected([&new_path, &new_path2]).rename_both(),
1141 ])
1142 .ensure_trackers_len(2);
1143 assert_eq!(
1144 watcher.get_watch_handles(),
1145 HashSet::from([tmpdir.to_path_buf(), new_path2])
1146 );
1147 }
1148
1149 #[test]
1150 fn move_out_of_watched_dir() {
1151 let tmpdir = testdir();
1152 let subdir = tmpdir.path().join("subdir");
1153 let (mut watcher, mut rx) = watcher();
1154
1155 let path = subdir.join("entry");
1156 std::fs::create_dir_all(&subdir).expect("create_dir_all");
1157 std::fs::File::create_new(&path).expect("create");
1158
1159 watcher.watch_recursively(&subdir);
1160 let new_path = tmpdir.path().join("entry");
1161
1162 std::fs::rename(&path, &new_path).expect("rename");
1163
1164 let event = rx.recv();
1165 let tracker = event.attrs.tracker();
1166 assert_eq!(event, expected(path).rename_from());
1167 assert!(tracker.is_some(), "tracker is none: [event:#?]");
1168 rx.ensure_empty();
1169 assert_eq!(watcher.get_watch_handles(), HashSet::from([subdir]));
1170 }
1171
1172 #[test]
1173 fn create_write_write_rename_write_remove() {
1174 let tmpdir = testdir();
1175 let (mut watcher, mut rx) = watcher();
1176
1177 let file1 = tmpdir.path().join("entry");
1178 let file2 = tmpdir.path().join("entry2");
1179 std::fs::File::create_new(&file2).expect("create file2");
1180 let new_path = tmpdir.path().join("renamed");
1181
1182 watcher.watch_recursively(&tmpdir);
1183 std::fs::write(&file1, "123").expect("write 1");
1184 std::fs::write(&file2, "321").expect("write 2");
1185 std::fs::rename(&file1, &new_path).expect("rename");
1186 std::fs::write(&new_path, b"1").expect("write 3");
1187 std::fs::remove_file(&new_path).expect("remove");
1188
1189 rx.wait_ordered_exact([
1190 expected(&file1).create_file(),
1191 expected(&file1).access_open_any(),
1192 expected(&file1).modify_data_any().multiple(),
1193 expected(&file1).access_close_write(),
1194 expected(&file2).access_open_any(),
1195 expected(&file2).modify_data_any().multiple(),
1196 expected(&file2).access_close_write(),
1197 expected(&file1).access_open_any().optional(),
1198 expected(&file1).rename_from(),
1199 expected(&new_path).rename_to(),
1200 expected([&file1, &new_path]).rename_both(),
1201 expected(&new_path).access_open_any(),
1202 expected(&new_path).modify_data_any().multiple(),
1203 expected(&new_path).access_close_write(),
1204 expected(&new_path).remove_file(),
1205 ]);
1206 assert_eq!(
1207 watcher.get_watch_handles(),
1208 HashSet::from([tmpdir.to_path_buf()])
1209 );
1210 }
1211
1212 #[test]
1213 fn rename_twice() {
1214 let tmpdir = testdir();
1215 let (mut watcher, mut rx) = watcher();
1216
1217 let path = tmpdir.path().join("entry");
1218 std::fs::File::create_new(&path).expect("create");
1219
1220 watcher.watch_recursively(&tmpdir);
1221 let new_path1 = tmpdir.path().join("renamed1");
1222 let new_path2 = tmpdir.path().join("renamed2");
1223
1224 std::fs::rename(&path, &new_path1).expect("rename1");
1225 std::fs::rename(&new_path1, &new_path2).expect("rename2");
1226
1227 rx.wait_ordered_exact([
1228 expected(&path).access_open_any().optional(),
1229 expected(&path).rename_from(),
1230 expected(&new_path1).rename_to(),
1231 expected([&path, &new_path1]).rename_both(),
1232 expected(&new_path1).access_open_any().optional(),
1233 expected(&new_path1).rename_from(),
1234 expected(&new_path2).rename_to(),
1235 expected([&new_path1, &new_path2]).rename_both(),
1236 ])
1237 .ensure_no_tail()
1238 .ensure_trackers_len(2);
1239 assert_eq!(
1240 watcher.get_watch_handles(),
1241 HashSet::from([tmpdir.to_path_buf()])
1242 );
1243 }
1244
1245 #[test]
1246 fn set_file_mtime() {
1247 let tmpdir = testdir();
1248 let (mut watcher, mut rx) = watcher();
1249
1250 let path = tmpdir.path().join("entry");
1251 let file = std::fs::File::create_new(&path).expect("create");
1252
1253 watcher.watch_recursively(&tmpdir);
1254
1255 file.set_modified(
1256 std::time::SystemTime::now()
1257 .checked_sub(Duration::from_secs(60 * 60))
1258 .expect("time"),
1259 )
1260 .expect("set_time");
1261
1262 assert_eq!(rx.recv(), expected(&path).modify_data_any());
1263 rx.ensure_empty();
1264 assert_eq!(
1265 watcher.get_watch_handles(),
1266 HashSet::from([tmpdir.to_path_buf()])
1267 );
1268 }
1269
1270 #[test]
1271 fn write_file_non_recursive_watch() {
1272 let tmpdir = testdir();
1273 let (mut watcher, mut rx) = watcher();
1274
1275 let path = tmpdir.path().join("entry");
1276 std::fs::File::create_new(&path).expect("create");
1277
1278 watcher.watch_nonrecursively(&path);
1279
1280 std::fs::write(&path, b"123").expect("write");
1281
1282 rx.wait_ordered_exact([
1283 expected(&path).access_open_any(),
1284 expected(&path).modify_data_any().multiple(),
1285 expected(&path).access_close_write(),
1286 ])
1287 .ensure_no_tail();
1288 assert_eq!(watcher.get_watch_handles(), HashSet::from([path]));
1289 }
1290
1291 #[test]
1292 fn watch_recursively_then_unwatch_child_stops_events_from_child() {
1293 let tmpdir = testdir();
1294 let (mut watcher, mut rx) = watcher();
1295
1296 let subdir = tmpdir.path().join("subdir");
1297 let file = subdir.join("file");
1298 std::fs::create_dir(&subdir).expect("create");
1299
1300 watcher.watch_recursively(&tmpdir);
1301
1302 std::fs::File::create(&file).expect("create");
1303
1304 rx.wait_ordered_exact([
1305 expected(&subdir).access_open_any().optional(),
1306 expected(&file).create_file(),
1307 expected(&file).access_open_any(),
1308 expected(&file).access_close_write(),
1309 ])
1310 .ensure_no_tail();
1311 assert_eq!(
1312 watcher.get_watch_handles(),
1313 HashSet::from([tmpdir.to_path_buf(), subdir])
1314 );
1315
1316 }
1329
1330 #[test]
1331 fn write_to_a_hardlink_pointed_to_the_watched_file_triggers_an_event() {
1332 let tmpdir = testdir();
1333 let (mut watcher, mut rx) = watcher();
1334
1335 let subdir = tmpdir.path().join("subdir");
1336 let file = subdir.join("file");
1337 let hardlink = tmpdir.path().join("hardlink");
1338
1339 std::fs::create_dir(&subdir).expect("create");
1340 std::fs::write(&file, "").expect("file");
1341 std::fs::hard_link(&file, &hardlink).expect("hardlink");
1342
1343 watcher.watch_nonrecursively(&file);
1344
1345 std::fs::write(&hardlink, "123123").expect("write to the hard link");
1346
1347 rx.wait_ordered_exact([
1348 expected(&file).access_open_any(),
1349 expected(&file).modify_data_any().multiple(),
1350 expected(&file).access_close_write(),
1351 ]);
1352 assert_eq!(watcher.get_watch_handles(), HashSet::from([file]));
1353 }
1354
1355 #[test]
1356 fn write_to_a_hardlink_pointed_to_the_file_in_the_watched_dir_doesnt_trigger_an_event() {
1357 let tmpdir = testdir();
1358 let (mut watcher, mut rx) = watcher();
1359
1360 let subdir = tmpdir.path().join("subdir");
1361 let file = subdir.join("file");
1362 let hardlink = tmpdir.path().join("hardlink");
1363
1364 std::fs::create_dir(&subdir).expect("create");
1365 std::fs::write(&file, "").expect("file");
1366 std::fs::hard_link(&file, &hardlink).expect("hardlink");
1367
1368 watcher.watch_nonrecursively(&subdir);
1369
1370 std::fs::write(&hardlink, "123123").expect("write to the hard link");
1371
1372 let events = rx.iter().collect::<Vec<_>>();
1373 assert!(events.is_empty(), "unexpected events: {events:#?}");
1374 assert_eq!(watcher.get_watch_handles(), HashSet::from([subdir]));
1375 }
1376
1377 #[test]
1378 #[ignore = "see https://github.com/notify-rs/notify/issues/727"]
1379 fn recursive_creation() {
1380 let tmpdir = testdir();
1381 let nested1 = tmpdir.path().join("1");
1382 let nested2 = tmpdir.path().join("1/2");
1383 let nested3 = tmpdir.path().join("1/2/3");
1384 let nested4 = tmpdir.path().join("1/2/3/4");
1385 let nested5 = tmpdir.path().join("1/2/3/4/5");
1386 let nested6 = tmpdir.path().join("1/2/3/4/5/6");
1387 let nested7 = tmpdir.path().join("1/2/3/4/5/6/7");
1388 let nested8 = tmpdir.path().join("1/2/3/4/5/6/7/8");
1389 let nested9 = tmpdir.path().join("1/2/3/4/5/6/7/8/9");
1390
1391 let (mut watcher, mut rx) = watcher();
1392
1393 watcher.watch_recursively(&tmpdir);
1394
1395 std::fs::create_dir_all(&nested9).expect("create_dir_all");
1396 rx.wait_ordered([
1397 expected(&nested1).create_folder(),
1398 expected(&nested2).create_folder(),
1399 expected(&nested3).create_folder(),
1400 expected(&nested4).create_folder(),
1401 expected(&nested5).create_folder(),
1402 expected(&nested6).create_folder(),
1403 expected(&nested7).create_folder(),
1404 expected(&nested8).create_folder(),
1405 expected(&nested9).create_folder(),
1406 ]);
1407 assert_eq!(
1408 watcher.get_watch_handles(),
1409 HashSet::from([
1410 tmpdir.to_path_buf(),
1411 nested1,
1412 nested2,
1413 nested3,
1414 nested4,
1415 nested5,
1416 nested6,
1417 nested7,
1418 nested8,
1419 nested9
1420 ])
1421 );
1422 }
1423}