use std::{
convert::{TryFrom, TryInto},
error::Error,
ffi::{OsStr, OsString},
fmt::Display,
mem,
os::unix::ffi::OsStrExt,
sync::Weak,
};
use inotify_sys as ffi;
use crate::fd_guard::FdGuard;
use crate::watches::WatchDescriptor;
#[derive(Debug)]
pub struct Events<'a> {
fd: Weak<FdGuard>,
buffer: &'a [u8],
num_bytes: usize,
pos: usize,
}
impl<'a> Events<'a> {
pub(crate) fn new(fd: Weak<FdGuard>, buffer: &'a [u8], num_bytes: usize) -> Self {
Events {
fd,
buffer,
num_bytes,
pos: 0,
}
}
}
impl<'a> Iterator for Events<'a> {
type Item = Event<&'a OsStr>;
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.num_bytes {
let (step, event) = Event::from_buffer(self.fd.clone(), &self.buffer[self.pos..]);
self.pos += step;
Some(event)
} else {
None
}
}
}
#[derive(Clone, Debug)]
pub struct Event<S> {
pub wd: WatchDescriptor,
pub mask: EventMask,
pub cookie: u32,
pub name: Option<S>,
}
impl<'a> Event<&'a OsStr> {
fn new(fd: Weak<FdGuard>, event: &ffi::inotify_event, name: &'a OsStr) -> Self {
let mask = EventMask::from_bits(event.mask)
.expect("Failed to convert event mask. This indicates a bug.");
let wd = crate::WatchDescriptor { id: event.wd, fd };
let name = if name.is_empty() { None } else { Some(name) };
Event {
wd,
mask,
cookie: event.cookie,
name,
}
}
pub(crate) fn from_buffer(fd: Weak<FdGuard>, buffer: &'a [u8]) -> (usize, Self) {
let event_size = mem::size_of::<ffi::inotify_event>();
assert!(buffer.len() >= event_size);
let ffi_event_ptr = buffer.as_ptr() as *const ffi::inotify_event;
let ffi_event = unsafe { ffi_event_ptr.read_unaligned() };
let bytes_left_in_buffer = buffer.len() - event_size;
assert!(bytes_left_in_buffer >= ffi_event.len as usize);
let bytes_consumed = event_size + ffi_event.len as usize;
let name = &buffer[event_size..bytes_consumed];
let name = name.splitn(2, |b| b == &0u8).next().unwrap();
let event = Event::new(fd, &ffi_event, OsStr::from_bytes(name));
(bytes_consumed, event)
}
#[deprecated = "use `to_owned()` instead; methods named `into_owned()` usually take self by value"]
#[allow(clippy::wrong_self_convention)]
pub fn into_owned(&self) -> EventOwned {
self.to_owned()
}
#[must_use = "cloning is often expensive and is not expected to have side effects"]
pub fn to_owned(&self) -> EventOwned {
Event {
wd: self.wd.clone(),
mask: self.mask,
cookie: self.cookie,
name: self.name.map(OsStr::to_os_string),
}
}
}
pub type EventOwned = Event<OsString>;
bitflags! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct EventMask: u32 {
const ACCESS = ffi::IN_ACCESS;
const ATTRIB = ffi::IN_ATTRIB;
const CLOSE_WRITE = ffi::IN_CLOSE_WRITE;
const CLOSE_NOWRITE = ffi::IN_CLOSE_NOWRITE;
const CREATE = ffi::IN_CREATE;
const DELETE = ffi::IN_DELETE;
const DELETE_SELF = ffi::IN_DELETE_SELF;
const MODIFY = ffi::IN_MODIFY;
const MOVE_SELF = ffi::IN_MOVE_SELF;
const MOVED_FROM = ffi::IN_MOVED_FROM;
const MOVED_TO = ffi::IN_MOVED_TO;
const OPEN = ffi::IN_OPEN;
const IGNORED = ffi::IN_IGNORED;
const ISDIR = ffi::IN_ISDIR;
const Q_OVERFLOW = ffi::IN_Q_OVERFLOW;
const UNMOUNT = ffi::IN_UNMOUNT;
}
}
impl EventMask {
pub fn parse(self) -> Result<ParsedEventMask, EventMaskParseError> {
self.try_into()
}
#[deprecated = "Use the safe `from_bits_retain` method instead"]
pub unsafe fn from_bits_unchecked(bits: u32) -> Self {
Self::from_bits_retain(bits)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ParsedEventMask {
pub kind: Option<EventKind>,
pub auxiliary_flags: EventAuxiliaryFlags,
}
impl ParsedEventMask {
pub fn from_parts(kind: Option<EventKind>, auxiliary_flags: EventAuxiliaryFlags) -> Self {
ParsedEventMask {
kind,
auxiliary_flags,
}
}
pub fn from_raw_event_mask(mask: EventMask) -> Result<Self, EventMaskParseError> {
if mask.contains(EventMask::Q_OVERFLOW) {
return Err(EventMaskParseError::QueueOverflow);
}
let kind = Option::<EventKind>::try_from(mask)?;
let auxiliary_flags = EventAuxiliaryFlags::from(mask);
Ok(ParsedEventMask::from_parts(kind, auxiliary_flags))
}
}
impl TryFrom<EventMask> for ParsedEventMask {
type Error = EventMaskParseError;
fn try_from(value: EventMask) -> Result<Self, Self::Error> {
Self::from_raw_event_mask(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EventKind {
Access,
Attrib,
CloseWrite,
CloseNowrite,
Create,
Delete,
DeleteSelf,
Modify,
MoveSelf,
MovedFrom,
MovedTo,
Open,
}
impl EventKind {
const BITFLAG_ENUM_MAP: &[(EventMask, EventKind)] = &[
(EventMask::ACCESS, EventKind::Access),
(EventMask::ATTRIB, EventKind::Attrib),
(EventMask::CLOSE_WRITE, EventKind::CloseWrite),
(EventMask::CLOSE_NOWRITE, EventKind::CloseNowrite),
(EventMask::CREATE, EventKind::Create),
(EventMask::DELETE, EventKind::Delete),
(EventMask::DELETE_SELF, EventKind::DeleteSelf),
(EventMask::MODIFY, EventKind::Modify),
(EventMask::MOVE_SELF, EventKind::MoveSelf),
(EventMask::MOVED_FROM, EventKind::MovedFrom),
(EventMask::MOVED_TO, EventKind::MovedTo),
(EventMask::OPEN, EventKind::Open),
];
pub fn from_raw_event_mask(mask: EventMask) -> Result<Option<Self>, EventMaskParseError> {
let mut kinds = Self::BITFLAG_ENUM_MAP.iter().filter_map(|bf_map| {
if mask.contains(bf_map.0) {
Some(bf_map.1)
} else {
None
}
});
let kind = kinds.next();
if kinds.next().is_some() {
return Err(EventMaskParseError::TooManyBitsSet(mask));
}
Ok(kind)
}
}
impl TryFrom<EventMask> for Option<EventKind> {
type Error = EventMaskParseError;
fn try_from(value: EventMask) -> Result<Self, Self::Error> {
EventKind::from_raw_event_mask(value)
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
pub struct EventAuxiliaryFlags {
pub ignored: bool,
pub isdir: bool,
pub unmount: bool,
}
impl EventAuxiliaryFlags {
pub fn from_raw_event_mask(mask: EventMask) -> Self {
EventAuxiliaryFlags {
ignored: mask.contains(EventMask::IGNORED),
isdir: mask.contains(EventMask::ISDIR),
unmount: mask.contains(EventMask::UNMOUNT),
}
}
}
impl From<EventMask> for EventAuxiliaryFlags {
fn from(value: EventMask) -> Self {
Self::from_raw_event_mask(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventMaskParseError {
TooManyBitsSet(EventMask),
QueueOverflow,
}
impl Display for EventMaskParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TooManyBitsSet(mask) => {
writeln!(
f,
"Error parsing event mask: too many event type bits set | {mask:?}"
)
}
Self::QueueOverflow => writeln!(f, "Error: the kernel's event queue overflowed"),
}
}
}
impl Error for EventMaskParseError {}
#[cfg(test)]
mod tests {
use std::{io::prelude::*, mem, slice, sync};
use inotify_sys as ffi;
use crate::{EventMask, EventMaskParseError};
use super::{Event, EventAuxiliaryFlags, EventKind, ParsedEventMask};
#[test]
fn from_buffer_should_not_mistake_next_event_for_name_of_previous_event() {
let mut buffer = [0u8; 1024];
let event = ffi::inotify_event {
wd: 0,
mask: 0,
cookie: 0,
len: 0, };
let event = unsafe {
slice::from_raw_parts(&event as *const _ as *const u8, mem::size_of_val(&event))
};
(&mut buffer[..])
.write_all(event)
.expect("Failed to write into buffer");
buffer[mem::size_of_val(event)] = 1;
let (_, event) = Event::from_buffer(sync::Weak::new(), &buffer);
assert_eq!(event.name, None);
}
#[test]
fn parse_event_kinds() {
for bf_map in EventKind::BITFLAG_ENUM_MAP {
assert_eq!(
Ok(ParsedEventMask {
kind: Some(bf_map.1),
auxiliary_flags: Default::default()
}),
bf_map.0.parse()
);
}
assert_eq!(
Ok(ParsedEventMask {
kind: None,
auxiliary_flags: Default::default()
}),
EventMask::from_bits_retain(0).parse()
)
}
#[test]
fn parse_event_auxiliary_flags() {
assert_eq!(
Ok(ParsedEventMask {
kind: None,
auxiliary_flags: EventAuxiliaryFlags {
ignored: true,
isdir: false,
unmount: false
}
}),
EventMask::IGNORED.parse()
);
assert_eq!(
Ok(ParsedEventMask {
kind: None,
auxiliary_flags: EventAuxiliaryFlags {
ignored: false,
isdir: true,
unmount: false
}
}),
EventMask::ISDIR.parse()
);
assert_eq!(
Ok(ParsedEventMask {
kind: None,
auxiliary_flags: EventAuxiliaryFlags {
ignored: false,
isdir: false,
unmount: true
}
}),
EventMask::UNMOUNT.parse()
);
}
#[test]
fn parse_event_errors() {
assert_eq!(
Err(EventMaskParseError::QueueOverflow),
EventMask::Q_OVERFLOW.parse()
);
let mask = EventMask::ATTRIB | EventMask::ACCESS;
assert_eq!(Err(EventMaskParseError::TooManyBitsSet(mask)), mask.parse());
}
}