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 fd_guard::FdGuard;
14 use 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`]: struct.Inotify.html#method.read_events_blocking
23 /// [`Inotify::read_events`]: struct.Inotify.html#method.read_events
24 pub struct Events<'a> {
25     fd       : Weak<FdGuard>,
26     buffer   : &'a [u8],
27     num_bytes: usize,
28     pos      : usize,
29 }
30 
31 impl<'a> Events<'a> {
new(fd: Weak<FdGuard>, buffer: &'a [u8], num_bytes: usize) -> Self32     pub(crate) fn new(fd: Weak<FdGuard>, buffer: &'a [u8], num_bytes: usize)
33         -> Self
34     {
35         Events {
36             fd       : fd,
37             buffer   : buffer,
38             num_bytes: num_bytes,
39             pos      : 0,
40         }
41     }
42 }
43 
44 impl<'a> Iterator for Events<'a> {
45     type Item = Event<&'a OsStr>;
46 
next(&mut self) -> Option<Self::Item>47     fn next(&mut self) -> Option<Self::Item> {
48         if self.pos < self.num_bytes {
49             let (step, event) = Event::from_buffer(self.fd.clone(), &self.buffer[self.pos..]);
50             self.pos += step;
51 
52             Some(event)
53         }
54         else {
55             None
56         }
57     }
58 }
59 
60 
61 /// An inotify event
62 ///
63 /// A file system event that describes a change that the user previously
64 /// registered interest in. To watch for events, call [`Inotify::add_watch`]. To
65 /// retrieve events, call [`Inotify::read_events_blocking`] or
66 /// [`Inotify::read_events`].
67 ///
68 /// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
69 /// [`Inotify::read_events_blocking`]: struct.Inotify.html#method.read_events_blocking
70 /// [`Inotify::read_events`]: struct.Inotify.html#method.read_events
71 #[derive(Clone, Debug)]
72 pub struct Event<S> {
73     /// Identifies the watch this event originates from
74     ///
75     /// This [`WatchDescriptor`] is equal to the one that [`Inotify::add_watch`]
76     /// returned when interest for this event was registered. The
77     /// [`WatchDescriptor`] can be used to remove the watch using
78     /// [`Inotify::rm_watch`], thereby preventing future events of this type
79     /// from being created.
80     ///
81     /// [`WatchDescriptor`]: struct.WatchDescriptor.html
82     /// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
83     /// [`Inotify::rm_watch`]: struct.Inotify.html#method.rm_watch
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`]: event_mask/constant.MOVED_FROM.html
96     /// [`MOVED_TO`]: event_mask/constant.MOVED_TO.html
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 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 = ::WatchDescriptor {
115             id: event.wd,
116             fd,
117         };
118 
119         let name = if name == "" {
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 event = 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         // convert that pointer into a reference.
162         let event = unsafe { &*event };
163 
164         // The name's length is given by `event.len`. There should always be
165         // enough bytes left in the buffer to fit the name. Let's make sure that
166         // is the case.
167         let bytes_left_in_buffer = buffer.len() - event_size;
168         assert!(bytes_left_in_buffer >= event.len as usize);
169 
170         // Directly after the event struct should be a name, if there's one
171         // associated with the event. Let's make a new slice that starts with
172         // that name. If there's no name, this slice might have a length of `0`.
173         let bytes_consumed = event_size + event.len as usize;
174         let name = &buffer[event_size..bytes_consumed];
175 
176         // Remove trailing '\0' bytes
177         //
178         // The events in the buffer are aligned, and `name` is filled up
179         // with '\0' up to the alignment boundary. Here we remove those
180         // additional bytes.
181         //
182         // The `unwrap` here is safe, because `splitn` always returns at
183         // least one result, even if the original slice contains no '\0'.
184         let name = name
185             .splitn(2, |b| b == &0u8)
186             .next()
187             .unwrap();
188 
189         let event = Event::new(
190             fd,
191             event,
192             OsStr::from_bytes(name),
193         );
194 
195         (bytes_consumed, event)
196     }
197 
into_owned(&self) -> EventOwned198     pub(crate) fn into_owned(&self) -> EventOwned {
199         Event {
200             wd: self.wd.clone(),
201             mask: self.mask,
202             cookie: self.cookie,
203             name: self.name.map(OsStr::to_os_string),
204         }
205     }
206 }
207 
208 
209 /// An owned version of `Event`
210 pub type EventOwned = Event<OsString>;
211 
212 
213 bitflags! {
214     /// Indicates the type of an event
215     ///
216     /// This struct can be retrieved from an [`Event`] via its `mask` field.
217     /// You can determine the [`Event`]'s type by comparing the `EventMask` to
218     /// its associated constants.
219     ///
220     /// Please refer to the documentation of [`Event`] for a usage example.
221     ///
222     /// [`Event`]: struct.Event.html
223     pub struct EventMask: u32 {
224         /// File was accessed
225         ///
226         /// When watching a directory, this event is only triggered for objects
227         /// inside the directory, not the directory itself.
228         ///
229         /// See [`inotify_sys::IN_ACCESS`].
230         ///
231         /// [`inotify_sys::IN_ACCESS`]: ../inotify_sys/constant.IN_ACCESS.html
232         const ACCESS = ffi::IN_ACCESS;
233 
234         /// Metadata (permissions, timestamps, ...) changed
235         ///
236         /// When watching a directory, this event can be triggered for the
237         /// directory itself, as well as objects inside the directory.
238         ///
239         /// See [`inotify_sys::IN_ATTRIB`].
240         ///
241         /// [`inotify_sys::IN_ATTRIB`]: ../inotify_sys/constant.IN_ATTRIB.html
242         const ATTRIB = ffi::IN_ATTRIB;
243 
244         /// File opened for writing was closed
245         ///
246         /// When watching a directory, this event is only triggered for objects
247         /// inside the directory, not the directory itself.
248         ///
249         /// See [`inotify_sys::IN_CLOSE_WRITE`].
250         ///
251         /// [`inotify_sys::IN_CLOSE_WRITE`]: ../inotify_sys/constant.IN_CLOSE_WRITE.html
252         const CLOSE_WRITE = ffi::IN_CLOSE_WRITE;
253 
254         /// File or directory not opened for writing was closed
255         ///
256         /// When watching a directory, this event can be triggered for the
257         /// directory itself, as well as objects inside the directory.
258         ///
259         /// See [`inotify_sys::IN_CLOSE_NOWRITE`].
260         ///
261         /// [`inotify_sys::IN_CLOSE_NOWRITE`]: ../inotify_sys/constant.IN_CLOSE_NOWRITE.html
262         const CLOSE_NOWRITE = ffi::IN_CLOSE_NOWRITE;
263 
264         /// File/directory created in watched directory
265         ///
266         /// When watching a directory, this event is only triggered for objects
267         /// inside the directory, not the directory itself.
268         ///
269         /// See [`inotify_sys::IN_CREATE`].
270         ///
271         /// [`inotify_sys::IN_CREATE`]: ../inotify_sys/constant.IN_CREATE.html
272         const CREATE = ffi::IN_CREATE;
273 
274         /// File/directory deleted from watched directory
275         ///
276         /// When watching a directory, this event is only triggered for objects
277         /// inside the directory, not the directory itself.
278         ///
279         /// See [`inotify_sys::IN_DELETE`].
280         ///
281         /// [`inotify_sys::IN_DELETE`]: ../inotify_sys/constant.IN_DELETE.html
282         const DELETE = ffi::IN_DELETE;
283 
284         /// Watched file/directory was deleted
285         ///
286         /// See [`inotify_sys::IN_DELETE_SELF`].
287         ///
288         /// [`inotify_sys::IN_DELETE_SELF`]: ../inotify_sys/constant.IN_DELETE_SELF.html
289         const DELETE_SELF = ffi::IN_DELETE_SELF;
290 
291         /// File was modified
292         ///
293         /// When watching a directory, this event is only triggered for objects
294         /// inside the directory, not the directory itself.
295         ///
296         /// See [`inotify_sys::IN_MODIFY`].
297         ///
298         /// [`inotify_sys::IN_MODIFY`]: ../inotify_sys/constant.IN_MODIFY.html
299         const MODIFY = ffi::IN_MODIFY;
300 
301         /// Watched file/directory was moved
302         ///
303         /// See [`inotify_sys::IN_MOVE_SELF`].
304         ///
305         /// [`inotify_sys::IN_MOVE_SELF`]: ../inotify_sys/constant.IN_MOVE_SELF.html
306         const MOVE_SELF = ffi::IN_MOVE_SELF;
307 
308         /// File was renamed/moved; watched directory contained old name
309         ///
310         /// When watching a directory, this event is only triggered for objects
311         /// inside the directory, not the directory itself.
312         ///
313         /// See [`inotify_sys::IN_MOVED_FROM`].
314         ///
315         /// [`inotify_sys::IN_MOVED_FROM`]: ../inotify_sys/constant.IN_MOVED_FROM.html
316         const MOVED_FROM = ffi::IN_MOVED_FROM;
317 
318         /// File was renamed/moved; watched directory contains new name
319         ///
320         /// When watching a directory, this event is only triggered for objects
321         /// inside the directory, not the directory itself.
322         ///
323         /// See [`inotify_sys::IN_MOVED_TO`].
324         ///
325         /// [`inotify_sys::IN_MOVED_TO`]: ../inotify_sys/constant.IN_MOVED_TO.html
326         const MOVED_TO = ffi::IN_MOVED_TO;
327 
328         /// File or directory was opened
329         ///
330         /// When watching a directory, this event can be triggered for the
331         /// directory itself, as well as objects inside the directory.
332         ///
333         /// See [`inotify_sys::IN_OPEN`].
334         ///
335         /// [`inotify_sys::IN_OPEN`]: ../inotify_sys/constant.IN_OPEN.html
336         const OPEN = ffi::IN_OPEN;
337 
338         /// Watch was removed
339         ///
340         /// This event will be generated, if the watch was removed explicitly
341         /// (via [`Inotify::rm_watch`]), or automatically (because the file was
342         /// deleted or the file system was unmounted).
343         ///
344         /// See [`inotify_sys::IN_IGNORED`].
345         ///
346         /// [`inotify_sys::IN_IGNORED`]: ../inotify_sys/constant.IN_IGNORED.html
347         const IGNORED = ffi::IN_IGNORED;
348 
349         /// Event related to a directory
350         ///
351         /// The subject of the event is a directory.
352         ///
353         /// See [`inotify_sys::IN_ISDIR`].
354         ///
355         /// [`inotify_sys::IN_ISDIR`]: ../inotify_sys/constant.IN_ISDIR.html
356         const ISDIR = ffi::IN_ISDIR;
357 
358         /// Event queue overflowed
359         ///
360         /// The event queue has overflowed and events have presumably been lost.
361         ///
362         /// See [`inotify_sys::IN_Q_OVERFLOW`].
363         ///
364         /// [`inotify_sys::IN_Q_OVERFLOW`]: ../inotify_sys/constant.IN_Q_OVERFLOW.html
365         const Q_OVERFLOW = ffi::IN_Q_OVERFLOW;
366 
367         /// File system containing watched object was unmounted.
368         /// File system was unmounted
369         ///
370         /// The file system that contained the watched object has been
371         /// unmounted. An event with [`WatchMask::IGNORED`] will subsequently be
372         /// generated for the same watch descriptor.
373         ///
374         /// See [`inotify_sys::IN_UNMOUNT`].
375         ///
376         /// [`WatchMask::IGNORED`]: #associatedconstant.IGNORED
377         /// [`inotify_sys::IN_UNMOUNT`]: ../inotify_sys/constant.IN_UNMOUNT.html
378         const UNMOUNT = ffi::IN_UNMOUNT;
379     }
380 }
381 
382 
383 #[cfg(test)]
384 mod tests {
385     use std::{
386         io::prelude::*,
387         mem,
388         slice,
389         sync,
390     };
391 
392     use inotify_sys as ffi;
393 
394     use super::Event;
395 
396 
397     #[test]
from_buffer_should_not_mistake_next_event_for_name_of_previous_event()398     fn from_buffer_should_not_mistake_next_event_for_name_of_previous_event() {
399         let mut buffer = [0u8; 1024];
400 
401         // First, put a normal event into the buffer
402         let event = ffi::inotify_event {
403             wd:     0,
404             mask:   0,
405             cookie: 0,
406             len:    0, // no name following after event
407         };
408         let event = unsafe {
409                 slice::from_raw_parts(
410                 &event as *const _ as *const u8,
411                 mem::size_of_val(&event),
412             )
413         };
414         (&mut buffer[..]).write(event)
415             .expect("Failed to write into buffer");
416 
417         // After that event, simulate an event that starts with a non-zero byte.
418         buffer[mem::size_of_val(&event)] = 1;
419 
420         // Now create the event and verify that the name is actually `None`, as
421         // dictated by the value `len` above.
422         let (_, event) = Event::from_buffer(
423             sync::Weak::new(),
424             &buffer,
425         );
426         assert_eq!(event.name, None);
427     }
428 }
429