From bab60681b34fa91bc8c262442985b8e36f58a0b8 Mon Sep 17 00:00:00 2001 From: guochao Date: Sat, 5 Apr 2025 06:00:46 +0000 Subject: [PATCH] implement fanotify event info --- fanotify-demo/src/main.rs | 225 ++++++++++++++++++++------------------ fanotify/src/consts.rs | 9 ++ fanotify/src/fanotify.rs | 63 ++++++++++- 3 files changed, 190 insertions(+), 107 deletions(-) diff --git a/fanotify-demo/src/main.rs b/fanotify-demo/src/main.rs index 9ba5942..b2f7aad 100644 --- a/fanotify-demo/src/main.rs +++ b/fanotify-demo/src/main.rs @@ -161,117 +161,132 @@ fn main() -> Result<(), Error> { continue; } for event in events.iter_mut() { - let Some(fd) = event.fd() else { - if init_flags & (InitFlags::FAN_REPORT_FID | InitFlags::FAN_REPORT_DIR_FID) - != InitFlags::empty() - { - warn!("fid not implementd"); - } else { - warn!("queue full"); - } - continue; - }; - let path = match std::fs::read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())) { - Ok(p) => p, - Err(err) => { - warn!( - "failed to read fd link for fd {}: {:?}", - fd.as_raw_fd(), - err - ); - continue; - } - }; - let cmdline_raw = match std::fs::read(format!("/proc/{}/cmdline", event.pid())) { - Ok(raw) => raw, - Err(err) => { - warn!( - "failed to read pid cmdline for fd {}: {:?}", - event.pid(), - err - ); - continue; - } - }; - - let cmdline = if cmdline_raw.len() > 0 { - Some( - cmdline_raw - .split(|&b| b == 0) - .map(|v| String::from_utf8_lossy(v)) - .collect::>>(), - ) - } else { - None - }; - - trace!( - "++++++++= {:?} {} {:?} {:?}", - fd, - event.pid(), - event.mask(), - path - ); - let arg0 = if let Some(cmdline) = cmdline.as_ref() { - for (idx, arg) in cmdline.iter().enumerate() { - trace!(" - {}: {}", idx, arg); - } - - let arg0 = cmdline[0].to_string(); - - arg0map.insert(event.pid(), arg0.clone()); - - arg0 - } else if arg0map.contains_key(&event.pid()) { - arg0map[&event.pid()].clone() - } else { - "".to_string() - }; - match event.mask() { - MaskFlags::FAN_ACCESS_PERM - | MaskFlags::FAN_OPEN_PERM - | MaskFlags::FAN_OPEN_EXEC_PERM => { - let allowed = match std::fs::metadata(&path) { - Ok(metadata) => { - // is a directory or filled with content - metadata.is_dir() || ready.contains(&path) - } - Err(error) => error.kind() == std::io::ErrorKind::NotFound, - }; - if allowed || whitelist.contains(&arg0) || storage_provider.contains(&arg0) { - info!("<<<<< {} allowed", fd.as_raw_fd()); - if let Err(err) = - fan.write_response(FanotifyResponse::new(fd, Response::FAN_ALLOW)) - { - warn!("write response for {} failed: {}", fd.as_raw_fd(), err); - } + if event.mask().is_permission_event() { + // permission event + let Some(fd) = event.fd() else { + if init_flags & (InitFlags::FAN_REPORT_FID | InitFlags::FAN_REPORT_DIR_FID) + != InitFlags::empty() + { + warn!("fid not implementd"); } else { - let fd = event.forget_fd(); - info!("<<<<< {} defered", fd.as_raw_fd()); - if let Some(fds) = bufferdfds.get_mut(&path) { - fds.push(fd); - } else { - bufferdfds.insert(path, vec![fd]); - } + warn!("queue full"); } - } - MaskFlags::FAN_CLOSE_WRITE => { - if storage_provider.contains(&arg0) { - ready.insert(path.clone()); - if let Some(fds) = bufferdfds.remove(&path) { - for fd in fds { - if let Err(err) = fan.write_response(FanotifyResponse::new( - fd.as_fd(), - Response::FAN_ALLOW, - )) { - warn!("write response for {} failed: {}", fd.as_raw_fd(), err); - } - info!(">>>>> {} allowed(defer)", fd.as_raw_fd()); + continue; + }; + let path = match std::fs::read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())) { + Ok(p) => p, + Err(err) => { + warn!( + "failed to read fd link for fd {}: {:?}", + fd.as_raw_fd(), + err + ); + continue; + } + }; + let cmdline_raw = match std::fs::read(format!("/proc/{}/cmdline", event.pid())) { + Ok(raw) => raw, + Err(err) => { + warn!( + "failed to read pid cmdline for fd {}: {:?}", + event.pid(), + err + ); + continue; + } + }; + + let cmdline = if cmdline_raw.len() > 0 { + Some( + cmdline_raw + .split(|&b| b == 0) + .map(|v| String::from_utf8_lossy(v)) + .collect::>>(), + ) + } else { + None + }; + + trace!( + "++++++++ permission event:{} {:?} {:?} {:?}", + event.pid(), + event.mask(), + fd, + path + ); + let arg0 = if let Some(cmdline) = cmdline.as_ref() { + for (idx, arg) in cmdline.iter().enumerate() { + trace!(" - {}: {}", idx, arg); + } + + let arg0 = cmdline[0].to_string(); + + arg0map.insert(event.pid(), arg0.clone()); + + arg0 + } else if arg0map.contains_key(&event.pid()) { + arg0map[&event.pid()].clone() + } else { + "".to_string() + }; + match event.mask() { + MaskFlags::FAN_ACCESS_PERM + | MaskFlags::FAN_OPEN_PERM + | MaskFlags::FAN_OPEN_EXEC_PERM => { + let allowed = match std::fs::metadata(&path) { + Ok(metadata) => { + // is a directory or filled with content + metadata.is_dir() || ready.contains(&path) + } + Err(error) => error.kind() == std::io::ErrorKind::NotFound, + }; + if allowed || whitelist.contains(&arg0) || storage_provider.contains(&arg0) + { + info!("<<<<< {} allowed", fd.as_raw_fd()); + if let Err(err) = + fan.write_response(FanotifyResponse::new(fd, Response::FAN_ALLOW)) + { + warn!("write response for {} failed: {}", fd.as_raw_fd(), err); + } + } else { + let fd = event.forget_fd(); + info!("<<<<< {} defered", fd.as_raw_fd()); + if let Some(fds) = bufferdfds.get_mut(&path) { + fds.push(fd); + } else { + bufferdfds.insert(path, vec![fd]); } } } + MaskFlags::FAN_CLOSE_WRITE => { + if storage_provider.contains(&arg0) { + ready.insert(path.clone()); + if let Some(fds) = bufferdfds.remove(&path) { + for fd in fds { + if let Err(err) = fan.write_response(FanotifyResponse::new( + fd.as_fd(), + Response::FAN_ALLOW, + )) { + warn!( + "write response for {} failed: {}", + fd.as_raw_fd(), + err + ); + } + info!(">>>>> {} allowed(defer)", fd.as_raw_fd()); + } + } + } + } + _ => {} } - _ => {} + } else { + trace!( + "++++++++ notification event: {} {:?} {:?}", + event.pid(), + event.mask(), + event.event_info, + ); } } } diff --git a/fanotify/src/consts.rs b/fanotify/src/consts.rs index 4ca6062..3436adf 100644 --- a/fanotify/src/consts.rs +++ b/fanotify/src/consts.rs @@ -114,3 +114,12 @@ fa_bitflags! { FAN_EVENT_ON_CHILD; // enable events on direct } } + +impl MaskFlags { + pub fn is_permission_event(&self) -> bool { + match self.bits() { + FAN_OPEN_PERM | FAN_ACCESS | FAN_OPEN_EXEC_PERM => true, + _ => false, + } + } +} \ No newline at end of file diff --git a/fanotify/src/fanotify.rs b/fanotify/src/fanotify.rs index b1b600d..ba1ce95 100644 --- a/fanotify/src/fanotify.rs +++ b/fanotify/src/fanotify.rs @@ -105,6 +105,53 @@ impl Fanotify { ); let event = uninited.assume_init(); + #[repr(C)] + union fanotify_event_info { + header: libc::fanotify_event_info_header, + fid: libc::fanotify_event_info_fid, + pidfd: libc::fanotify_event_info_pidfd, + error: libc::fanotify_event_info_error, + } + + let mut event_info = Vec::new(); + + const HEADER_SIZE: usize = std::mem::size_of::(); + + let mut header_offset = EVENT_SIZE; + while header_offset + HEADER_SIZE < event.event_len as usize { + // pre-check + let mut uninited: MaybeUninit = MaybeUninit::uninit(); + + std::ptr::copy( + buffer.as_ptr().add(offset+EVENT_SIZE), + uninited.as_mut_ptr().cast(), + EVENT_SIZE, + ); + std::ptr::copy( + buffer.as_ptr().add(offset + header_offset), + uninited.as_mut_ptr().add(header_offset).cast(), + (*uninited.as_ptr()).header.len as usize - HEADER_SIZE, + ); + let event_info_len = (*uninited.as_ptr()).header.len as usize; + let event_info_type = (*uninited.as_ptr()).header.info_type; + + match event_info_type { + libc::FAN_EVENT_INFO_TYPE_FID | libc::FAN_EVENT_INFO_TYPE_DFID_NAME | libc::FAN_EVENT_INFO_TYPE_DFID=> { + event_info.push(EventInfo::Fid(uninited.assume_init().fid)); + } + libc::FAN_EVENT_INFO_TYPE_PIDFD => { + event_info.push(EventInfo::PidFd(uninited.assume_init().pidfd)); + } + libc::FAN_EVENT_INFO_TYPE_ERROR => { + event_info.push(EventInfo::PidFd(uninited.assume_init().pidfd)); + } + _ => { + panic!("unknown fan_event_info_header.type={event_info_type}") + } + } + header_offset += event_info_len; + } + // #define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, (struct fanotify_event_metadata*)(((char*)(meta)) + (meta) -> event_len) // meta = FAN_EVENT_NEXT(meta, len) translate to: // len -= meta->event_len; // shrink rest length @@ -115,7 +162,7 @@ impl Fanotify { // + (meta) -> event_len // add event_len to move to next // ); offset += event.event_len as usize; - result.push(Event::new(event)); + result.push(Event::new(event, event_info)); } } @@ -139,12 +186,17 @@ impl Fanotify { pub struct Event { pub fanotify_event_metadata: libc::fanotify_event_metadata, + pub event_info: Vec, } impl Event { - fn new(fanotify_event_metadata: libc::fanotify_event_metadata) -> Self { + fn new( + fanotify_event_metadata: libc::fanotify_event_metadata, + event_info: Vec, + ) -> Self { Self { fanotify_event_metadata, + event_info, } } // compatible to nix::sys::fanotify::FanotifyEvent @@ -191,6 +243,13 @@ impl Drop for Event { } } +#[derive(Debug)] +pub enum EventInfo { + Fid(libc::fanotify_event_info_fid), + PidFd(libc::fanotify_event_info_pidfd), + Error(libc::fanotify_event_info_error), +} + pub struct Response { inner: libc::fanotify_response, }