Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use the WatchDescriptor struct generated by inotify-rs to match events to files that generated them?

I'm using Inotify (through the inotify-rs wrapper) to watch a large number of files (not directories) across a filesystem.

The following inotify-rs method returns a WatchDescriptor struct:

add_watch<P>(&mut self, path: P, mask: WatchMask) -> io::Result<WatchDescriptor>
where
   P: AsRef<Path>

WatchDescriptor is a newtype struct: pub struct WatchDescriptor(RawFd); where std::os::unix::io::RawFd is type c_int/f32

the Inotify subsystem returns an inotify_event struct each time a watch is triggered:

struct inotify_event {
        int      wd;       /* Watch descriptor */
        uint32_t mask;     /* Mask describing event */
        uint32_t cookie;   /* Unique cookie associating related
                                events (for rename(2)) */
        uint32_t len;      /* Size of name field */
        char     name[];   /* Optional null-terminated name */
    };

The "Watch descriptor" int wd matches the watch descriptor generated by the initial call to add_watch()

In turn, the inotify-rs wrapper returns the following event struct:

pub struct Event<'a> {    
    pub wd    : WatchDescriptor,
    pub mask  : EventMask,
    pub cookie: u32,
    pub name  : &'a OsStr,
}

I am trying to use Event.wd to match the event to a file from a list of files being watched. (.name only returns a filename if the event triggered on a watch for a directory) I have been unsuccessful at trying to use a HashMap because the struct WatchDescriptor does not derive the Hash trait. I tried to fork the crate and implement that myself, but that opened up an entirely new can of worms.

The simplest way should be to use Event.wd.0 to get a c_int/i32 but then I have to deal with error: field '0' of struct 'inotify::WatchDescriptor' is private

Is there a simple way to do this, short of re-writing the wrapper to behave the way I want it, or to do a PR and wait for it to be merged?

I have considered creating an Inotify for each file being watched, but in a scenario where hundreds of files could be watched, this will be prohibitively expensive.

like image 859
arsalan86 Avatar asked Dec 03 '25 08:12

arsalan86


1 Answers

There are some substantial issues with this library:

  • The poor choice of type to represent the inotify watch descriptor. Inotify watch descriptors are not Unix file descriptors, they are just some integral number, scoped per inotify file descriptor. Not a flaw by itself, but I certainly would view that as a red flag.
  • As noted above, inotify watch descriptor values are scoped per parent inotify file descriptor. This makes the use of [derive(Eq)] on WatchDescriptor highly questionable. Looks like a straight-up bug.
  • There is nothing to ensure that inotify watches are closed before closing the inotify descriptor. There is nothing to ensure that watch descriptors don't get reused behind your back (they can be reused after watch descriptor number wraps). Those issues probably won't bite you immediately, but… IMO, the entire inotify-rs should have been declared unsafe. It does not add any actual safety on top of inotify, just some sugar and pointless wrappers. It does not even implement Drop for the watch descriptor!
  • The library does nothing to highlight major gotchas of inotify: event queue overflows, IN_IGNORED and hard links sharing the same inode (and the same inotify watch descriptors!)

I recommend you to carefully read the inotify documentation and write your own wrapper if necessary. This library won't save you any trouble.

like image 176
user1643723 Avatar answered Dec 05 '25 21:12

user1643723