notify/
inotify.rs

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