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