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