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