inotify/
events.rs

1use std::{
2    ffi::{
3        OsStr,
4        OsString,
5    },
6    mem,
7    os::unix::ffi::OsStrExt,
8    sync::Weak,
9};
10
11use inotify_sys as ffi;
12
13use crate::fd_guard::FdGuard;
14use crate::watches::WatchDescriptor;
15
16
17/// Iterator over inotify events
18///
19/// Allows for iteration over the events returned by
20/// [`Inotify::read_events_blocking`] or [`Inotify::read_events`].
21///
22/// [`Inotify::read_events_blocking`]: crate::Inotify::read_events_blocking
23/// [`Inotify::read_events`]: crate::Inotify::read_events
24#[derive(Debug)]
25pub struct Events<'a> {
26    fd       : Weak<FdGuard>,
27    buffer   : &'a [u8],
28    num_bytes: usize,
29    pos      : usize,
30}
31
32impl<'a> Events<'a> {
33    pub(crate) fn new(fd: Weak<FdGuard>, buffer: &'a [u8], num_bytes: usize)
34        -> Self
35    {
36        Events {
37            fd,
38            buffer,
39            num_bytes,
40            pos: 0,
41        }
42    }
43}
44
45impl<'a> Iterator for Events<'a> {
46    type Item = Event<&'a OsStr>;
47
48    fn next(&mut self) -> Option<Self::Item> {
49        if self.pos < self.num_bytes {
50            let (step, event) = Event::from_buffer(self.fd.clone(), &self.buffer[self.pos..]);
51            self.pos += step;
52
53            Some(event)
54        }
55        else {
56            None
57        }
58    }
59}
60
61
62/// An inotify event
63///
64/// A file system event that describes a change that the user previously
65/// registered interest in. To watch for events, call [`Watches::add`]. To
66/// retrieve events, call [`Inotify::read_events_blocking`] or
67/// [`Inotify::read_events`].
68///
69/// [`Watches::add`]: crate::Watches::add
70/// [`Inotify::read_events_blocking`]: crate::Inotify::read_events_blocking
71/// [`Inotify::read_events`]: crate::Inotify::read_events
72#[derive(Clone, Debug)]
73pub struct Event<S> {
74    /// Identifies the watch this event originates from
75    ///
76    /// This [`WatchDescriptor`] is equal to the one that [`Watches::add`]
77    /// returned when interest for this event was registered. The
78    /// [`WatchDescriptor`] can be used to remove the watch using
79    /// [`Watches::remove`], thereby preventing future events of this type
80    /// from being created.
81    ///
82    /// [`Watches::add`]: crate::Watches::add
83    /// [`Watches::remove`]: crate::Watches::remove
84    pub wd: WatchDescriptor,
85
86    /// Indicates what kind of event this is
87    pub mask: EventMask,
88
89    /// Connects related events to each other
90    ///
91    /// When a file is renamed, this results two events: [`MOVED_FROM`] and
92    /// [`MOVED_TO`]. The `cookie` field will be the same for both of them,
93    /// thereby making is possible to connect the event pair.
94    ///
95    /// [`MOVED_FROM`]: EventMask::MOVED_FROM
96    /// [`MOVED_TO`]: EventMask::MOVED_TO
97    pub cookie: u32,
98
99    /// The name of the file the event originates from
100    ///
101    /// This field is set only if the subject of the event is a file or directory in a
102    /// watched directory. If the event concerns a file or directory that is
103    /// watched directly, `name` will be `None`.
104    pub name: Option<S>,
105}
106
107impl<'a> Event<&'a OsStr> {
108    fn new(fd: Weak<FdGuard>, event: &ffi::inotify_event, name: &'a OsStr)
109        -> Self
110    {
111        let mask = EventMask::from_bits(event.mask)
112            .expect("Failed to convert event mask. This indicates a bug.");
113
114        let wd = crate::WatchDescriptor {
115            id: event.wd,
116            fd,
117        };
118
119        let name = if name.is_empty() {
120            None
121        }
122        else {
123            Some(name)
124        };
125
126        Event {
127            wd,
128            mask,
129            cookie: event.cookie,
130            name,
131        }
132    }
133
134    /// Create an `Event` from a buffer
135    ///
136    /// Assumes that a full `inotify_event` plus its name is located at the
137    /// beginning of `buffer`.
138    ///
139    /// Returns the number of bytes used from the buffer, and the event.
140    ///
141    /// # Panics
142    ///
143    /// Panics if the buffer does not contain a full event, including its name.
144    pub(crate) fn from_buffer(
145        fd    : Weak<FdGuard>,
146        buffer: &'a [u8],
147    )
148        -> (usize, Self)
149    {
150        let event_size = mem::size_of::<ffi::inotify_event>();
151
152        // Make sure that the buffer is big enough to contain an event, without
153        // the name. Otherwise we can't safely convert it to an `inotify_event`.
154        assert!(buffer.len() >= event_size);
155
156        let ffi_event_ptr = buffer.as_ptr() as *const ffi::inotify_event;
157
158        // We have a pointer to an `inotify_event`, pointing to the beginning of
159        // `buffer`. Since we know, as per the assertion above, that there are
160        // enough bytes in the buffer for at least one event, we can safely
161        // read that `inotify_event`.
162        // We call `read_unaligned()` since the byte buffer has alignment 1
163        // and `inotify_event` has a higher alignment, so `*` cannot be used to dereference
164        // the unaligned pointer (undefined behavior).
165        let ffi_event = unsafe { ffi_event_ptr.read_unaligned() };
166
167        // The name's length is given by `event.len`. There should always be
168        // enough bytes left in the buffer to fit the name. Let's make sure that
169        // is the case.
170        let bytes_left_in_buffer = buffer.len() - event_size;
171        assert!(bytes_left_in_buffer >= ffi_event.len as usize);
172
173        // Directly after the event struct should be a name, if there's one
174        // associated with the event. Let's make a new slice that starts with
175        // that name. If there's no name, this slice might have a length of `0`.
176        let bytes_consumed = event_size + ffi_event.len as usize;
177        let name = &buffer[event_size..bytes_consumed];
178
179        // Remove trailing '\0' bytes
180        //
181        // The events in the buffer are aligned, and `name` is filled up
182        // with '\0' up to the alignment boundary. Here we remove those
183        // additional bytes.
184        //
185        // The `unwrap` here is safe, because `splitn` always returns at
186        // least one result, even if the original slice contains no '\0'.
187        let name = name
188            .splitn(2, |b| b == &0u8)
189            .next()
190            .unwrap();
191
192        let event = Event::new(
193            fd,
194            &ffi_event,
195            OsStr::from_bytes(name),
196        );
197
198        (bytes_consumed, event)
199    }
200
201    /// Returns an owned copy of the event.
202    #[deprecated = "use `to_owned()` instead; methods named `into_owned()` usually take self by value"]
203    #[allow(clippy::wrong_self_convention)]
204    pub fn into_owned(&self) -> EventOwned {
205        self.to_owned()
206    }
207
208    /// Returns an owned copy of the event.
209    #[must_use = "cloning is often expensive and is not expected to have side effects"]
210    pub fn to_owned(&self) -> EventOwned {
211        Event {
212            wd: self.wd.clone(),
213            mask: self.mask,
214            cookie: self.cookie,
215            name: self.name.map(OsStr::to_os_string),
216        }
217    }
218}
219
220
221/// An owned version of `Event`
222pub type EventOwned = Event<OsString>;
223
224
225bitflags! {
226    /// Indicates the type of an event
227    ///
228    /// This struct can be retrieved from an [`Event`] via its `mask` field.
229    /// You can determine the [`Event`]'s type by comparing the `EventMask` to
230    /// its associated constants.
231    ///
232    /// Please refer to the documentation of [`Event`] for a usage example.
233    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
234    pub struct EventMask: u32 {
235        /// File was accessed
236        ///
237        /// When watching a directory, this event is only triggered for objects
238        /// inside the directory, not the directory itself.
239        ///
240        /// See [`inotify_sys::IN_ACCESS`].
241        const ACCESS = ffi::IN_ACCESS;
242
243        /// Metadata (permissions, timestamps, ...) changed
244        ///
245        /// When watching a directory, this event can be triggered for the
246        /// directory itself, as well as objects inside the directory.
247        ///
248        /// See [`inotify_sys::IN_ATTRIB`].
249        const ATTRIB = ffi::IN_ATTRIB;
250
251        /// File opened for writing was closed
252        ///
253        /// When watching a directory, this event is only triggered for objects
254        /// inside the directory, not the directory itself.
255        ///
256        /// See [`inotify_sys::IN_CLOSE_WRITE`].
257        const CLOSE_WRITE = ffi::IN_CLOSE_WRITE;
258
259        /// File or directory not opened for writing was closed
260        ///
261        /// When watching a directory, this event can be triggered for the
262        /// directory itself, as well as objects inside the directory.
263        ///
264        /// See [`inotify_sys::IN_CLOSE_NOWRITE`].
265        const CLOSE_NOWRITE = ffi::IN_CLOSE_NOWRITE;
266
267        /// File/directory created in watched directory
268        ///
269        /// When watching a directory, this event is only triggered for objects
270        /// inside the directory, not the directory itself.
271        ///
272        /// See [`inotify_sys::IN_CREATE`].
273        const CREATE = ffi::IN_CREATE;
274
275        /// File/directory deleted from watched directory
276        ///
277        /// When watching a directory, this event is only triggered for objects
278        /// inside the directory, not the directory itself.
279        const DELETE = ffi::IN_DELETE;
280
281        /// Watched file/directory was deleted
282        ///
283        /// See [`inotify_sys::IN_DELETE_SELF`].
284        const DELETE_SELF = ffi::IN_DELETE_SELF;
285
286        /// File was modified
287        ///
288        /// When watching a directory, this event is only triggered for objects
289        /// inside the directory, not the directory itself.
290        ///
291        /// See [`inotify_sys::IN_MODIFY`].
292        const MODIFY = ffi::IN_MODIFY;
293
294        /// Watched file/directory was moved
295        ///
296        /// See [`inotify_sys::IN_MOVE_SELF`].
297        const MOVE_SELF = ffi::IN_MOVE_SELF;
298
299        /// File was renamed/moved; watched directory contained old name
300        ///
301        /// When watching a directory, this event is only triggered for objects
302        /// inside the directory, not the directory itself.
303        ///
304        /// See [`inotify_sys::IN_MOVED_FROM`].
305        const MOVED_FROM = ffi::IN_MOVED_FROM;
306
307        /// File was renamed/moved; watched directory contains new name
308        ///
309        /// When watching a directory, this event is only triggered for objects
310        /// inside the directory, not the directory itself.
311        ///
312        /// See [`inotify_sys::IN_MOVED_TO`].
313        const MOVED_TO = ffi::IN_MOVED_TO;
314
315        /// File or directory was opened
316        ///
317        /// When watching a directory, this event can be triggered for the
318        /// directory itself, as well as objects inside the directory.
319        ///
320        /// See [`inotify_sys::IN_OPEN`].
321        const OPEN = ffi::IN_OPEN;
322
323        /// Watch was removed
324        ///
325        /// This event will be generated, if the watch was removed explicitly
326        /// (via [`Watches::remove`]), or automatically (because the file was
327        /// deleted or the file system was unmounted).
328        ///
329        /// See [`inotify_sys::IN_IGNORED`].
330        ///
331        /// [`Watches::remove`]: crate::Watches::remove
332        const IGNORED = ffi::IN_IGNORED;
333
334        /// Event related to a directory
335        ///
336        /// The subject of the event is a directory.
337        ///
338        /// See [`inotify_sys::IN_ISDIR`].
339        const ISDIR = ffi::IN_ISDIR;
340
341        /// Event queue overflowed
342        ///
343        /// The event queue has overflowed and events have presumably been lost.
344        ///
345        /// See [`inotify_sys::IN_Q_OVERFLOW`].
346        const Q_OVERFLOW = ffi::IN_Q_OVERFLOW;
347
348        /// File system containing watched object was unmounted.
349        /// File system was unmounted
350        ///
351        /// The file system that contained the watched object has been
352        /// unmounted. An event with [`EventMask::IGNORED`] will subsequently be
353        /// generated for the same watch descriptor.
354        ///
355        /// See [`inotify_sys::IN_UNMOUNT`].
356        const UNMOUNT = ffi::IN_UNMOUNT;
357    }
358}
359
360impl EventMask {
361    /// Wrapper around [`Self::from_bits_retain`] for backwards compatibility
362    ///
363    /// # Safety
364    ///
365    /// This function is not actually unsafe. It is just a wrapper around the
366    /// safe [`Self::from_bits_retain`].
367    #[deprecated = "Use the safe `from_bits_retain` method instead"]
368    pub unsafe fn from_bits_unchecked(bits: u32) -> Self {
369        Self::from_bits_retain(bits)
370    }
371}
372
373
374#[cfg(test)]
375mod tests {
376    use std::{
377        io::prelude::*,
378        mem,
379        slice,
380        sync,
381    };
382
383    use inotify_sys as ffi;
384
385    use super::Event;
386
387
388    #[test]
389    fn from_buffer_should_not_mistake_next_event_for_name_of_previous_event() {
390        let mut buffer = [0u8; 1024];
391
392        // First, put a normal event into the buffer
393        let event = ffi::inotify_event {
394            wd:     0,
395            mask:   0,
396            cookie: 0,
397            len:    0, // no name following after event
398        };
399        let event = unsafe {
400                slice::from_raw_parts(
401                &event as *const _ as *const u8,
402                mem::size_of_val(&event),
403            )
404        };
405        (&mut buffer[..]).write(event)
406            .expect("Failed to write into buffer");
407
408        // After that event, simulate an event that starts with a non-zero byte.
409        buffer[mem::size_of_val(&event)] = 1;
410
411        // Now create the event and verify that the name is actually `None`, as
412        // dictated by the value `len` above.
413        let (_, event) = Event::from_buffer(
414            sync::Weak::new(),
415            &buffer,
416        );
417        assert_eq!(event.name, None);
418    }
419}