notify/
inotify.rs

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