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