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