use crate::CoreError;
use crate::error::syscall_ret;
use crate::reactor::Fd;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InotifyEvent {
pub wd: i32,
pub mask: u32,
pub name: Option<Vec<u8>>,
}
pub const MODIFY_MASK: u32 = libc::IN_MODIFY;
pub const PACKAGE_FILE_MASK: u32 = libc::IN_MODIFY | libc::IN_DELETE_SELF | libc::IN_MOVE_SELF;
pub const PARENT_WATCH_MASK: u32 = libc::IN_CREATE
| libc::IN_MOVED_TO
| libc::IN_CLOSE_WRITE
| libc::IN_MODIFY
| libc::IN_DELETE_SELF
| libc::IN_MOVE_SELF;
pub const QUEUE_OVERFLOW_MASK: u32 = libc::IN_Q_OVERFLOW;
pub const IGNORED_MASK: u32 = libc::IN_IGNORED;
pub const UNMOUNT_MASK: u32 = libc::IN_UNMOUNT;
pub const DELETE_SELF_MASK: u32 = libc::IN_DELETE_SELF;
pub const MOVE_SELF_MASK: u32 = libc::IN_MOVE_SELF;
pub fn init() -> Result<Fd, CoreError> {
let fd = unsafe { libc::inotify_init1(libc::IN_CLOEXEC | libc::IN_NONBLOCK) };
syscall_ret(fd, "inotify_init1")?;
Fd::new(fd, "inotify_init1")
}
pub fn add_watch(fd: &Fd, path: &str, mask: u32) -> Result<i32, CoreError> {
let path = std::ffi::CString::new(path)
.map_err(|_| CoreError::sys(libc::EINVAL, "inotify path contains nul"))?;
let wd = unsafe { libc::inotify_add_watch(fd.raw(), path.as_ptr(), mask) };
if wd < 0 {
return Err(CoreError::sys(
std::io::Error::last_os_error().raw_os_error().unwrap_or(0),
"inotify_add_watch",
));
}
Ok(wd)
}
pub fn remove_watch(fd: &Fd, wd: i32) -> Result<(), CoreError> {
#[cfg(target_os = "android")]
let raw_wd = u32::try_from(wd).map_err(|_| CoreError::sys(libc::EINVAL, "inotify_rm_watch"))?;
#[cfg(not(target_os = "android"))]
let raw_wd = wd;
let ret = unsafe { libc::inotify_rm_watch(fd.raw(), raw_wd) };
syscall_ret(ret, "inotify_rm_watch")
}
pub fn read_events(fd: &Fd) -> Result<Vec<InotifyEvent>, CoreError> {
let mut all_events = Vec::new();
let mut buf = vec![0u8; 4096];
loop {
match fd.read_slice(&mut buf) {
Ok(Some(0)) => break,
Ok(Some(n)) => {
all_events.extend(decode_events(&buf[..n])?);
}
Ok(None) => break, Err(e) => return Err(e),
}
}
Ok(all_events)
}
pub fn decode_events(buf: &[u8]) -> Result<Vec<InotifyEvent>, CoreError> {
let mut events = Vec::new();
let mut offset = 0;
let base = std::mem::size_of::<libc::inotify_event>();
while offset + base <= buf.len() {
let event: libc::inotify_event = unsafe {
std::ptr::read_unaligned(buf.as_ptr().add(offset) as *const libc::inotify_event)
};
let Some(size) = base.checked_add(event.len as usize) else {
return Err(CoreError::sys(libc::EINVAL, "decode_inotify_event"));
};
if offset + size > buf.len() {
return Err(CoreError::sys(libc::EINVAL, "decode_inotify_event"));
}
let name = if event.len > 0 {
let name_buf = &buf[offset + base..offset + base + event.len as usize];
name_buf.split(|&b| b == 0).next().map(|s| s.to_vec())
} else {
None
};
events.push(InotifyEvent {
wd: event.wd,
mask: event.mask,
name,
});
offset += size;
}
if offset != buf.len() {
return Err(CoreError::sys(libc::EINVAL, "decode_inotify_event"));
}
Ok(events)
}