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