Skip to main content

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