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