diff --git a/fact-ebpf/src/bpf/bound_path.h b/fact-ebpf/src/bpf/bound_path.h index 7a2091f9..cf249b68 100644 --- a/fact-ebpf/src/bpf/bound_path.h +++ b/fact-ebpf/src/bpf/bound_path.h @@ -38,8 +38,12 @@ __always_inline static struct bound_path_t* _path_read(struct path* path, bound_ return bound_path; } -__always_inline static struct bound_path_t* path_read_unchecked(struct path* path) { - return _path_read(path, BOUND_PATH_MAIN, true); +__always_inline static struct bound_path_t* path_read_unchecked(struct path* path, bool use_bpf_d_path) { + return _path_read(path, BOUND_PATH_MAIN, use_bpf_d_path); +} + +__always_inline static struct bound_path_t* path_read_alt_unchecked(struct path* path, bool use_bpf_d_path) { + return _path_read(path, BOUND_PATH_ALTERNATE, use_bpf_d_path); } __always_inline static struct bound_path_t* path_read(struct path* path) { diff --git a/fact-ebpf/src/bpf/events.h b/fact-ebpf/src/bpf/events.h index 00d91027..249daf53 100644 --- a/fact-ebpf/src/bpf/events.h +++ b/fact-ebpf/src/bpf/events.h @@ -123,9 +123,9 @@ __always_inline static void submit_rename_event(struct submit_event_args_t* args } args->event->type = FILE_ACTIVITY_RENAME; - bpf_probe_read_str(args->event->rename.filename, PATH_MAX, old_filename); - inode_copy(&args->event->rename.inode, old_inode); - args->event->rename.monitored = old_monitored; + bpf_probe_read_str(args->event->from.filename, PATH_MAX, old_filename); + inode_copy(&args->event->from.inode, old_inode); + args->event->from.monitored = old_monitored; __submit_event(args, path_hooks_support_bpf_d_path); } @@ -212,3 +212,36 @@ __always_inline static void submit_acl_event(struct submit_event_args_t* args, // inode_set_acl does not support bpf_d_path (no struct path available) __submit_event(args, false); } + +__always_inline static void submit_mount_event(struct submit_event_args_t* args) { + if (!reserve_event(args)) { + return; + } + args->event->type = FILE_ACTIVITY_MOUNT; + + __submit_event(args, true); +} + +__always_inline static void submit_umount_event(struct submit_event_args_t* args) { + if (!reserve_event(args)) { + return; + } + args->event->type = FILE_ACTIVITY_UMOUNT; + + __submit_event(args, true); +} + +__always_inline static void submit_move_mount_event(struct submit_event_args_t* args, + const char from_filename[PATH_MAX], + inode_key_t* from_inode, + monitored_t from_monitored) { + if (!reserve_event(args)) { + return; + } + args->event->type = FILE_ACTIVITY_MOVE_MOUNT; + bpf_probe_read_str(args->event->from.filename, PATH_MAX, from_filename); + inode_copy(&args->event->from.inode, from_inode); + args->event->from.monitored = from_monitored; + + __submit_event(args, false); +} diff --git a/fact-ebpf/src/bpf/main.c b/fact-ebpf/src/bpf/main.c index b880a573..7f2a5919 100644 --- a/fact-ebpf/src/bpf/main.c +++ b/fact-ebpf/src/bpf/main.c @@ -54,7 +54,7 @@ int BPF_PROG(trace_file_open, struct file* file) { } } - struct bound_path_t* path = path_read_unchecked(&file->f_path); + struct bound_path_t* path = path_read_unchecked(&file->f_path, true); if (path == NULL) { bpf_printk("Failed to read path"); m->file_open.error++; @@ -484,3 +484,110 @@ int BPF_PROG(trace_path_rmdir, struct path* dir, struct dentry* dentry) { submit_rmdir_event(&args); return 0; } + +SEC("lsm/sb_mount") +int BPF_PROG(trace_sb_mount, const char* dev_name, struct path* path, const char* type, unsigned long flags, void* data) { + struct metrics_t* m = get_metrics(); + if (m == NULL) { + return 0; + } + struct submit_event_args_t args = {.metrics = &m->sb_mount}; + args.metrics->total++; + + struct bound_path_t* bound_path = path_read_unchecked(path, true); + if (bound_path == NULL) { + bpf_printk("Failed to read mount directory"); + args.metrics->error++; + return 0; + } + args.filename = bound_path->path; + + args.inode = inode_to_key(path->dentry->d_inode); + + struct dentry* parent_dentry = BPF_CORE_READ(path, dentry, d_parent); + struct inode* parent_inode_ptr = parent_dentry ? BPF_CORE_READ(parent_dentry, d_inode) : NULL; + args.parent_inode = inode_to_key(parent_inode_ptr); + + args.monitored = is_monitored(&args.inode, bound_path, &args.parent_inode); + if (args.monitored == NOT_MONITORED) { + args.metrics->ignored++; + return 0; + } + + submit_mount_event(&args); + + return 0; +} + +SEC("lsm/sb_umount") +int BPF_PROG(trace_sb_umount, struct vfsmount* mnt, int flags) { + struct metrics_t* m = get_metrics(); + if (m == NULL) { + return 0; + } + struct submit_event_args_t args = {.metrics = &m->sb_mount}; + args.metrics->total++; + + struct path p = {.dentry = BPF_CORE_READ(mnt, mnt_root), .mnt = mnt}; + struct bound_path_t* bound_path = _path_read(&p, BOUND_PATH_MAIN, false); + if (bound_path == NULL) { + bpf_printk("Failed to read umount directory"); + args.metrics->error++; + return 0; + } + args.filename = bound_path->path; + + args.inode = inode_to_key(mnt->mnt_root->d_inode); + + struct dentry* parent_dentry = BPF_CORE_READ(mnt, mnt_root, d_parent); + struct inode* parent_inode_ptr = parent_dentry ? BPF_CORE_READ(parent_dentry, d_inode) : NULL; + args.parent_inode = inode_to_key(parent_inode_ptr); + + args.monitored = is_monitored(&args.inode, bound_path, &args.parent_inode); + if (args.monitored == NOT_MONITORED) { + args.metrics->ignored++; + return 0; + } + + submit_umount_event(&args); + + return 0; +} + +SEC("lsm/move_mount") +int BPF_PROG(trace_move_mount, struct path* from, struct path* to) { + struct metrics_t* m = get_metrics(); + if (m == NULL) { + return 0; + } + struct submit_event_args_t args = {.metrics = &m->move_mount}; + + args.metrics->total++; + + struct bound_path_t* to_path = path_read_unchecked(to, false); + if (to_path == NULL) { + bpf_printk("Failed to read to_path"); + goto error; + } + args.filename = to_path->path; + + struct bound_path_t* from_path = path_read_alt_unchecked(from, false); + if (from_path == NULL) { + bpf_printk("Failed to read from_path"); + goto error; + } + + args.inode = inode_to_key(to->dentry->d_inode); + args.parent_inode = inode_to_key(to->dentry->d_inode); + args.monitored = is_monitored(&args.inode, to_path, &args.parent_inode); + + inode_key_t from_inode = inode_to_key(from->dentry->d_inode); + monitored_t from_monitored = is_monitored(&from_inode, from_path, NULL); + + submit_move_mount_event(&args, from_path->path, &from_inode, from_monitored); + return 0; + +error: + args.metrics->error++; + return 0; +} diff --git a/fact-ebpf/src/bpf/types.h b/fact-ebpf/src/bpf/types.h index ce496672..80b94d22 100644 --- a/fact-ebpf/src/bpf/types.h +++ b/fact-ebpf/src/bpf/types.h @@ -115,6 +115,9 @@ typedef enum file_activity_type_t { FILE_ACTIVITY_SETXATTR, FILE_ACTIVITY_REMOVEXATTR, FILE_ACTIVITY_ACL_SET, + FILE_ACTIVITY_MOUNT, + FILE_ACTIVITY_UMOUNT, + FILE_ACTIVITY_MOVE_MOUNT, } file_activity_type_t; struct event_t { @@ -140,7 +143,7 @@ struct event_t { char filename[PATH_MAX]; inode_key_t inode; monitored_t monitored; - } rename; + } from; // Used by events that have two paths (like rename or move_mount). struct { char name[XATTR_NAME_MAX_LEN]; } xattr; @@ -194,4 +197,7 @@ struct metrics_t { struct metrics_by_hook_t inode_setxattr; struct metrics_by_hook_t inode_removexattr; struct metrics_by_hook_t inode_set_acl; + struct metrics_by_hook_t sb_mount; + struct metrics_by_hook_t sb_umount; + struct metrics_by_hook_t move_mount; }; diff --git a/fact-ebpf/src/lib.rs b/fact-ebpf/src/lib.rs index 4117e983..57acf1c0 100644 --- a/fact-ebpf/src/lib.rs +++ b/fact-ebpf/src/lib.rs @@ -158,6 +158,9 @@ impl_metrics_t!( inode_setxattr, inode_removexattr, inode_set_acl, + sb_mount, + sb_umount, + move_mount, ); unsafe impl Pod for metrics_t {} diff --git a/fact/src/event/mod.rs b/fact/src/event/mod.rs index 5792e174..c38c543f 100644 --- a/fact/src/event/mod.rs +++ b/fact/src/event/mod.rs @@ -109,16 +109,13 @@ impl Event { }; FileData::Chmod(data) } - EventTestData::Rename(old_path) => { - let data = RenameFileData { - new: inner, - old: BaseFileData { - filename: old_path, - ..Default::default() - }, - }; - FileData::Rename(data) - } + EventTestData::Rename(old_path) => FileData::Rename { + new: inner, + old: BaseFileData { + filename: old_path, + ..Default::default() + }, + }, }; Ok(Event { @@ -150,7 +147,19 @@ impl Event { } pub fn is_rename(&self) -> bool { - matches!(self.file, FileData::Rename(_)) + matches!(self.file, FileData::Rename { .. }) + } + + pub fn is_mount(&self) -> bool { + matches!(self.file, FileData::Mount(_)) + } + + pub fn is_umount(&self) -> bool { + matches!(self.file, FileData::Umount(_)) + } + + pub fn is_move_mount(&self) -> bool { + matches!(self.file, FileData::MoveMount { .. }) } /// Unwrap the inner FileData and return the inode that triggered @@ -160,14 +169,18 @@ impl Event { /// the 'new' inode will be returned. pub fn get_inode(&self) -> &inode_key_t { match &self.file { - FileData::Open(data) => &data.inode, - FileData::Creation(data) => &data.inode, - FileData::MkDir(data) => &data.inode, - FileData::RmDir(data) => &data.inode, - FileData::Unlink(data) => &data.inode, + FileData::Open(data) + | FileData::Creation(data) + | FileData::MkDir(data) + | FileData::RmDir(data) + | FileData::Unlink(data) + | FileData::Mount(data) + | FileData::Umount(data) => &data.inode, FileData::Chmod(data) => &data.inner.inode, FileData::Chown(data) => &data.inner.inode, - FileData::Rename(data) => &data.new.inode, + FileData::Rename { new: data, .. } | FileData::MoveMount { to: data, .. } => { + &data.inode + } FileData::SetXattr(data) => &data.inner.inode, FileData::RemoveXattr(data) => &data.inner.inode, FileData::AclSet(data) => &data.inner.inode, @@ -177,14 +190,18 @@ impl Event { /// Get the parent inode for the file in this event. pub fn get_parent_inode(&self) -> &inode_key_t { match &self.file { - FileData::Open(data) => &data.parent_inode, - FileData::Creation(data) => &data.parent_inode, - FileData::MkDir(data) => &data.parent_inode, - FileData::RmDir(data) => &data.parent_inode, - FileData::Unlink(data) => &data.parent_inode, + FileData::Open(data) + | FileData::Creation(data) + | FileData::MkDir(data) + | FileData::RmDir(data) + | FileData::Unlink(data) + | FileData::Mount(data) + | FileData::Umount(data) => &data.parent_inode, FileData::Chmod(data) => &data.inner.parent_inode, FileData::Chown(data) => &data.inner.parent_inode, - FileData::Rename(data) => &data.new.parent_inode, + FileData::Rename { new: data, .. } | FileData::MoveMount { to: data, .. } => { + &data.parent_inode + } FileData::SetXattr(data) => &data.inner.parent_inode, FileData::RemoveXattr(data) => &data.inner.parent_inode, FileData::AclSet(data) => &data.inner.parent_inode, @@ -196,21 +213,27 @@ impl Event { /// will be returned. pub fn get_old_inode(&self) -> Option<&inode_key_t> { match &self.file { - FileData::Rename(data) => Some(&data.old.inode), + FileData::Rename { old: data, .. } | FileData::MoveMount { from: data, .. } => { + Some(&data.inode) + } _ => None, } } pub fn get_filename(&self) -> &PathBuf { match &self.file { - FileData::Open(data) => &data.filename, - FileData::Creation(data) => &data.filename, - FileData::MkDir(data) => &data.filename, - FileData::RmDir(data) => &data.filename, - FileData::Unlink(data) => &data.filename, + FileData::Open(data) + | FileData::Creation(data) + | FileData::MkDir(data) + | FileData::RmDir(data) + | FileData::Unlink(data) + | FileData::Mount(data) + | FileData::Umount(data) => &data.filename, FileData::Chmod(data) => &data.inner.filename, FileData::Chown(data) => &data.inner.filename, - FileData::Rename(data) => &data.new.filename, + FileData::Rename { new: data, .. } | FileData::MoveMount { to: data, .. } => { + &data.filename + } FileData::SetXattr(data) => &data.inner.filename, FileData::RemoveXattr(data) => &data.inner.filename, FileData::AclSet(data) => &data.inner.filename, @@ -219,21 +242,27 @@ impl Event { pub fn get_old_filename(&self) -> Option<&PathBuf> { match &self.file { - FileData::Rename(data) => Some(&data.old.filename), + FileData::Rename { old: data, .. } | FileData::MoveMount { from: data, .. } => { + Some(&data.filename) + } _ => None, } } pub fn get_host_path(&self) -> &PathBuf { match &self.file { - FileData::Open(data) => &data.host_file, - FileData::Creation(data) => &data.host_file, - FileData::MkDir(data) => &data.host_file, - FileData::RmDir(data) => &data.host_file, - FileData::Unlink(data) => &data.host_file, + FileData::Open(data) + | FileData::Creation(data) + | FileData::MkDir(data) + | FileData::RmDir(data) + | FileData::Unlink(data) + | FileData::Mount(data) + | FileData::Umount(data) => &data.host_file, FileData::Chmod(data) => &data.inner.host_file, FileData::Chown(data) => &data.inner.host_file, - FileData::Rename(data) => &data.new.host_file, + FileData::Rename { new: data, .. } | FileData::MoveMount { to: data, .. } => { + &data.host_file + } FileData::SetXattr(data) => &data.inner.host_file, FileData::RemoveXattr(data) => &data.inner.host_file, FileData::AclSet(data) => &data.inner.host_file, @@ -242,7 +271,9 @@ impl Event { pub fn get_old_host_path(&self) -> Option<&PathBuf> { match &self.file { - FileData::Rename(data) => Some(&data.old.host_file), + FileData::Rename { old: data, .. } | FileData::MoveMount { from: data, .. } => { + Some(&data.host_file) + } _ => None, } } @@ -253,14 +284,18 @@ impl Event { /// the 'new' host_file will be set. pub fn set_host_path(&mut self, host_path: PathBuf) { match &mut self.file { - FileData::Open(data) => data.host_file = host_path, - FileData::Creation(data) => data.host_file = host_path, - FileData::MkDir(data) => data.host_file = host_path, - FileData::RmDir(data) => data.host_file = host_path, - FileData::Unlink(data) => data.host_file = host_path, + FileData::Open(data) + | FileData::Creation(data) + | FileData::MkDir(data) + | FileData::RmDir(data) + | FileData::Unlink(data) + | FileData::Mount(data) + | FileData::Umount(data) => data.host_file = host_path, FileData::Chmod(data) => data.inner.host_file = host_path, FileData::Chown(data) => data.inner.host_file = host_path, - FileData::Rename(data) => data.new.host_file = host_path, + FileData::Rename { new: data, .. } | FileData::MoveMount { to: data, .. } => { + data.host_file = host_path + } FileData::SetXattr(data) => data.inner.host_file = host_path, FileData::RemoveXattr(data) => data.inner.host_file = host_path, FileData::AclSet(data) => data.inner.host_file = host_path, @@ -270,21 +305,28 @@ impl Event { /// Same as `set_host_path` but setting the 'old' host_file for /// operations that have one, like rename. pub fn set_old_host_path(&mut self, host_path: PathBuf) { - if let FileData::Rename(data) = &mut self.file { - data.old.host_file = host_path + match &mut self.file { + FileData::Rename { old: data, .. } | FileData::MoveMount { from: data, .. } => { + data.host_file = host_path + } + _ => unreachable!("Called set_old_host_path on invalid type"), } } pub fn get_monitored(&self) -> monitored_t { match &self.file { - FileData::Open(data) => data.monitored, - FileData::Creation(data) => data.monitored, - FileData::MkDir(data) => data.monitored, - FileData::RmDir(data) => data.monitored, - FileData::Unlink(data) => data.monitored, + FileData::Open(data) + | FileData::Creation(data) + | FileData::MkDir(data) + | FileData::RmDir(data) + | FileData::Unlink(data) + | FileData::Mount(data) + | FileData::Umount(data) => data.monitored, FileData::Chmod(data) => data.inner.monitored, FileData::Chown(data) => data.inner.monitored, - FileData::Rename(data) => data.new.monitored, + FileData::Rename { new: data, .. } | FileData::MoveMount { to: data, .. } => { + data.monitored + } FileData::SetXattr(data) => data.inner.monitored, FileData::RemoveXattr(data) => data.inner.monitored, FileData::AclSet(data) => data.inner.monitored, @@ -293,7 +335,9 @@ impl Event { pub fn get_old_monitored(&self) -> Option { match &self.file { - FileData::Rename(data) => Some(data.old.monitored), + FileData::Rename { old: data, .. } | FileData::MoveMount { from: data, .. } => { + Some(data.monitored) + } _ => None, } } @@ -379,10 +423,19 @@ pub enum FileData { Unlink(BaseFileData), Chmod(ChmodFileData), Chown(ChownFileData), - Rename(RenameFileData), + Rename { + new: BaseFileData, + old: BaseFileData, + }, SetXattr(XattrFileData), RemoveXattr(XattrFileData), AclSet(AclSetFileData), + Mount(BaseFileData), + MoveMount { + to: BaseFileData, + from: BaseFileData, + }, + Umount(BaseFileData), } impl FileData { @@ -394,6 +447,16 @@ impl FileData { monitored: monitored_t, extra_data: fact_ebpf::event_t__bindgen_ty_1, ) -> anyhow::Result { + fn read_from_data( + extra_data: fact_ebpf::event_t__bindgen_ty_1, + ) -> anyhow::Result { + let filename = unsafe { extra_data.from.filename }; + let inode = unsafe { extra_data.from.inode }; + let monitored = unsafe { extra_data.from.monitored }; + + BaseFileData::new(filename, inode, Default::default(), monitored) + } + let inner = BaseFileData::new(filename, inode, parent_inode, monitored)?; let file = match event_type { file_activity_type_t::FILE_ACTIVITY_OPEN => FileData::Open(inner), @@ -420,19 +483,8 @@ impl FileData { FileData::Chown(data) } file_activity_type_t::FILE_ACTIVITY_RENAME => { - let old_filename = unsafe { extra_data.rename.filename }; - let old_inode = unsafe { extra_data.rename.inode }; - let old_monitored = unsafe { extra_data.rename.monitored }; - let data = RenameFileData { - new: inner, - old: BaseFileData::new( - old_filename, - old_inode, - Default::default(), - old_monitored, - )?, - }; - FileData::Rename(data) + let old = read_from_data(extra_data)?; + FileData::Rename { new: inner, old } } file_activity_type_t::FILE_ACTIVITY_SETXATTR => { let xattr_name = slice_to_string( @@ -461,6 +513,12 @@ impl FileData { entries, }) } + file_activity_type_t::FILE_ACTIVITY_MOUNT => FileData::Mount(inner), + file_activity_type_t::FILE_ACTIVITY_UMOUNT => FileData::Umount(inner), + file_activity_type_t::FILE_ACTIVITY_MOVE_MOUNT => { + let from = read_from_data(extra_data)?; + FileData::MoveMount { to: inner, from } + } invalid => unreachable!("Invalid event type: {invalid:?}"), }; @@ -508,14 +566,26 @@ impl From for fact_api::file_activity::File { let f_act = fact_api::FileOwnershipChange::from(event); fact_api::file_activity::File::Ownership(f_act) } - FileData::Rename(event) => { - let f_act = fact_api::FileRename::from(event); + FileData::Rename { new, old } => { + let f_act = fact_api::FileRename { + new: Some(new.into()), + old: Some(old.into()), + }; fact_api::file_activity::File::Rename(f_act) } FileData::AclSet(event) => { let f_act = fact_api::FileAclChange::from(event); fact_api::file_activity::File::Acl(f_act) } + FileData::Mount(_) => { + unreachable!("Mount event reached protobuf conversion"); + } + FileData::MoveMount { .. } => { + unreachable!("MoveMount event reached protobuf conversion"); + } + FileData::Umount(_) => { + unreachable!("Umount event reached protobuf conversion"); + } } } } @@ -531,7 +601,16 @@ impl PartialEq for FileData { (FileData::Unlink(this), FileData::Unlink(other)) => this == other, (FileData::Chmod(this), FileData::Chmod(other)) => this == other, (FileData::Chown(this), FileData::Chown(other)) => this == other, - (FileData::Rename(this), FileData::Rename(other)) => this == other, + ( + FileData::Rename { + new: l_new, + old: l_old, + }, + FileData::Rename { + new: r_new, + old: r_old, + }, + ) => l_new == r_new && l_old == r_old, (FileData::SetXattr(this), FileData::SetXattr(other)) => this == other, (FileData::RemoveXattr(this), FileData::RemoveXattr(other)) => this == other, (FileData::AclSet(this), FileData::AclSet(other)) => { @@ -656,12 +735,6 @@ impl From for fact_api::FileOwnershipChange { } } -#[derive(Debug, Clone, Serialize)] -pub struct RenameFileData { - new: BaseFileData, - old: BaseFileData, -} - #[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] pub enum AclTag { UserObj, @@ -726,17 +799,6 @@ pub struct AclSetFileData { entries: Vec, } -impl From for fact_api::FileRename { - fn from(RenameFileData { new, old }: RenameFileData) -> Self { - let new = fact_api::FileActivityBase::from(new); - let old = fact_api::FileActivityBase::from(old); - fact_api::FileRename { - old: Some(old), - new: Some(new), - } - } -} - impl From for i32 { fn from(tag: AclTag) -> Self { match tag { @@ -781,13 +843,6 @@ impl From for fact_api::FileAclChange { } } -#[cfg(test)] -impl PartialEq for RenameFileData { - fn eq(&self, other: &Self) -> bool { - self.new == other.new && self.old == other.old - } -} - #[derive(Debug, Clone, Serialize)] pub struct XattrFileData { inner: BaseFileData, diff --git a/fact/src/host_scanner.rs b/fact/src/host_scanner.rs index 273bd5df..41f997e3 100644 --- a/fact/src/host_scanner.rs +++ b/fact/src/host_scanner.rs @@ -384,6 +384,70 @@ You can increase this limit with: } } + fn handle_mount_inner(&self, path: &Path, monitored: monitored_t) { + if monitored != monitored_t::MONITORED_BY_INODE || path.as_os_str().is_empty() { + return; + } + + if let Err(e) = self.scan() { + warn!("Host scan failed: {e:?}"); + } + } + + /// Handle a mount being created in a monitored directory. + /// + /// This should really do a partial scan of the directory where the + /// mount is being created, but we don't have an easy way to do that + /// at the moment, so we trigger a full scan instead. + fn handle_mount_event(&self, event: &Event) { + self.handle_mount_inner(event.get_host_path(), event.get_monitored()); + } + + fn handle_umount_inner(&self, prefix: &Path, monitored: monitored_t) { + if monitored != monitored_t::MONITORED_BY_INODE || prefix.as_os_str().is_empty() { + return; + } + + // Cleanup any items that are no longer mounted + self.inode_map.borrow_mut().retain(|inode, path| { + if path.starts_with(prefix) && path != prefix { + if let Err(e) = self.kernel_inode_map.borrow_mut().remove(inode) { + warn!( + "Failed to remove kernel entry for {}: {e:?}", + path.display() + ); + } + false + } else { + true + } + }); + } + + /// Handle a monitored directory being unmounted. + /// + /// Iterates over the inode_map entries finding files that have a + /// prefix that matches the host path in the event and removing them. + fn handle_umount_event(&self, event: &Event) { + self.handle_umount_inner(event.get_host_path(), event.get_monitored()); + } + + /// Handle a move mount event. + /// + /// In practise, a move mount event behaves as a mount at the 'to' + /// location and a umount at the 'from' location, so this method + /// behaves just like that. + fn handle_move_mount_event(&self, event: &Event) { + self.handle_mount_inner(event.get_host_path(), event.get_monitored()); + + // unwrap is safe here because get_old_* always return Some() + // for move mount events. + self.handle_umount_inner( + event.get_old_host_path().unwrap(), + event.get_old_monitored().unwrap(), + ); + } + /// Periodically notify the host scanner main task that a scan needs /// to happen. /// @@ -457,6 +521,19 @@ You can increase this limit with: continue; } + // Handle mount events and move on. + if event.is_mount() { + self.handle_mount_event(&event); + continue; + } else if event.is_umount() { + self.handle_umount_event(&event); + continue; + } else if event.is_move_mount() { + self.handle_move_mount_event(&event); + continue; + } + + if event.is_rename() { self.handle_rename_event(&mut event); } if event.is_monitored_by_parent() && diff --git a/fact/src/metrics/kernel_metrics.rs b/fact/src/metrics/kernel_metrics.rs index d1e77bfb..37238c4b 100644 --- a/fact/src/metrics/kernel_metrics.rs +++ b/fact/src/metrics/kernel_metrics.rs @@ -73,4 +73,7 @@ define_kernel_metrics!( inode_setxattr, inode_removexattr, inode_set_acl, + sb_mount, + sb_umount, + move_mount, );