diff --git a/crates/ospect/src/fs.rs b/crates/ospect/src/fs.rs index 9784929e..868cca11 100644 --- a/crates/ospect/src/fs.rs +++ b/crates/ospect/src/fs.rs @@ -192,9 +192,9 @@ where #[derive(Debug)] pub struct Mount { /// Name of the mounted device. - pub source: String, + pub name: String, /// Mount point, i.e., where the mounted filesystem is available. - pub target: std::path::PathBuf, + pub path: std::path::PathBuf, /// Type of the mounted filesystem (e.g. `ext4`, `ramfs`, `proc`). pub fs_type: String, } @@ -356,7 +356,7 @@ mod tests { .unwrap() .map(Result::unwrap); - assert!(mounts.find(|mount| mount.target == Path::new("/")).is_some()); + assert!(mounts.find(|mount| mount.path == Path::new("/")).is_some()); } #[cfg(target_family = "windows")] @@ -374,7 +374,7 @@ mod tests { system_drive_path.push(std::path::MAIN_SEPARATOR_STR); assert! { - mounts.find(|mount| &mount.target == &system_drive_path).is_some() + mounts.find(|mount| &mount.path == &system_drive_path).is_some() }; } } diff --git a/crates/ospect/src/fs/linux.rs b/crates/ospect/src/fs/linux.rs index 5323b8d0..c57f5b56 100644 --- a/crates/ospect/src/fs/linux.rs +++ b/crates/ospect/src/fs/linux.rs @@ -222,16 +222,16 @@ impl Mounts { // There is more data in the file but we don't care for the time being // and only "parse" the first three columns. - let source = cols.next() + let name = cols.next() .ok_or_else(|| std::io::ErrorKind::InvalidData)?; - let target = cols.next() + let path = cols.next() .ok_or_else(|| std::io::ErrorKind::InvalidData)?; let fs_type = cols.next() .ok_or_else(|| std::io::ErrorKind::InvalidData)?; Ok(Mount { - source: source.into(), - target: target.into(), + name: name.into(), + path: path.into(), fs_type: fs_type.into(), }) } @@ -414,23 +414,23 @@ proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 let mut mounts = Mounts::new(MTAB.as_bytes()); let mount = mounts.next().unwrap().unwrap(); - assert_eq!(mount.source, "sysfs"); - assert_eq!(mount.target, Path::new("/sys")); + assert_eq!(mount.name, "sysfs"); + assert_eq!(mount.path, Path::new("/sys")); assert_eq!(mount.fs_type, "sysfs"); let mount = mounts.next().unwrap().unwrap(); - assert_eq!(mount.source, "proc"); - assert_eq!(mount.target, Path::new("/proc")); + assert_eq!(mount.name, "proc"); + assert_eq!(mount.path, Path::new("/proc")); assert_eq!(mount.fs_type, "proc"); let mount = mounts.next().unwrap().unwrap(); - assert_eq!(mount.source, "/dev/foobar"); - assert_eq!(mount.target, Path::new("/")); + assert_eq!(mount.name, "/dev/foobar"); + assert_eq!(mount.path, Path::new("/")); assert_eq!(mount.fs_type, "ext4"); let mount = mounts.next().unwrap().unwrap(); - assert_eq!(mount.source, "/dev/quux"); - assert_eq!(mount.target, Path::new("/usr/quux")); + assert_eq!(mount.name, "/dev/quux"); + assert_eq!(mount.path, Path::new("/usr/quux")); assert_eq!(mount.fs_type, "ext4"); assert!(mounts.next().is_none()); diff --git a/crates/ospect/src/fs/macos.rs b/crates/ospect/src/fs/macos.rs index 1b34006c..5951c19f 100644 --- a/crates/ospect/src/fs/macos.rs +++ b/crates/ospect/src/fs/macos.rs @@ -244,11 +244,11 @@ pub fn mounts() -> std::io::Result>> // safety invariants required by the `from_ptr` call are met. Note that // `statfs` is now on the stack, so the lifetime of this reference is // valid until the end of this scope. - let source = unsafe { + let name = unsafe { std::ffi::CStr::from_ptr(statfs.f_mntfromname.as_ptr()) }.to_string_lossy(); // SAFETY: Same as above. - let target = unsafe { + let path = unsafe { std::ffi::CStr::from_ptr(statfs.f_mntonname.as_ptr()) }.to_bytes(); // SAFETY: Same as above. @@ -259,8 +259,8 @@ pub fn mounts() -> std::io::Result>> use std::os::unix::ffi::OsStrExt as _; mounts.push(Mount { - source: source.into_owned(), - target: PathBuf::from(OsStr::from_bytes(target)), + name: name.into_owned(), + path: PathBuf::from(OsStr::from_bytes(path)), fs_type: fs_type.into_owned(), }); } diff --git a/crates/ospect/src/fs/windows.rs b/crates/ospect/src/fs/windows.rs index cbd2f0aa..702f04b0 100644 --- a/crates/ospect/src/fs/windows.rs +++ b/crates/ospect/src/fs/windows.rs @@ -62,9 +62,9 @@ pub fn mounts() -> std::io::Result>> for mount_point in VolumeMountPoints::new(&name_buf)? { results.push(Ok(Mount { - source: OsString::from_wide(&name_buf[0..name_len]) + name: OsString::from_wide(&name_buf[0..name_len]) .to_string_lossy().into_owned(), - target: mount_point, + path: mount_point, fs_type: OsString::from_wide(&fs_type_buf[0..fs_type_len]) .to_string_lossy().into_owned(), })); diff --git a/crates/rrg-proto/build.rs b/crates/rrg-proto/build.rs index 11cd82a6..a7657995 100644 --- a/crates/rrg-proto/build.rs +++ b/crates/rrg-proto/build.rs @@ -32,6 +32,7 @@ const PROTOS_V2: &'static [&'static str] = &[ "../../proto/rrg/action/get_system_metadata.proto", "../../proto/rrg/action/list_connections.proto", "../../proto/rrg/action/list_interfaces.proto", + "../../proto/rrg/action/list_mounts.proto", ]; fn main() { diff --git a/crates/rrg-proto/src/lib.rs b/crates/rrg-proto/src/lib.rs index 59ae2295..a927b8f1 100644 --- a/crates/rrg-proto/src/lib.rs +++ b/crates/rrg-proto/src/lib.rs @@ -105,6 +105,18 @@ pub mod v2 { } } + impl From for fs::Mount { + + fn from(mount: ospect::fs::Mount) -> fs::Mount { + let mut proto = fs::Mount::default(); + proto.set_name(mount.name); + proto.set_path(mount.path.into()); + proto.set_fs_type(mount.fs_type); + + proto + } + } + impl From for net::IpAddress { fn from(addr: std::net::Ipv4Addr) -> net::IpAddress { diff --git a/crates/rrg/Cargo.toml b/crates/rrg/Cargo.toml index 759a52dc..7d3ef0dc 100644 --- a/crates/rrg/Cargo.toml +++ b/crates/rrg/Cargo.toml @@ -13,6 +13,7 @@ default = [ "action-get_filesystem_timeline", "action-list_connections", "action-list_interfaces", + "action-list_mounts", # These actions are deprecated (awaiting migration to the new protocol). "action-insttime", @@ -31,6 +32,7 @@ action-get_file_contents = ["dep:sha2"] action-get_filesystem_timeline = ["dep:flate2", "dep:sha2"] action-list_connections = [] action-list_interfaces = [] +action-list_mounts = [] # These actions are deprecated (awaiting migration to the new protocol). action-insttime = [] diff --git a/crates/rrg/src/action.rs b/crates/rrg/src/action.rs index 966522ba..4e514eeb 100644 --- a/crates/rrg/src/action.rs +++ b/crates/rrg/src/action.rs @@ -36,6 +36,9 @@ pub mod list_connections; #[cfg(feature = "action-list_interfaces")] pub mod list_interfaces; +#[cfg(feature = "action-list_mounts")] +pub mod list_mounts; + use log::info; /// Dispatches the given `request` to an appropriate action handler. @@ -86,6 +89,10 @@ where ListInterfaces => { handle(session, request, self::list_interfaces::handle) } + #[cfg(feature = "action-list_mounts")] + ListMounts => { + handle(session, request, self::list_mounts::handle) + } // We allow `unreachable_patterns` because otherwise we get a warning if // we compile with all the actions enabled. #[allow(unreachable_patterns)] diff --git a/crates/rrg/src/action/deprecated/filesystems.rs b/crates/rrg/src/action/deprecated/filesystems.rs index fd0023df..a29b6a89 100644 --- a/crates/rrg/src/action/deprecated/filesystems.rs +++ b/crates/rrg/src/action/deprecated/filesystems.rs @@ -90,8 +90,8 @@ impl crate::response::Item for Response { // when `mount_point` and `device` fields of `Filesystem` message // will have `bytes` type instead of `string`. let mut proto = rrg_proto::sysinfo::Filesystem::new(); - proto.set_device(self.mount_info.source); - proto.set_mount_point(self.mount_info.target.to_string_lossy().into_owned()); + proto.set_device(self.mount_info.name); + proto.set_mount_point(self.mount_info.path.to_string_lossy().into_owned()); proto.set_field_type(self.mount_info.fs_type); proto @@ -103,13 +103,6 @@ mod tests { use super::*; - #[test] - fn test_if_any_filesystem_exists() { - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, ()).is_ok()); - assert_ne!(session.reply_count(), 0); - } - #[cfg(feature = "test-fuse")] #[test] fn test_fuse_filesystem() { diff --git a/crates/rrg/src/action/list_mounts.rs b/crates/rrg/src/action/list_mounts.rs new file mode 100644 index 00000000..a690be53 --- /dev/null +++ b/crates/rrg/src/action/list_mounts.rs @@ -0,0 +1,61 @@ +// Copyright 2023 Google LLC +// +// Use of this source code is governed by an MIT-style license that can be found +// in the LICENSE file or at https://opensource.org/licenses/MIT. + +/// A result of the `list_mounts` action. +struct Item { + // Information about the individual filesystem mount. + mount: ospect::fs::Mount, +} + +// Handles invocations of the `list_mounts` action. +pub fn handle(session: &mut S, _: ()) -> crate::session::Result<()> +where + S: crate::session::Session, +{ + let mounts = ospect::fs::mounts() + .map_err(crate::session::Error::action)?; + + for mount in mounts { + let mount = match mount { + Ok(mount) => mount, + Err(error) => { + log::warn!("failed to obtain mount information: {}", error); + continue; + } + }; + + session.reply(Item { + mount, + })?; + } + + Ok(()) +} + +impl crate::response::Item for Item { + + type Proto = rrg_proto::v2::list_mounts::Result; + + fn into_proto(self) -> rrg_proto::v2::list_mounts::Result { + let mut proto = rrg_proto::v2::list_mounts::Result::default(); + proto.set_mount(self.mount.into()); + + proto + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn handle_some_mount() { + let mut session = crate::session::FakeSession::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert!(session.reply_count() > 0); + } +} diff --git a/crates/rrg/src/request.rs b/crates/rrg/src/request.rs index fe01966f..0155e888 100644 --- a/crates/rrg/src/request.rs +++ b/crates/rrg/src/request.rs @@ -33,6 +33,8 @@ pub enum Action { ListNamedPipes, /// List network interfaces available on the system. ListInterfaces, + /// List filesystem mounts available on the system. + ListMounts, /// List users available on the system. ListUsers, /// Get the snapshot of the entire filesystem. @@ -52,6 +54,7 @@ impl std::fmt::Display for Action { Action::ListConnections => write!(fmt, "list_connections"), Action::ListNamedPipes => write!(fmt, "list_named_pipes"), Action::ListInterfaces => write!(fmt, "list_interfaces"), + Action::ListMounts => write!(fmt, "list_mounts"), Action::ListUsers => write!(fmt, "list_users"), Action::GetFilesystemTimeline => write!(fmt, "get_filesystem_timeline"), } @@ -95,6 +98,7 @@ impl TryFrom for Action { LIST_CONNECTIONS => Ok(Action::ListConnections), LIST_NAMED_PIPES => Ok(Action::ListNamedPipes), LIST_INTERFACES => Ok(Action::ListInterfaces), + LIST_MOUNTS => Ok(Action::ListMounts), LIST_USERS => Ok(Action::ListUsers), GET_FILESYSTEM_TIMELINE => Ok(Action::GetFilesystemTimeline), _ => { diff --git a/proto/rrg.proto b/proto/rrg.proto index fc15c665..29f83d49 100644 --- a/proto/rrg.proto +++ b/proto/rrg.proto @@ -36,6 +36,8 @@ enum Action { GET_FILESYSTEM_TIMELINE = 10; // List network interfaces available on the system. LIST_INTERFACES = 11; + // List filesystem mounts available on the system. + LIST_MOUNTS = 12; // TODO: Define more actions that should be supported. diff --git a/proto/rrg/action/list_mounts.proto b/proto/rrg/action/list_mounts.proto new file mode 100644 index 00000000..455c95b6 --- /dev/null +++ b/proto/rrg/action/list_mounts.proto @@ -0,0 +1,14 @@ +// Copyright 2023 Google LLC +// +// Use of this source code is governed by an MIT-style license that can be found +// in the LICENSE file or at https://opensource.org/licenses/MIT. +syntax = "proto3"; + +package rrg.action.list_mounts; + +import "rrg/fs.proto"; + +message Result { + // Information about the individual filesystem mount. + rrg.fs.Mount mount = 1; +} diff --git a/proto/rrg/fs.proto b/proto/rrg/fs.proto index 67386734..ed85e001 100644 --- a/proto/rrg/fs.proto +++ b/proto/rrg/fs.proto @@ -70,3 +70,13 @@ message FileExtAttr { // This can be an arbitrary sequence of bytes both on macOS and Linux. bytes value = 2; } + +// Information about a mounted filesystem. +message Mount { + // Name or other identifier of the mounted device. + string name = 1; + // Path at which the mounted filesystem is available (a mount point). + Path path = 2; + // Type of the mounted filesystem (e.g. `ext4`, `ramfs`, `NTFS`). + string fs_type = 3; +}