1 use std::{
2     ffi::{
3         OsStr,
4         OsString,
5     },
6     mem,
7     os::unix::ffi::OsStrExt,
8     sync::Weak,
9 };
10 
11 use inotify_sys as ffi;
12 
13 use crate::fd_guard::FdGuard;
14 use 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)]
25 pub struct Events<'a> {
26     fd       : Weak<FdGuard>,
27     buffer   : &'a [u8],
28     num_bytes: usize,
29     pos      : usize,
30 }
31 
32 impl<'a> Events<'a> {
new(fd: Weak<FdGuard>, buffer: &'a [u8], num_bytes: usize) -> Self33     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 
45 impl<'a> Iterator for Events<'a> {
46     type Item = Event<&'a OsStr>;
47 
next(&mut self) -> Option<Self::Item>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)]
73 pub 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 
107 impl<'a> Event<&'a OsStr> {
new(fd: Weak<FdGuard>, event: &ffi::inotify_event, name: &'a OsStr) -> Self108     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.
from_buffer( fd : Weak<FdGuard>, buffer: &'a [u8], ) -> (usize, Self)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)]
into_owned(&self) -> EventOwned204     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"]
to_owned(&self) -> EventOwned210     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`
222 pub type EventOwned = Event<OsString>;
223 
224 
225 bitflags! {
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 
360 impl 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"]
from_bits_unchecked(bits: u32) -> Self368     pub unsafe fn from_bits_unchecked(bits: u32) -> Self {
369         Self::from_bits_retain(bits)
370     }
371 }
372 
373 
374 #[cfg(test)]
375 mod 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]
from_buffer_should_not_mistake_next_event_for_name_of_previous_event()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 }
420