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> { |
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 | |
45 | impl<'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: usize, event: Event<&OsStr>) = 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> { |
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` |
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" ] |
368 | 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 ] |
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 | |