diff --git a/.gitmodules b/.gitmodules index 28b3bce9..8ad0f10a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "proto/grr"] - path = vendor/grr - url = https://github.com/google/grr [submodule "proto/vendor/protobuf"] path = vendor/protobuf url = https://github.com/protocolbuffers/protobuf diff --git a/crates/rrg-proto/build.rs b/crates/rrg-proto/build.rs index a7657995..628ca9e1 100644 --- a/crates/rrg-proto/build.rs +++ b/crates/rrg-proto/build.rs @@ -6,20 +6,6 @@ use std::path::PathBuf; const PROTOS: &'static [&'static str] = &[ - "../../vendor/grr/grr/proto/grr_response_proto/semantic.proto", - "../../vendor/grr/grr/proto/grr_response_proto/sysinfo.proto", - "../../vendor/grr/grr/proto/grr_response_proto/knowledge_base.proto", - "../../vendor/grr/grr/proto/grr_response_proto/jobs.proto", - "../../vendor/grr/grr/proto/grr_response_proto/timeline.proto", - "../../vendor/grr/grr/proto/grr_response_proto/anomaly.proto", - "../../vendor/grr/grr/proto/grr_response_proto/export.proto", - "../../vendor/grr/grr/proto/grr_response_proto/objects.proto", - "../../vendor/grr/grr/proto/grr_response_proto/output_plugin.proto", - "../../vendor/grr/grr/proto/grr_response_proto/flows.proto", - "../../vendor/grr/grr/proto/grr_response_proto/user.proto", -]; - -const PROTOS_V2: &'static [&'static str] = &[ "../../proto/rrg.proto", "../../proto/rrg/blob.proto", "../../proto/rrg/fs.proto", @@ -40,33 +26,18 @@ fn main() { .expect("no output directory") .into(); - - let proto_out_dir = outdir.join("proto"); - std::fs::create_dir_all(&proto_out_dir).unwrap(); - - protobuf_codegen_pure::Codegen::new() - .out_dir(&proto_out_dir) - .include("../../vendor/grr/grr/proto") - .include("../../vendor/protobuf/src") - .inputs(PROTOS) - .customize(protobuf_codegen_pure::Customize { - gen_mod_rs: Some(true), - ..Default::default() - }) - .run().unwrap(); - - for proto in PROTOS_V2 { + for proto in PROTOS { println!("cargo:rerun-if-changed={}", proto); } - let proto_out_dir = outdir.join("proto-v2"); + let proto_out_dir = outdir.join("proto"); std::fs::create_dir_all(&proto_out_dir).unwrap(); protobuf_codegen_pure::Codegen::new() .out_dir(&proto_out_dir) .include("../../vendor/protobuf/src") .include("../../proto") - .inputs(PROTOS_V2) + .inputs(PROTOS) .customize(protobuf_codegen_pure::Customize { gen_mod_rs: Some(true), ..Default::default() diff --git a/crates/rrg-proto/src/lib.rs b/crates/rrg-proto/src/lib.rs index a927b8f1..d74e0533 100644 --- a/crates/rrg-proto/src/lib.rs +++ b/crates/rrg-proto/src/lib.rs @@ -6,652 +6,351 @@ pub mod convert; pub mod path; -pub mod v2 { - include!(concat!(env!("OUT_DIR"), "/proto-v2/mod.rs")); - - impl From for os::Type { - - fn from(kind: ospect::os::Kind) -> os::Type { - match kind { - ospect::os::Kind::Linux => os::Type::LINUX, - ospect::os::Kind::Macos => os::Type::MACOS, - ospect::os::Kind::Windows => os::Type::WINDOWS, - } - } - } - - impl From for fs::Path { - - fn from(path: std::path::PathBuf) -> fs::Path { - let mut proto = fs::Path::default(); - proto.set_raw_bytes(crate::path::into_bytes(path)); - - proto - } - } - - - impl TryFrom for std::path::PathBuf { - - type Error = ParsePathError; - - fn try_from(mut proto: fs::Path) -> Result { - crate::path::from_bytes(proto.take_raw_bytes()) - .map_err(ParsePathError) - } - } - - impl From for fs::FileMetadata_Type { - - fn from(file_type: std::fs::FileType) -> fs::FileMetadata_Type { - match () { - _ if file_type.is_file() => fs::FileMetadata_Type::FILE, - _ if file_type.is_dir() => fs::FileMetadata_Type::DIR, - _ if file_type.is_symlink() => fs::FileMetadata_Type::SYMLINK, - _ => fs::FileMetadata_Type::UNKNOWN, - } - } - } - - impl From for fs::FileMetadata { - - fn from(metadata: std::fs::Metadata) -> fs::FileMetadata { - use crate::into_timestamp; - - let mut proto = fs::FileMetadata::default(); - proto.set_field_type(metadata.file_type().into()); - proto.set_size(metadata.len()); - - match metadata.accessed() { - Ok(time) => proto.set_access_time(into_timestamp(time)), - Err(_) => (), // TODO(@panhania): Consider logging. - } - match metadata.modified() { - Ok(time) => proto.set_modification_time(into_timestamp(time)), - Err(_) => (), // TODO(@panhania): Consider logging. - } - match metadata.created() { - Ok(time) => proto.set_creation_time(into_timestamp(time)), - Err(_) => (), // TODO(@panhania): Consider logging. - } - - proto - } - } - - impl From for fs::FileExtAttr { - - fn from(ext_attr: ospect::fs::ExtAttr) -> fs::FileExtAttr { - let mut proto = fs::FileExtAttr::default(); - proto.set_value(ext_attr.value); - - #[cfg(target_family = "unix")] - { - use std::os::unix::ffi::OsStringExt as _; - proto.set_name(ext_attr.name.into_vec()); - } - - // Extended attributes are not supported on Windows, so technically - // we don't need to have this code. But in case somebody creates an - // aritficial extended attribute code it is better to be at least - // somewhat covered. - #[cfg(target_family = "windows")] - { - let name_str = ext_attr.name.to_string_lossy(); - proto.set_name(name_str.as_bytes().into()); - } - - proto - } - } - - impl From for fs::Mount { +include!(concat!(env!("OUT_DIR"), "/proto/mod.rs")); - 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); +impl From for os::Type { - proto + fn from(kind: ospect::os::Kind) -> os::Type { + match kind { + ospect::os::Kind::Linux => os::Type::LINUX, + ospect::os::Kind::Macos => os::Type::MACOS, + ospect::os::Kind::Windows => os::Type::WINDOWS, } } +} - impl From for net::IpAddress { +impl From for fs::Path { - fn from(addr: std::net::Ipv4Addr) -> net::IpAddress { - let mut proto = net::IpAddress::default(); - proto.set_octets(Vec::from(addr.octets())); + fn from(path: std::path::PathBuf) -> fs::Path { + let mut proto = fs::Path::default(); + proto.set_raw_bytes(crate::path::into_bytes(path)); - proto - } + proto } +} - impl From for net::IpAddress { - fn from(addr: std::net::Ipv6Addr) -> net::IpAddress { - let mut proto = net::IpAddress::default(); - proto.set_octets(Vec::from(addr.octets())); +impl TryFrom for std::path::PathBuf { - proto - } - } + type Error = ParsePathError; - impl From for net::IpAddress { - - fn from(addr: std::net::IpAddr) -> net::IpAddress { - match addr { - std::net::IpAddr::V4(addr) => addr.into(), - std::net::IpAddr::V6(addr) => addr.into(), - } - } + fn try_from(mut proto: fs::Path) -> Result { + crate::path::from_bytes(proto.take_raw_bytes()) + .map_err(ParsePathError) } +} - impl From for net::SocketAddress { - - fn from(addr: std::net::SocketAddrV4) -> net::SocketAddress { - let mut proto = net::SocketAddress::default(); - proto.set_ip_address(net::IpAddress::from(*addr.ip())); - proto.set_port(u32::from(addr.port())); +impl From for fs::FileMetadata_Type { - proto + fn from(file_type: std::fs::FileType) -> fs::FileMetadata_Type { + match () { + _ if file_type.is_file() => fs::FileMetadata_Type::FILE, + _ if file_type.is_dir() => fs::FileMetadata_Type::DIR, + _ if file_type.is_symlink() => fs::FileMetadata_Type::SYMLINK, + _ => fs::FileMetadata_Type::UNKNOWN, } } +} - impl From for net::SocketAddress { +impl From for fs::FileMetadata { - fn from(addr: std::net::SocketAddrV6) -> net::SocketAddress { - let mut proto = net::SocketAddress::default(); - proto.set_ip_address(net::IpAddress::from(*addr.ip())); - proto.set_port(u32::from(addr.port())); + fn from(metadata: std::fs::Metadata) -> fs::FileMetadata { + let mut proto = fs::FileMetadata::default(); + proto.set_field_type(metadata.file_type().into()); + proto.set_size(metadata.len()); - proto + match metadata.accessed() { + Ok(time) => proto.set_access_time(into_timestamp(time)), + Err(_) => (), // TODO(@panhania): Consider logging. } - } - - impl From for net::SocketAddress { - - fn from(addr: std::net::SocketAddr) -> net::SocketAddress { - match addr { - std::net::SocketAddr::V4(addr) => addr.into(), - std::net::SocketAddr::V6(addr) => addr.into(), - } + match metadata.modified() { + Ok(time) => proto.set_modification_time(into_timestamp(time)), + Err(_) => (), // TODO(@panhania): Consider logging. } - } - - impl From for net::MacAddress { - - fn from(addr: ospect::net::MacAddr) -> net::MacAddress { - let mut proto = net::MacAddress::default(); - proto.set_octets(Vec::from(addr.octets())); - - proto + match metadata.created() { + Ok(time) => proto.set_creation_time(into_timestamp(time)), + Err(_) => (), // TODO(@panhania): Consider logging. } - } - impl From for net::TcpState { - - fn from(state: ospect::net::TcpState) -> net::TcpState { - use ospect::net::TcpState::*; - match state { - Listen => net::TcpState::LISTEN, - SynSent => net::TcpState::SYN_SENT, - SynReceived => net::TcpState::SYN_RECEIVED, - Established => net::TcpState::ESTABLISHED, - FinWait1 => net::TcpState::FIN_WAIT_1, - FinWait2 => net::TcpState::FIN_WAIT_2, - CloseWait => net::TcpState::CLOSE_WAIT, - Closing => net::TcpState::CLOSING, - LastAck => net::TcpState::LAST_ACK, - TimeWait => net::TcpState::TIME_WAIT, - Closed => net::TcpState::CLOSED, - } - } + proto } +} - impl From for net::TcpConnection { +impl From for fs::FileExtAttr { - fn from(conn: ospect::net::TcpConnectionV4) -> net::TcpConnection { - let mut proto = net::TcpConnection::default(); - proto.set_pid(conn.pid()); - proto.set_local_address(conn.local_addr().into()); - proto.set_remote_address(conn.remote_addr().into()); - proto.set_state(conn.state().into()); + fn from(ext_attr: ospect::fs::ExtAttr) -> fs::FileExtAttr { + let mut proto = fs::FileExtAttr::default(); + proto.set_value(ext_attr.value); - proto + #[cfg(target_family = "unix")] + { + use std::os::unix::ffi::OsStringExt as _; + proto.set_name(ext_attr.name.into_vec()); } - } - - impl From for net::TcpConnection { - - fn from(conn: ospect::net::TcpConnectionV6) -> net::TcpConnection { - let mut proto = net::TcpConnection::default(); - proto.set_pid(conn.pid()); - proto.set_local_address(conn.local_addr().into()); - proto.set_remote_address(conn.remote_addr().into()); - proto.set_state(conn.state().into()); - proto + // Extended attributes are not supported on Windows, so technically + // we don't need to have this code. But in case somebody creates an + // aritficial extended attribute code it is better to be at least + // somewhat covered. + #[cfg(target_family = "windows")] + { + let name_str = ext_attr.name.to_string_lossy(); + proto.set_name(name_str.as_bytes().into()); } - } - - impl From for net::TcpConnection { - fn from(conn: ospect::net::TcpConnection) -> net::TcpConnection { - match conn { - ospect::net::TcpConnection::V4(conn) => conn.into(), - ospect::net::TcpConnection::V6(conn) => conn.into(), - } - } + proto } +} - impl From for net::UdpConnection { +impl From for fs::Mount { - fn from(conn: ospect::net::UdpConnectionV4) -> net::UdpConnection { - let mut proto = net::UdpConnection::default(); - proto.set_pid(conn.pid()); - proto.set_local_address(conn.local_addr().into()); + 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 - } + proto } +} - impl From for net::UdpConnection { - - fn from(conn: ospect::net::UdpConnectionV6) -> net::UdpConnection { - let mut proto = net::UdpConnection::default(); - proto.set_pid(conn.pid()); - proto.set_local_address(conn.local_addr().into()); - - proto - } - } +impl From for net::IpAddress { - impl From for net::UdpConnection { + fn from(addr: std::net::Ipv4Addr) -> net::IpAddress { + let mut proto = net::IpAddress::default(); + proto.set_octets(Vec::from(addr.octets())); - fn from(conn: ospect::net::UdpConnection) -> net::UdpConnection { - match conn { - ospect::net::UdpConnection::V4(conn) => conn.into(), - ospect::net::UdpConnection::V6(conn) => conn.into(), - } - } + proto } +} - impl From for net::Connection { +impl From for net::IpAddress { - fn from(conn: ospect::net::Connection) -> net::Connection { - let mut proto = net::Connection::default(); - match conn { - ospect::net::Connection::Tcp(conn) => { - proto.set_tcp(conn.into()); - } - ospect::net::Connection::Udp(conn) => { - proto.set_udp(conn.into()); - } - } + fn from(addr: std::net::Ipv6Addr) -> net::IpAddress { + let mut proto = net::IpAddress::default(); + proto.set_octets(Vec::from(addr.octets())); - proto - } + proto } +} - impl From for net::Interface { - - fn from(iface: ospect::net::Interface) -> net::Interface { - let mut proto = net::Interface::default(); - proto.set_name(iface.name().to_string_lossy().into_owned()); - - if let Some(mac_addr) = iface.mac_addr() { - proto.set_mac_address((*mac_addr).into()); - } - - let ip_addrs = iface.ip_addrs() - .map(|ip_addr| net::IpAddress::from(*ip_addr)) - .collect::>(); - proto.set_ip_addresses(ip_addrs.into()); +impl From for net::IpAddress { - proto + fn from(addr: std::net::IpAddr) -> net::IpAddress { + match addr { + std::net::IpAddr::V4(addr) => addr.into(), + std::net::IpAddr::V6(addr) => addr.into(), } } +} - impl From for log::LevelFilter { - - fn from(level: rrg::Log_Level) -> log::LevelFilter { - match level { - rrg::Log_Level::UNSET => log::LevelFilter::Off, - rrg::Log_Level::ERROR => log::LevelFilter::Error, - rrg::Log_Level::WARN => log::LevelFilter::Warn, - rrg::Log_Level::INFO => log::LevelFilter::Info, - rrg::Log_Level::DEBUG => log::LevelFilter::Debug, - } - } - } +impl From for net::SocketAddress { - impl From for rrg::Log_Level { + fn from(addr: std::net::SocketAddrV4) -> net::SocketAddress { + let mut proto = net::SocketAddress::default(); + proto.set_ip_address(net::IpAddress::from(*addr.ip())); + proto.set_port(u32::from(addr.port())); - fn from(level: log::Level) -> rrg::Log_Level { - match level { - log::Level::Error => rrg::Log_Level::ERROR, - log::Level::Warn => rrg::Log_Level::WARN, - log::Level::Info => rrg::Log_Level::INFO, - log::Level::Debug => rrg::Log_Level::DEBUG, - log::Level::Trace => rrg::Log_Level::DEBUG, - } - } + proto } +} - /// A type representing errors that can occur when parsing paths. - #[derive(Debug, PartialEq, Eq)] - pub struct ParsePathError(crate::path::ParseError); +impl From for net::SocketAddress { - impl std::fmt::Display for ParsePathError { + fn from(addr: std::net::SocketAddrV6) -> net::SocketAddress { + let mut proto = net::SocketAddress::default(); + proto.set_ip_address(net::IpAddress::from(*addr.ip())); + proto.set_port(u32::from(addr.port())); - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - self.0.fmt(fmt) - } + proto } +} - impl std::error::Error for ParsePathError { +impl From for net::SocketAddress { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.0.source() + fn from(addr: std::net::SocketAddr) -> net::SocketAddress { + match addr { + std::net::SocketAddr::V4(addr) => addr.into(), + std::net::SocketAddr::V6(addr) => addr.into(), } } } -include!(concat!(env!("OUT_DIR"), "/proto/mod.rs")); - -impl From for jobs::DataBlob { +impl From for net::MacAddress { - fn from(value: bool) -> jobs::DataBlob { - let mut result = jobs::DataBlob::new(); - result.set_boolean(value); + fn from(addr: ospect::net::MacAddr) -> net::MacAddress { + let mut proto = net::MacAddress::default(); + proto.set_octets(Vec::from(addr.octets())); - result + proto } } -impl From for jobs::DataBlob { - - fn from(value: i64) -> jobs::DataBlob { - let mut result = jobs::DataBlob::new(); - result.set_integer(value); +impl From for net::TcpState { - result + fn from(state: ospect::net::TcpState) -> net::TcpState { + use ospect::net::TcpState::*; + match state { + Listen => net::TcpState::LISTEN, + SynSent => net::TcpState::SYN_SENT, + SynReceived => net::TcpState::SYN_RECEIVED, + Established => net::TcpState::ESTABLISHED, + FinWait1 => net::TcpState::FIN_WAIT_1, + FinWait2 => net::TcpState::FIN_WAIT_2, + CloseWait => net::TcpState::CLOSE_WAIT, + Closing => net::TcpState::CLOSING, + LastAck => net::TcpState::LAST_ACK, + TimeWait => net::TcpState::TIME_WAIT, + Closed => net::TcpState::CLOSED, + } } } -impl From for jobs::DataBlob { +impl From for net::TcpConnection { - fn from(value: f32) -> jobs::DataBlob { - let mut result = jobs::DataBlob::new(); - result.set_float(value); + fn from(conn: ospect::net::TcpConnectionV4) -> net::TcpConnection { + let mut proto = net::TcpConnection::default(); + proto.set_pid(conn.pid()); + proto.set_local_address(conn.local_addr().into()); + proto.set_remote_address(conn.remote_addr().into()); + proto.set_state(conn.state().into()); - result + proto } } -impl From> for jobs::DataBlob { +impl From for net::TcpConnection { - fn from(value: Vec) -> jobs::DataBlob { - let mut result = jobs::DataBlob::new(); - result.set_data(value); + fn from(conn: ospect::net::TcpConnectionV6) -> net::TcpConnection { + let mut proto = net::TcpConnection::default(); + proto.set_pid(conn.pid()); + proto.set_local_address(conn.local_addr().into()); + proto.set_remote_address(conn.remote_addr().into()); + proto.set_state(conn.state().into()); - result + proto } } -impl From for jobs::DataBlob { +impl From for net::TcpConnection { - fn from(value: String) -> jobs::DataBlob { - let mut result = jobs::DataBlob::new(); - result.set_string(value); - - result + fn from(conn: ospect::net::TcpConnection) -> net::TcpConnection { + match conn { + ospect::net::TcpConnection::V4(conn) => conn.into(), + ospect::net::TcpConnection::V6(conn) => conn.into(), + } } } -impl jobs::KeyValue { +impl From for net::UdpConnection { - /// Creates a key-value pair. - /// - /// Both the key and the value are going to be equal to the given values. - /// - /// # Examples - /// - /// ``` - /// use rrg_proto::jobs::KeyValue; - /// - /// let entry = KeyValue::pair(String::from("foo"), 42i64); - /// assert_eq!(entry.get_k().get_string(), String::from("foo")); - /// assert_eq!(entry.get_v().get_integer(), 42); - /// ``` - pub fn pair(key: K, value: V) -> jobs::KeyValue - where - K: Into, - V: Into, - { - let mut result = jobs::KeyValue::new(); - result.set_k(key.into()); - result.set_v(value.into()); + fn from(conn: ospect::net::UdpConnectionV4) -> net::UdpConnection { + let mut proto = net::UdpConnection::default(); + proto.set_pid(conn.pid()); + proto.set_local_address(conn.local_addr().into()); - result - } - - /// Creates a key-only key-value. - /// - /// The key is going to be equal to the given value and the key will be - /// `None`. - /// - /// # Examples - /// - /// ``` - /// use rrg_proto::jobs::KeyValue; - /// - /// let entry = KeyValue::key(String::from("quux")); - /// assert_eq!(entry.get_k().get_string(), String::from("quux")); - /// assert!(!entry.has_v()); - /// ``` - pub fn key(key: K) -> jobs::KeyValue - where - K: Into, - { - let mut result = jobs::KeyValue::new(); - result.set_k(key.into()); - - result + proto } } -impl std::iter::FromIterator for jobs::AttributedDict { +impl From for net::UdpConnection { - fn from_iter(iter: I) -> jobs::AttributedDict - where - I: IntoIterator, - { - let mut result = jobs::AttributedDict::new(); - result.set_dat(iter.into_iter().collect()); + fn from(conn: ospect::net::UdpConnectionV6) -> net::UdpConnection { + let mut proto = net::UdpConnection::default(); + proto.set_pid(conn.pid()); + proto.set_local_address(conn.local_addr().into()); - result - } -} - -impl std::iter::FromIterator<(K, V)> for jobs::AttributedDict -where - K: Into, - V: Into, -{ - fn from_iter(iter: I) -> jobs::AttributedDict - where - I: IntoIterator, - { - let pair = |(key, value)| jobs::KeyValue::pair(key, value); - iter.into_iter().map(pair).collect() + proto } } -impl crate::convert::FromLossy for jobs::StatEntry { - - fn from_lossy(metadata: std::fs::Metadata) -> jobs::StatEntry { - use rrg_macro::ack; - - let mut result = jobs::StatEntry::new(); - result.set_st_size(metadata.len()); +impl From for net::UdpConnection { - let atime_secs = ack! { - metadata.accessed(), - error: "failed to obtain file access time" - }.and_then(|atime| ack! { - secs(atime), - error: "failed to convert access time to seconds" - }); - if let Some(atime_secs) = atime_secs { - result.set_st_atime(atime_secs); - } - - let mtime_secs = ack! { - metadata.modified(), - error: "failed to obtain file modification time" - }.and_then(|mtime| ack! { - secs(mtime), - error: "failed to convert modification time to seconds" - }); - if let Some(mtime_secs) = mtime_secs { - result.set_st_mtime(mtime_secs); + fn from(conn: ospect::net::UdpConnection) -> net::UdpConnection { + match conn { + ospect::net::UdpConnection::V4(conn) => conn.into(), + ospect::net::UdpConnection::V6(conn) => conn.into(), } + } +} - let btime_secs = ack! { - metadata.created(), - error: "failed to obtain file creation time" - }.and_then(|btime| ack! { - secs(btime), - error: "failed to convert creation time to seconds" - }); - if let Some(btime_secs) = btime_secs { - result.set_st_btime(btime_secs); - } +impl From for net::Connection { - #[cfg(target_family = "unix")] - { - use std::os::unix::fs::MetadataExt as _; - - let ctime_secs = ack! { - u64::try_from(metadata.ctime()), - error: "negative inode change time" - }; - if let Some(ctime_secs) = ctime_secs { - result.set_st_ctime(ctime_secs); + fn from(conn: ospect::net::Connection) -> net::Connection { + let mut proto = net::Connection::default(); + match conn { + ospect::net::Connection::Tcp(conn) => { + proto.set_tcp(conn.into()); + } + ospect::net::Connection::Udp(conn) => { + proto.set_udp(conn.into()); } - - result.set_st_mode(metadata.mode().into()); - result.set_st_ino(metadata.ino()); - result.set_st_dev(metadata.dev()); - result.set_st_rdev(metadata.rdev()); - result.set_st_nlink(metadata.nlink()); - result.set_st_uid(metadata.uid()); - result.set_st_gid(metadata.gid()); - result.set_st_blocks(metadata.blocks()); - result.set_st_blksize(metadata.blksize()); } - result + proto } } -impl Into for ospect::fs::ExtAttr { +impl From for net::Interface { - fn into(self) -> jobs::StatEntry_ExtAttr { - let mut proto = jobs::StatEntry_ExtAttr::new(); + fn from(iface: ospect::net::Interface) -> net::Interface { + let mut proto = net::Interface::default(); + proto.set_name(iface.name().to_string_lossy().into_owned()); - #[cfg(target_family = "unix")] - { - use std::os::unix::ffi::OsStringExt as _; - proto.set_name(self.name.into_vec()); + if let Some(mac_addr) = iface.mac_addr() { + proto.set_mac_address((*mac_addr).into()); } - #[cfg(target_os = "windows")] - { - let name = self.name.to_string_lossy().into_owned().into_bytes(); - proto.set_name(name); - } - - proto.set_value(self.value); + let ip_addrs = iface.ip_addrs() + .map(|ip_addr| net::IpAddress::from(*ip_addr)) + .collect::>(); + proto.set_ip_addresses(ip_addrs.into()); proto } } -impl TryFrom for std::path::PathBuf { - - type Error = ParsePathSpecError; +impl From for log::LevelFilter { - fn try_from(mut spec: jobs::PathSpec) -> Result { - if spec.get_pathtype() != jobs::PathSpec_PathType::OS { - return Err(ParsePathSpecError { - kind: ParsePathSpecErrorKind::InvalidType, - }); + fn from(level: rrg::Log_Level) -> log::LevelFilter { + match level { + rrg::Log_Level::UNSET => log::LevelFilter::Off, + rrg::Log_Level::ERROR => log::LevelFilter::Error, + rrg::Log_Level::WARN => log::LevelFilter::Warn, + rrg::Log_Level::INFO => log::LevelFilter::Info, + rrg::Log_Level::DEBUG => log::LevelFilter::Debug, } - - let path = spec.take_path(); - if path.is_empty() { - return Err(ParsePathSpecError { - kind: ParsePathSpecErrorKind::Empty, - }); - } - - Ok(std::path::PathBuf::from(path)) } } -impl From for jobs::PathSpec { +impl From for rrg::Log_Level { - fn from(path: std::path::PathBuf) -> jobs::PathSpec { - let mut result = jobs::PathSpec::new(); - result.set_path(path.to_string_lossy().into_owned()); - result.set_pathtype(jobs::PathSpec_PathType::OS); - - result + fn from(level: log::Level) -> rrg::Log_Level { + match level { + log::Level::Error => rrg::Log_Level::ERROR, + log::Level::Warn => rrg::Log_Level::WARN, + log::Level::Info => rrg::Log_Level::INFO, + log::Level::Debug => rrg::Log_Level::DEBUG, + log::Level::Trace => rrg::Log_Level::DEBUG, + } } } -/// An enum listing possible issues when parsing path specification. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -pub enum ParsePathSpecErrorKind { - /// Attempted to parse an empty path. - Empty, - /// Attempted to parse a path of invalid type. - InvalidType, -} - -/// An error type for situations where parsing path specification failed. -#[derive(Clone, Debug)] -pub struct ParsePathSpecError { - kind: ParsePathSpecErrorKind, -} - -impl ParsePathSpecError { - - /// Describes the exact cause of the parsing failure. - pub fn kind(&self) -> ParsePathSpecErrorKind { - self.kind - } -} +/// A type representing errors that can occur when parsing paths. +#[derive(Debug, PartialEq, Eq)] +pub struct ParsePathError(crate::path::ParseError); -impl std::fmt::Display for ParsePathSpecError { +impl std::fmt::Display for ParsePathError { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - use ParsePathSpecErrorKind::*; - - match self.kind { - Empty => write!(fmt, "empty path"), - InvalidType => write!(fmt, "invalid path type"), - } + self.0.fmt(fmt) } } -impl std::error::Error for ParsePathSpecError { +impl std::error::Error for ParsePathError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None + self.0.source() } } diff --git a/crates/rrg/Cargo.toml b/crates/rrg/Cargo.toml index 7d3ef0dc..ae8a07a3 100644 --- a/crates/rrg/Cargo.toml +++ b/crates/rrg/Cargo.toml @@ -14,16 +14,6 @@ default = [ "action-list_connections", "action-list_interfaces", "action-list_mounts", - - # These actions are deprecated (awaiting migration to the new protocol). - "action-insttime", - "action-interfaces", - "action-filesystems", - "action-finder", - "action-listdir", - "action-metadata", - "action-network", - "action-stat", ] action-get_system_metadata = [] @@ -34,16 +24,6 @@ action-list_connections = [] action-list_interfaces = [] action-list_mounts = [] -# These actions are deprecated (awaiting migration to the new protocol). -action-insttime = [] -action-interfaces = [] -action-filesystems = [] -action-finder = ["dep:digest", "dep:md-5", "dep:sha1", "dep:sha2"] -action-listdir = [] -action-metadata = [] -action-network = [] -action-stat = [] - test-setfattr = [] test-chattr = [] test-fuse = ["dep:fuse"] diff --git a/crates/rrg/src/action.rs b/crates/rrg/src/action.rs index 4e514eeb..3f53c761 100644 --- a/crates/rrg/src/action.rs +++ b/crates/rrg/src/action.rs @@ -15,9 +15,6 @@ //! instance of the corresponding request type and send some (zero or more) //! instances of the corresponding response type. -#[allow(dead_code)] -mod deprecated; // TODO(@panhania): Delete this module. - #[cfg(feature = "action-get_system_metadata")] pub mod get_system_metadata; diff --git a/crates/rrg/src/action/deprecated.rs b/crates/rrg/src/action/deprecated.rs deleted file mode 100644 index 2b6d0bc6..00000000 --- a/crates/rrg/src/action/deprecated.rs +++ /dev/null @@ -1,32 +0,0 @@ -// 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. - -//! Index of deprecated RRG actions that follow the old protocol. -//! -//! Code from this module is supposed to be migrated to use the new protocol and -//! be completely removed at some point. - -#[cfg(feature = "action-filesystems")] -#[cfg(target_os = "linux")] -pub mod filesystems; - -#[cfg(feature = "action-finder")] -pub mod finder; - -#[cfg(feature = "action-insttime")] -mod insttime; - -#[cfg(feature = "action-interfaces")] -#[cfg(target_family = "unix")] -pub mod interfaces; - -#[cfg(feature = "action-listdir")] -pub mod listdir; - -#[cfg(feature = "action-network")] -pub mod network; - -#[cfg(feature = "action-stat")] -pub mod stat; diff --git a/crates/rrg/src/action/deprecated/filesystems.rs b/crates/rrg/src/action/deprecated/filesystems.rs deleted file mode 100644 index a29b6a89..00000000 --- a/crates/rrg/src/action/deprecated/filesystems.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2020 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 handler and associated types for the filesystems action. -//! -//! The filesystems action lists all mounted filesystems on the client, -//! collecting device name, mount point, filesystem type and its options. -//! Current implementation works only in Linux systems. - -use crate::session::{self, Session}; - -use std::fmt::{Display, Formatter}; - -/// Enum of possible errors, which can occur during collecting filesystems data. -#[derive(Debug)] -enum Error { - /// Missing mtab-like file error. - MissingFile(std::io::Error), - /// Parsing mtab-like file error. - MountInfoParse(std::io::Error), -} - -impl std::error::Error for Error { - - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use Error::*; - - match *self { - MissingFile(ref error) => Some(error), - MountInfoParse(ref error) => Some(error), - } - } -} - -impl Display for Error { - - fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result { - use Error::*; - - match *self { - MissingFile(ref error) => { - write!(fmt, "missed file error: {}", error) - }, - MountInfoParse(ref error) => { - write!(fmt, "failed to obtain mount information: {}", error) - }, - } - } -} - -impl From for session::Error { - - fn from(error: Error) -> session::Error { - session::Error::action(error) - } -} - -/// A response type for the filesystems action. -pub struct Response { - /// Information about the filesystem. - mount_info: ospect::fs::Mount, -} - -/// Handles requests for the filesystems action. -/// -/// Initially searches in `/proc/mounts`. If it's missing, falls back to -/// `/etc/mtab`. -pub fn handle(session: &mut S, _: ()) -> session::Result<()> { - let mounts = ospect::fs::mounts() - .map_err(Error::MissingFile)?; - - for mount_info in mounts { - let mount_info = mount_info.map_err(Error::MountInfoParse)?; - session.reply(Response { - mount_info, - })?; - } - - Ok(()) -} - -impl crate::response::Item for Response { - - type Proto = rrg_proto::sysinfo::Filesystem; - - fn into_proto(self) -> rrg_proto::sysinfo::Filesystem { - // TODO: Remove lossy conversion of `PathBuf` to `String` - // 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.name); - proto.set_mount_point(self.mount_info.path.to_string_lossy().into_owned()); - proto.set_field_type(self.mount_info.fs_type); - - proto - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[cfg(feature = "test-fuse")] - #[test] - fn test_fuse_filesystem() { - /// Unit-like struct, representing a filesystem for testing with `fuse`. - struct FuseFilesystem; - - impl fuse::Filesystem for FuseFilesystem {} - - let fs_name = "fuse-test-fs"; - - let tmp_dir = tempfile::tempdir().unwrap(); - let mountpoint = tmp_dir.path(); - - let fs_name_option = format!("fsname={}", fs_name); - let options = [ - "-o", "ro", - "-o", "nosuid", - "-o", "nodev", - "-o", "relatime", - "-o", "subtype=custom-type", - "-o", &fs_name_option, - ]; - let options = options.iter().map(|opt| opt.as_ref()) - .collect::>(); - - // Spawn a background thread to handle filesystem operations. - // When `_background_session` is dropped, filesystem will be unmounted. - let background_session = unsafe { - fuse::spawn_mount(FuseFilesystem, &mountpoint, &options).unwrap() - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, ()).is_ok()); - - let fuse_mounted_fs = session.replies::() - .find(|reply| reply.mount_info.source == fs_name) - .expect("no reply with a mounted FUSE filesystem"); - - let mount_info = &fuse_mounted_fs.mount_info; - assert_eq!(mount_info.source, fs_name); - assert_eq!(mount_info.target, tmp_dir.path()); - assert_eq!(mount_info.fs_type, "fuse.custom-type"); - - drop(background_session); - } -} diff --git a/crates/rrg/src/action/deprecated/finder.rs b/crates/rrg/src/action/deprecated/finder.rs deleted file mode 100644 index 6fc31749..00000000 --- a/crates/rrg/src/action/deprecated/finder.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2020 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. - -//! Handler for `client side file finder` action. - -pub mod download; -pub mod glob; -pub mod chunks; -pub mod condition; -pub mod groups; -pub mod request; -pub mod hash; diff --git a/crates/rrg/src/action/deprecated/finder/chunks.rs b/crates/rrg/src/action/deprecated/finder/chunks.rs deleted file mode 100644 index 00e61dc5..00000000 --- a/crates/rrg/src/action/deprecated/finder/chunks.rs +++ /dev/null @@ -1,170 +0,0 @@ -use std::fs::File; -use std::io::{BufReader, Read, Seek, SeekFrom, Take}; -use std::path::Path; -use rrg_macro::ack; - -/// Implements `Iterator` trait splitting underlying `bytes` into chunks. -#[derive(Debug)] -pub struct Chunks { - /// Data source for the chunks. - data: std::io::Bytes, - /// Chunks reading configuration. - config: ChunksConfig, - /// Data from the previous chunk to be used as an overlap in the next chunk. - overlap_data: Vec, -} - -pub struct GetFileChunksConfig { - /// Number of bytes skipped from the beginning of the file. - pub start_offset: u64, - /// Maximum number of bytes read from the file. - pub max_read_bytes: u64, - /// Desired number of bytes in chunks. Only the last chunk can be smaller - /// than the `bytes_per_chunk`. - pub bytes_per_chunk: u64, - /// A number of bytes that the next chunk will share with the previous one. - pub overlap_bytes: u64, -} - -/// Opens the file and returns its content in chunks. -pub fn get_file_chunks>( - path: P, - config: &GetFileChunksConfig, -) -> Option>>> { - let file = open_file(path, config.start_offset, config.max_read_bytes)?; - - Some(chunks( - BufReader::new(file), - ChunksConfig { - bytes_per_chunk: config.bytes_per_chunk, - overlap_bytes: config.overlap_bytes, - }, - )) -} - -fn open_file>( - path: P, - offset: u64, - max_size: u64, -) -> Option> { - let mut file = ack! { - File::open(path.as_ref()), - error: "failed to open file: {}", path.as_ref().display() - }?; - - ack!( - file.seek(SeekFrom::Start(offset)), - error: "failed to seek in file: {}", path.as_ref().display() - )?; - - Some(file.take(max_size)) -} - -#[derive(Debug)] -struct ChunksConfig { - /// Desired number of bytes in chunks. Only the last chunk can be smaller - /// than the `bytes_per_chunk`. - pub bytes_per_chunk: u64, - /// A number of bytes that the next chunk will share with the previous one. - pub overlap_bytes: u64, -} - -impl std::iter::Iterator for Chunks { - type Item = std::io::Result>; - - fn next(&mut self) -> Option>> { - let mut ret = std::mem::take(&mut self.overlap_data); - let mut bytes_read = false; - for byte in &mut self.data { - let byte = match byte { - Ok(byte) => byte, - Err(err) => return Some(Err(err)), - }; - bytes_read = true; - ret.push(byte); - - if ret.len() == self.config.bytes_per_chunk as usize { - let overlap_start = - ret.len() - self.config.overlap_bytes as usize; - self.overlap_data = Vec::from(&ret[overlap_start..]); - return Some(Ok(ret)); - } - } - if bytes_read { - return Some(Ok(ret)); - } - - return None; - } -} - -/// Returns an iterator over `reader` returning chunks of bytes. -fn chunks(reader: R, config: ChunksConfig) -> Chunks { - assert!(config.bytes_per_chunk > 0); - assert!(config.bytes_per_chunk > config.overlap_bytes); - Chunks { - data: reader.bytes(), - config, - overlap_data: vec![], - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_simple_chunks() { - let data = vec![1, 2, 3, 4, 5]; - let mut chunks = chunks( - data.as_slice(), - ChunksConfig { - bytes_per_chunk: 2, - overlap_bytes: 0, - }, - ); - assert_eq!(chunks.next().unwrap().unwrap(), vec![1, 2]); - assert_eq!(chunks.next().unwrap().unwrap(), vec![3, 4]); - assert_eq!(chunks.next().unwrap().unwrap(), vec![5]); - assert!(chunks.next().is_none()); - } - - #[test] - fn test_overlapping_chunks() { - let data = vec![1, 2, 3, 4, 5]; - let mut chunks = chunks( - data.as_slice(), - ChunksConfig { - bytes_per_chunk: 3, - overlap_bytes: 1, - }, - ); - assert_eq!(chunks.next().unwrap().unwrap(), vec![1, 2, 3]); - assert_eq!(chunks.next().unwrap().unwrap(), vec![3, 4, 5]); - assert!(chunks.next().is_none()); - } - - #[test] - fn test_get_file_chunks_basic_use_case() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - - std::fs::write(&path, b"abcdef").unwrap(); - - let mut chunks = get_file_chunks( - &path, - &GetFileChunksConfig { - start_offset: 1, - max_read_bytes: 4, - bytes_per_chunk: 2, - overlap_bytes: 1, - }, - ) - .unwrap(); - - assert_eq!(chunks.next().unwrap().unwrap(), b"bc"); - assert_eq!(chunks.next().unwrap().unwrap(), b"cd"); - assert_eq!(chunks.next().unwrap().unwrap(), b"de"); - assert!(chunks.next().is_none()); - } -} diff --git a/crates/rrg/src/action/deprecated/finder/condition.rs b/crates/rrg/src/action/deprecated/finder/condition.rs deleted file mode 100644 index 4d9c26c1..00000000 --- a/crates/rrg/src/action/deprecated/finder/condition.rs +++ /dev/null @@ -1,777 +0,0 @@ -use super::chunks::{get_file_chunks, GetFileChunksConfig}; -use super::request::{ - Condition, ContentsMatchCondition, MatchMode, -}; -use crate::fs::Entry; -use log::warn; -use rrg_macro::ack; -use std::cmp::{max, min}; -#[cfg(target_family = "unix")] -use std::fs::Metadata; -#[cfg(target_family = "unix")] -use std::os::unix::fs::MetadataExt; -#[cfg(target_os = "linux")] -use ospect::fs::linux::flags; - -/// Returns true if all conditions were met. -/// If the data required for checking the condition cannot be obtained then -/// the condition is assumed to be met. -pub fn check_conditions(conditions: &Vec, entry: &Entry) -> bool { - conditions.into_iter().all(|c| check_condition(c, &entry)) -} - -/// Returns positions of matches from all conditions when all -/// `match_conditions` found at least 1 match. Returns empty `Vec` otherwise. -/// If the file content cannot be obtained the condition is assumed to -/// be not met. -pub fn find_matches( - match_conditions: &Vec, - entry: &Entry, -) -> Vec { - let mut ret = vec![]; - for match_condition in match_conditions { - let mut matches = matches(match_condition, &entry); - if matches.is_empty() { - return vec![]; - } - ret.append(&mut matches); - } - - ret -} - -/// Checks is the condition is met by the entry. -/// In case of simple conditions if the data required for checking the condition -/// cannot be obtained then the condition is assumed to be met. -fn check_condition(condition: &Condition, entry: &Entry) -> bool { - match condition { - Condition::ModificationTime { min, max } => { - let actual = ack! { - entry.metadata.modified(), - error: "failed to obtain modification time" - }; - match actual { - Some(actual) => is_in_range(&actual, (min, max)), - None => true, - } - } - - Condition::AccessTime { min, max } => { - let actual = ack! { - entry.metadata.accessed(), - error: "failed to obtain access time" - }; - match actual { - Some(actual) => is_in_range(&actual, (min, max)), - None => true, - } - } - - #[cfg(target_family = "unix")] - Condition::InodeChangeTime { min, max } => { - match read_ctime(&entry.metadata) { - Some(actual) => is_in_range(&actual, (min, max)), - None => { - warn!( - "failed to obtain inode change time for file: {}", - entry.path.display() - ); - true - } - } - } - - // TODO(spawek): windows implementation - #[cfg(not(target_family = "unix"))] - Condition::InodeChangeTime { .. } => true, - - Condition::Size { min, max } => { - is_in_range(&entry.metadata.len(), (min, max)) - } - - #[cfg(target_os = "linux")] - Condition::ExtFlags { - linux_bits_set, - linux_bits_unset, - .. - } => { - // TODO(spawek): support osx bits - let mut ok = true; - - if let Ok(flags) = flags(&entry.path) { - if let Some(linux_bits_set) = linux_bits_set { - ok &= flags & linux_bits_set == flags; - } - if let Some(linux_bits_unset) = linux_bits_unset { - ok &= flags & linux_bits_unset == 0; - } - } else { - warn!( - "failed to obtain extended flags for file: {}", - entry.path.display() - ); - }; - - ok - } - - #[cfg(not(target_os = "linux"))] - Condition::ExtFlags {..} => true, - } -} - -/// Checks if `value` is in range [`min`, `max`] (inclusive on both ends). -/// If range option is equal `None` then the condition is not checked. -fn is_in_range(value: &T, range: (&Option, &Option)) -> bool { - if let Some(min) = &range.0 { - if value < min { - return false; - } - } - if let Some(max) = &range.1 { - if value > max { - return false; - } - } - - true -} - -fn matches( - condition: &ContentsMatchCondition, - entry: &Entry, -) -> Vec { - const BYTES_PER_CHUNK: u64 = 10 * 1024 * 1024; - const OVERLAP_BYTES: u64 = 1024 * 1024; - - let chunks = get_file_chunks( - &entry.path, - &GetFileChunksConfig { - start_offset: condition.start_offset, - max_read_bytes: condition.length, - bytes_per_chunk: BYTES_PER_CHUNK, - overlap_bytes: OVERLAP_BYTES, - }, - ); - let chunks = match chunks { - Some(chunks) => chunks, - None => return vec![], - }; - - let mut matches = vec![]; - let mut offset = condition.start_offset; - - for chunk in chunks { - let chunk = match chunk { - Ok(chunk) => chunk, - Err(err) => { - warn!( - "failed to read chunk from file: {}, error: {}", - entry.path.display(), - err - ); - return vec![]; - } - }; - for m in condition.regex.find_iter(chunk.as_slice()) { - let start = max(m.start() as u64 - condition.bytes_before, 0); - let end = - min(m.end() as u64 + condition.bytes_after, chunk.len() as u64); - let data = chunk[(start as usize)..(end as usize)].to_vec(); - - let mut buf_ref = rrg_proto::jobs::BufferReference::new(); - buf_ref.set_offset(offset + start); - buf_ref.set_length(end - start); - buf_ref.set_data(data); - buf_ref.set_pathspec(entry.path.clone().into()); - - matches.push(buf_ref); - - match condition.mode { - MatchMode::FirstHit => return matches, - MatchMode::AllHits => (), - } - } - offset += BYTES_PER_CHUNK - OVERLAP_BYTES; - } - - matches -} - -/// Reads inode change time from metadata. -#[cfg(target_family = "unix")] -fn read_ctime(metadata: &Metadata) -> Option { - let time = std::time::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs(metadata.ctime() as u64))?; - time.checked_add(std::time::Duration::from_nanos( - metadata.ctime_nsec() as u64 - )) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::thread::sleep; - use std::time::Duration; - - #[test] - fn test_is_in_range() { - assert!(is_in_range(&2, (&Some(1), &Some(3)))); - assert!(is_in_range(&2, (&Some(2), &Some(2)))); - assert!(is_in_range(&2, (&Some(2), &None))); - assert!(is_in_range(&2, (&None, &Some(2)))); - assert!(is_in_range(&2, (&None, &None))); - assert!(!is_in_range(&2, (&Some(3), &None))); - assert!(!is_in_range(&2, (&None, &Some(1)))); - } - - #[test] - #[cfg(target_family = "unix")] - fn test_read_ctime() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "test").unwrap(); - - let metadata = path.metadata().unwrap(); - let ctime = read_ctime(&metadata).unwrap(); - let mtime = metadata.modified().unwrap(); - assert_eq!(ctime, mtime); - } - - #[test] - fn test_size_condition() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "test123456").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - assert!(check_condition( - &Condition::Size { - min: None, - max: None, - }, - &entry - )); - assert!(check_condition( - &Condition::Size { - min: Some(10), - max: Some(10), - }, - &entry - )); - assert!(!check_condition( - &Condition::Size { - min: Some(11), - max: None, - }, - &entry - )); - assert!(!check_condition( - &Condition::Size { - min: None, - max: Some(9), - }, - &entry - )); - } - - #[test] - #[cfg(target_os = "linux")] - fn test_extflags_condition() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "test").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path: path.clone(), - }; - - let flags = flags(&path).unwrap(); - assert_ne!(flags, 0); - - assert!(check_condition( - &Condition::ExtFlags { - linux_bits_set: None, - linux_bits_unset: None, - osx_bits_set: None, - osx_bits_unset: None - }, - &entry - )); - assert!(check_condition( - &Condition::ExtFlags { - linux_bits_set: Some(flags), - linux_bits_unset: Some(flags.reverse_bits()), - osx_bits_set: None, - osx_bits_unset: None - }, - &entry - )); - assert!(!check_condition( - &Condition::ExtFlags { - linux_bits_set: Some(flags.reverse_bits()), - linux_bits_unset: None, - osx_bits_set: None, - osx_bits_unset: None - }, - &entry - )); - assert!(!check_condition( - &Condition::ExtFlags { - linux_bits_set: None, - linux_bits_unset: Some(flags), - osx_bits_set: None, - osx_bits_unset: None - }, - &entry - )); - } - - #[test] - #[cfg(target_family = "unix")] - fn test_modification_time_condition() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "test").unwrap(); - - let metadata = path.metadata().unwrap(); - let modify_time = metadata.modified().unwrap(); - let pre_modify_time = modify_time - Duration::from_nanos(1); - let post_modify_time = modify_time + Duration::from_nanos(1); - - let entry = Entry { metadata, path }; - - assert!(check_condition( - &Condition::ModificationTime { - min: None, - max: None, - }, - &entry - )); - assert!(check_condition( - &Condition::ModificationTime { - min: Some(pre_modify_time), - max: Some(post_modify_time), - }, - &entry - )); - assert!(!check_condition( - &Condition::ModificationTime { - min: Some(post_modify_time), - max: None, - }, - &entry - )); - assert!(!check_condition( - &Condition::ModificationTime { - min: None, - max: Some(pre_modify_time), - }, - &entry - )); - } - - #[test] - #[cfg(target_family = "unix")] - fn test_access_time_condition() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "test").unwrap(); - - // Wait at least 10ms before reading to ensure that the access time - // will be updated. Updating access time is dependent on OS - // configuration, which may cause this test to fail. - sleep(Duration::from_millis(100)); - assert_eq!(std::fs::read_to_string(&path).unwrap(), "test"); - - let metadata = path.metadata().unwrap(); - let access_time = metadata.accessed().unwrap(); - assert!(access_time > metadata.modified().unwrap()); - let pre_access_time = access_time - Duration::from_nanos(1); - let post_access_time = access_time + Duration::from_nanos(1); - - let entry = Entry { metadata, path }; - - assert!(check_condition( - &Condition::AccessTime { - min: None, - max: None, - }, - &entry - )); - assert!(check_condition( - &Condition::AccessTime { - min: Some(pre_access_time), - max: Some(post_access_time), - }, - &entry - )); - assert!(!check_condition( - &Condition::AccessTime { - min: Some(post_access_time), - max: None, - }, - &entry - )); - assert!(!check_condition( - &Condition::AccessTime { - min: None, - max: Some(pre_access_time), - }, - &entry - )); - } - - #[test] - #[cfg(target_family = "unix")] - fn test_change_time_condition() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "test").unwrap(); - - let modify_time = path.metadata().unwrap().modified().unwrap(); - - // Wait at least 10ms before reading to ensure that the change time - // will be updated. Updating change time is dependent on OS - // configuration, which may cause this test to fail. - sleep(Duration::from_millis(1000)); - let mut perms = path.metadata().unwrap().permissions(); - perms.set_readonly(true); - std::fs::set_permissions(&path, perms).unwrap(); - - let metadata = path.metadata().unwrap(); - let change_time = read_ctime(&metadata).unwrap(); - assert!(change_time > modify_time); - let pre_change_time = change_time - Duration::from_nanos(1); - let post_change_time = change_time + Duration::from_nanos(1); - - let entry = Entry { metadata, path }; - - assert!(check_condition( - &Condition::InodeChangeTime { - min: None, - max: None, - }, - &entry - )); - assert!(check_condition( - &Condition::InodeChangeTime { - min: Some(pre_change_time), - max: Some(post_change_time), - }, - &entry - )); - assert!(!check_condition( - &Condition::InodeChangeTime { - min: Some(post_change_time), - max: None, - }, - &entry - )); - assert!(!check_condition( - &Condition::InodeChangeTime { - min: None, - max: Some(pre_change_time), - }, - &entry - )); - } - - #[test] - fn test_multiple_matching_conditions() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "test").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - assert!(check_conditions( - &vec![ - Condition::Size { - min: None, - max: None, - }, - Condition::Size { - min: None, - max: None, - } - ], - &entry - )); - } - - #[test] - fn test_not_all_matching_condition() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "test").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - assert!(!check_conditions( - &vec![ - Condition::Size { - min: Some(5), - max: None, - }, - Condition::Size { - min: None, - max: None, - } - ], - &entry - )); - } - - #[test] - fn test_contents_match_condition() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "123te123st123").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path: path.clone(), - }; - - let matches = matches( - &ContentsMatchCondition { - regex: regex::bytes::Regex::new("te.*st").unwrap(), - mode: MatchMode::AllHits, - bytes_before: 1, - bytes_after: 2, - start_offset: 0, - length: 1000, - }, - &entry, - ); - assert_eq!(matches.len(), 1); - - let m = matches.first().unwrap(); - assert_eq!(m.get_data(), &"3te123st12".as_bytes().to_vec()); - assert_eq!(m.get_length(), 10); - assert_eq!(m.get_offset(), 2); - assert_eq!(m.get_pathspec().get_path(), path.to_str().unwrap()); - } - - #[test] - fn test_contents_match_condition_start_offset() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "123test123test123").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path: path.clone(), - }; - - let matches = matches( - &ContentsMatchCondition { - regex: regex::bytes::Regex::new("test").unwrap(), - mode: MatchMode::AllHits, - bytes_before: 0, - bytes_after: 0, - start_offset: 6, - length: 1000, - }, - &entry, - ); - assert_eq!(matches.len(), 1); - - let m = matches.first().unwrap(); - assert_eq!(m.get_data(), &"test".as_bytes().to_vec()); - assert_eq!(m.get_length(), 4); - assert_eq!(m.get_offset(), 10); - assert_eq!(m.get_pathspec().get_path(), path.to_str().unwrap()); - } - - #[test] - fn test_contents_match_condition_length() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "123test123test123").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path: path.clone(), - }; - - let matches = matches( - &ContentsMatchCondition { - regex: regex::bytes::Regex::new("test").unwrap(), - mode: MatchMode::AllHits, - bytes_before: 0, - bytes_after: 0, - start_offset: 0, - length: 13, - }, - &entry, - ); - assert_eq!(matches.len(), 1); - - let m = matches.first().unwrap(); - assert_eq!(m.get_data(), &"test".as_bytes().to_vec()); - assert_eq!(m.get_length(), 4); - assert_eq!(m.get_offset(), 3); - assert_eq!(m.get_pathspec().get_path(), path.to_str().unwrap()); - } - - #[test] - fn test_contents_match_condition_multiple_matches() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "123test123tttt123").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path: path.clone(), - }; - - let matches = matches( - &ContentsMatchCondition { - regex: regex::bytes::Regex::new(r"t\w\wt").unwrap(), - mode: MatchMode::AllHits, - bytes_before: 0, - bytes_after: 0, - start_offset: 0, - length: 1000, - }, - &entry, - ); - assert_eq!(matches.len(), 2); - - { - let m = matches.get(0).unwrap(); - assert_eq!(m.get_data(), &"test".as_bytes().to_vec()); - assert_eq!(m.get_length(), 4); - assert_eq!(m.get_offset(), 3); - assert_eq!(m.get_pathspec().get_path(), path.to_str().unwrap()); - } - - { - let m = matches.get(1).unwrap(); - assert_eq!(m.get_data(), &"tttt".as_bytes().to_vec()); - assert_eq!(m.get_length(), 4); - assert_eq!(m.get_offset(), 10); - assert_eq!(m.get_pathspec().get_path(), path.to_str().unwrap()); - } - } - - #[test] - fn test_contents_match_condition_stop_on_first_hit() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "123test123tttt123").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path: path.clone(), - }; - - let matches = matches( - &ContentsMatchCondition { - regex: regex::bytes::Regex::new(r"t\w\wt").unwrap(), - mode: MatchMode::FirstHit, - bytes_before: 0, - bytes_after: 0, - start_offset: 0, - length: 1000, - }, - &entry, - ); - assert_eq!(matches.len(), 1); - - let m = matches.first().unwrap(); - assert_eq!(m.get_data(), &"test".as_bytes().to_vec()); - assert_eq!(m.get_length(), 4); - assert_eq!(m.get_offset(), 3); - assert_eq!(m.get_pathspec().get_path(), path.to_str().unwrap()); - } - - #[test] - fn test_multiple_contents_match_conditions_ok_case() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "123test123tttt123").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path: path.clone(), - }; - - let matches = find_matches( - &vec![ - ContentsMatchCondition { - regex: regex::bytes::Regex::new(r"test").unwrap(), - mode: MatchMode::FirstHit, - bytes_before: 0, - bytes_after: 0, - start_offset: 0, - length: 1000, - }, - ContentsMatchCondition { - regex: regex::bytes::Regex::new(r"tttt").unwrap(), - mode: MatchMode::FirstHit, - bytes_before: 0, - bytes_after: 0, - start_offset: 0, - length: 1000, - }, - ], - &entry, - ); - assert_eq!(matches.len(), 2); - - { - let m = matches.get(0).unwrap(); - assert_eq!(m.get_data(), &"test".as_bytes().to_vec()); - assert_eq!(m.get_length(), 4); - assert_eq!(m.get_offset(), 3); - assert_eq!(m.get_pathspec().get_path(), path.to_str().unwrap()); - } - - { - let m = matches.get(1).unwrap(); - assert_eq!(m.get_data(), &"tttt".as_bytes().to_vec()); - assert_eq!(m.get_length(), 4); - assert_eq!(m.get_offset(), 10); - assert_eq!(m.get_pathspec().get_path(), path.to_str().unwrap()); - } - } - - #[test] - fn test_multiple_contents_match_conditions_returns_nothing_if_1_condition_fails( - ) { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "123test123").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path: path.clone(), - }; - - let matches = find_matches( - &vec![ - ContentsMatchCondition { - regex: regex::bytes::Regex::new(r"test").unwrap(), - mode: MatchMode::FirstHit, - bytes_before: 0, - bytes_after: 0, - start_offset: 0, - length: 1000, - }, - ContentsMatchCondition { - regex: regex::bytes::Regex::new(r"abcd").unwrap(), - mode: MatchMode::FirstHit, - bytes_before: 0, - bytes_after: 0, - start_offset: 0, - length: 1000, - }, - ], - &entry, - ); - assert!(matches.is_empty()); - } -} diff --git a/crates/rrg/src/action/deprecated/finder/download.rs b/crates/rrg/src/action/deprecated/finder/download.rs deleted file mode 100644 index ff9d56ee..00000000 --- a/crates/rrg/src/action/deprecated/finder/download.rs +++ /dev/null @@ -1,301 +0,0 @@ -use super::chunks::{ - get_file_chunks, Chunks, GetFileChunksConfig, -}; -use super::request::{ - DownloadActionOptions, HashActionOptions, -}; -use crate::fs::Entry; -use sha2::{Digest, Sha256}; -use std::fs::File; -use std::io::{BufReader, Take}; - -#[derive(Debug)] -pub enum Response { - /// Download action is not performed and no further action is required. - Skip(), - /// File was not downloaded, but hash action must be executed. - HashRequest(HashActionOptions), - /// Chunks of data to be uploaded. - CollectData(Chunks>>), -} - -/// Performs `download` action logic and returns file contents to be uploaded -/// or another action to be executed. -pub fn download(entry: &Entry, config: &DownloadActionOptions) -> Response { - if entry.metadata.len() > config.max_size { - use rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy::*; - match config.oversized_file_policy { - SKIP => { - return Response::Skip(); - } - DOWNLOAD_TRUNCATED => (), - HASH_TRUNCATED => { - let hash_config = HashActionOptions { - max_size: config.max_size, - oversized_file_policy: - rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy::HASH_TRUNCATED, - }; - return Response::HashRequest(hash_config); - } - }; - } - - let chunks = get_file_chunks( - &entry.path, - &GetFileChunksConfig { - max_read_bytes: config.max_size, - bytes_per_chunk: config.chunk_size, - start_offset: 0, - overlap_bytes: 0, - }, - ); - - match chunks { - Some(chunks) => Response::CollectData(chunks), - None => Response::Skip(), - } -} - -/// A type representing unique identifier of a given chunk. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct ChunkId { - /// A SHA-256 digest of the referenced chunk data. - sha256: [u8; 32], - offset: u64, - length: u64, -} - -impl ChunkId { - /// Creates a chunk identifier for the given chunk. - pub fn make(chunk: &[u8], offset: u64) -> ChunkId { - ChunkId { - sha256: Sha256::digest(chunk).into(), - length: chunk.len() as u64, - offset, - } - } -} - -#[derive(Debug)] -pub struct DownloadEntry { - pub chunk_ids: Vec, - pub chunk_size: u64, -} - -impl From for rrg_proto::jobs::BlobImageDescriptor { - - fn from(entry: DownloadEntry) -> rrg_proto::jobs::BlobImageDescriptor { - let chunks = entry.chunk_ids - .into_iter() - .map(|chunk_id| chunk_id.into()) - .collect(); - - let mut proto = rrg_proto::jobs::BlobImageDescriptor::new(); - proto.set_chunk_size(entry.chunk_size); - proto.set_chunks(chunks); - - proto - } -} - -impl From for rrg_proto::jobs::BlobImageChunkDescriptor { - - fn from(chunk_id: ChunkId) -> rrg_proto::jobs::BlobImageChunkDescriptor { - let mut proto = rrg_proto::jobs::BlobImageChunkDescriptor::new(); - proto.set_offset(chunk_id.offset); - proto.set_length(chunk_id.length); - proto.set_digest(chunk_id.sha256.to_vec()); - - proto - } -} - -/// A type representing a particular chunk of the returned timeline. -pub struct Chunk { - pub data: Vec, -} - -impl crate::response::Item for Chunk { - type Proto = rrg_proto::jobs::DataBlob; - - fn into_proto(self) -> rrg_proto::jobs::DataBlob { - self.data.into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_chunked_download() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "some_data").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - let result = download( - &entry, - &DownloadActionOptions { - max_size: 100, - oversized_file_policy: rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy::SKIP, - use_external_stores: false, - chunk_size: 5, - }, - ); - - let mut chunks = match result { - Response::CollectData(chunks) => chunks, - _ => panic!("Unexpected result type."), - }; - - assert_eq!( - chunks.next().unwrap().unwrap(), - "some_".bytes().collect::>() - ); - assert_eq!( - chunks.next().unwrap().unwrap(), - "data".bytes().collect::>() - ); - assert!(chunks.next().is_none()); - } - - #[test] - fn test_no_empty_chunk_download() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "some_").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - let result = download( - &entry, - &DownloadActionOptions { - max_size: 100, - oversized_file_policy: rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy::SKIP, - use_external_stores: false, - chunk_size: 5, - }, - ); - - let mut chunks = match result { - Response::CollectData(chunks) => chunks, - _ => panic!("Unexpected result type."), - }; - - assert_eq!( - chunks.next().unwrap().unwrap(), - "some_".bytes().collect::>() - ); - assert!(chunks.next().is_none()); - } - - #[test] - fn test_skip_above_max_size() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "some_1").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - let result = download( - &entry, - &DownloadActionOptions { - max_size: 5, - oversized_file_policy: rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy::SKIP, - use_external_stores: false, - chunk_size: 5, - }, - ); - - assert!(matches!(result, Response::Skip())); - } - - #[test] - fn test_hash_above_max_size() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "some_1").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - let result = download( - &entry, - &DownloadActionOptions { - max_size: 5, - oversized_file_policy: rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy::HASH_TRUNCATED, - use_external_stores: false, - chunk_size: 5, - }, - ); - - assert!(matches!( - result, - Response::HashRequest(HashActionOptions { - max_size: 5, - oversized_file_policy: rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy::HASH_TRUNCATED, - }) - )); - } - - #[test] - fn test_download_truncated_above_max_size() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "some_1").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - let result = download( - &entry, - &DownloadActionOptions { - max_size: 5, - oversized_file_policy: rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy::DOWNLOAD_TRUNCATED, - use_external_stores: false, - chunk_size: 3, - }, - ); - - let mut chunks = match result { - Response::CollectData(chunks) => chunks, - _ => panic!("Unexpected result type."), - }; - - assert_eq!( - chunks.next().unwrap().unwrap(), - "som".bytes().collect::>() - ); - assert_eq!( - chunks.next().unwrap().unwrap(), - "e_".bytes().collect::>() - ); - assert!(chunks.next().is_none()); - } - - #[test] - fn test_chunk_id() { - let chunk = ChunkId::make(b"some_test_data", 5); - assert_eq!(&chunk.length, &14); - assert_eq!(&chunk.offset, &5); - assert_eq!( - &chunk.sha256, - &[ - 0xd7, 0x6d, 0x85, 0xad, 0xca, 0x8a, 0xfa, 0xd2, 0x05, 0xed, - 0xeb, 0xc1, 0x1f, 0x9b, 0x50, 0x86, 0xbc, 0xa7, 0x5a, 0xcb, - 0x51, 0x2a, 0x74, 0x8b, 0xc7, 0x96, 0x60, 0xe1, 0x34, 0x6a, - 0xf5, 0x46 - ] - ); - } -} diff --git a/crates/rrg/src/action/deprecated/finder/glob.rs b/crates/rrg/src/action/deprecated/finder/glob.rs deleted file mode 100644 index de725e0e..00000000 --- a/crates/rrg/src/action/deprecated/finder/glob.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2020 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. - -use regex::Regex; - -/// Converts glob expression into a Rust Regex. -/// -/// This implementation is inspired by CPython code: -/// https://github.com/python/cpython/blob/2.7/Lib/fnmatch.py -pub fn glob_to_regex(pat: &str) -> Result { - let chars: Vec = pat.chars().collect(); - let mut i: usize = 0; - let n: usize = chars.len(); - let mut res = String::new(); - while i < n { - let c = chars[i]; - i = i + 1; - if c == '*' { - res = res + ".*"; - } else if c == '?' { - res = res + "."; - } else if c == '[' { - let mut j = i; - if j < n && chars[j] == '!' { - j = j + 1; - } - if j < n && chars[j] == ']' { - j = j + 1; - } - while j < n && chars[j] != ']' { - j = j + 1; - } - if j >= n { - res = res + r"\["; - } else { - let mut stuff = pat[i..j].replace(r"\", r"\\").to_owned(); - let stuff_first_char: char = stuff.chars().next().unwrap(); - i = j + 1; - if stuff_first_char == '!' { - stuff = String::from("^") + &stuff[1..]; - } else if stuff_first_char == '^' { - stuff = String::from(r"\") + &stuff; - } - res = format!("{}[{}]", res, stuff); - } - } else { - res = res + ®ex::escape(&c.to_string()); - } - } - - res = res.replace(r"\\", r"\"); - - // Resulting regex is supposed to perform full matches on the text. - res = format!("^{}$", res); - - Regex::new(&res) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_glob_to_regex() { - assert_eq!(glob_to_regex("*").unwrap().as_str(), "^.*$"); - assert_eq!(glob_to_regex("?").unwrap().as_str(), "^.$"); - assert_eq!(glob_to_regex("a?b*").unwrap().as_str(), "^a.b.*$"); - assert_eq!(glob_to_regex("[abc]").unwrap().as_str(), "^[abc]$"); - assert_eq!(glob_to_regex("[]]").unwrap().as_str(), "^[]]$"); - assert_eq!(glob_to_regex("[!x]").unwrap().as_str(), "^[^x]$"); - assert_eq!(glob_to_regex("[^x]").unwrap().as_str(), r"^[\^x]$"); - assert_eq!(glob_to_regex("[x").unwrap().as_str(), r"^\[x$"); - assert_eq!(glob_to_regex("[a]]").unwrap().as_str(), r"^[a]\]$"); - assert_eq!(glob_to_regex(r"[\\]\\").unwrap().as_str(), r"^[\\]\\$"); - assert_eq!(glob_to_regex("ąźć").unwrap().as_str(), "^ąźć$"); - } -} diff --git a/crates/rrg/src/action/deprecated/finder/groups.rs b/crates/rrg/src/action/deprecated/finder/groups.rs deleted file mode 100644 index 6909681d..00000000 --- a/crates/rrg/src/action/deprecated/finder/groups.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2020 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. - -use lazy_static::lazy_static; -use regex::Regex; - -/// Performs group expansion on a given path. -/// For example, given path `foo/{bar,baz}/{quux,norf}` this method will yield -/// `foo/bar/quux`, `foo/bar/norf`, `foo/baz/quux`, `foo/baz/norf`. -/// The order of expansion is defined as if there were generated by `for` loops -/// nested in order of groups in the path. -/// -/// Differences from GRR: -/// - support for empty value in the 1st and 2nd alternative e.g.: -/// - `/{,asd}`, `/{asd,}` are resolved here, but not in GRR -/// - `/{a,b,}` are resolved both in GRR and in here -/// - support for a single alternative e.g.: -/// - `/{asd}` is resolved here, but not in GRR -pub fn expand_groups(path: &str) -> Vec { - lazy_static! { - static ref GROUPS_MATCHER: Regex = Regex::new(r"\{[^\{]+?\}").unwrap(); - } - - let mut offset = 0; - let mut chunks: Vec> = vec![]; - - for m in GROUPS_MATCHER.find_iter(path) { - chunks.push(vec![&path[offset..m.start()]]); - chunks.push(path[m.start() + 1..m.end() - 1].split(',').collect()); - offset = m.end(); - } - chunks.push(vec![&path[offset..]]); - - join_chunks(chunks) -} - -/// In the input the inner Vec is a list of alternatives available for -/// the chunk, the outer Vec is a list of chunks - e.g. -/// [["/home/"], -/// ["spawek/", "hanuszczak/"], -/// [".gitignore", ".hgignore"]] -/// -/// The output consists of all the possible concatenations of all chunks in -/// the order of appearance, where exactly 1 alternative is used from each -/// chunk. -/// The order of alternatives is defined as if there were generated by `for` -/// loops nested in order of chunks. -/// -/// # Examples -/// ```ignore -/// assert_eq!( -/// join_chunks(vec![ -/// vec!["/home/"], -/// vec!["spawek/", "hanuszczak/"], -/// vec![".gitignore", ".hgignore"] -/// ]), -/// vec![ -/// "/home/spawek/.gitignore", -/// "/home/spawek/.hgignore", -/// "/home/hanuszczak/.gitignore", -/// "/home/hanuszczak/.hgignore" -/// ] -/// ); -/// ``` -fn join_chunks(chunks: Vec>) -> Vec { - let first_chunk = match chunks.first() { - Some(v) => v, - None => return vec![], - }; - - let mut ret: Vec = - first_chunk.iter().map(|x| (*x).to_owned()).collect(); - for chunk in chunks.into_iter().skip(1) { - let mut new_ret = vec![]; - for prefix in ret { - for suffix in &chunk { - new_ret.push(prefix.to_owned() + suffix); - } - } - ret = new_ret; - } - - ret -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_expand_groups() { - assert_eq!(expand_groups("/some/path"), vec!["/some/path"]); - assert_eq!( - expand_groups("/some/{1,2,3}"), - vec!["/some/1", "/some/2", "/some/3"] - ); - assert_eq!( - expand_groups("/some/{1,2}/{3,4}"), - vec!["/some/1/3", "/some/1/4", "/some/2/3", "/some/2/4"] - ); - assert_eq!( - expand_groups("/some/{1,}a/{3,}a"), - vec!["/some/1a/3a", "/some/1a/a", "/some/a/3a", "/some/a/a"] - ); - assert_eq!(expand_groups("/some/{123}"), vec!["/some/123"]); - } - - #[test] - fn test_join_chunks_ok_case() { - assert_eq!( - join_chunks(vec![ - vec!["/home/"], - vec!["spawek/", "hanuszczak/"], - vec![".gitignore", ".hgignore"] - ]), - vec![ - "/home/spawek/.gitignore", - "/home/spawek/.hgignore", - "/home/hanuszczak/.gitignore", - "/home/hanuszczak/.hgignore" - ] - ); - } - - #[test] - fn test_join_chunks_on_empty_input() { - assert!(join_chunks(vec![]).is_empty()) - } - - #[test] - fn test_join_chunks_on_empty_chunk() { - assert!( - join_chunks(vec![vec!["/home/"], vec![], vec![".gitignore"]]) - .is_empty() - ); - } - - #[test] - fn test_join_chunks_on_input_without_groups() { - assert_eq!( - join_chunks(vec![vec!["/home"]]), - vec!["/home"] - ); - } -} diff --git a/crates/rrg/src/action/deprecated/finder/hash.rs b/crates/rrg/src/action/deprecated/finder/hash.rs deleted file mode 100644 index 3b464eba..00000000 --- a/crates/rrg/src/action/deprecated/finder/hash.rs +++ /dev/null @@ -1,195 +0,0 @@ -use super::request::HashActionOptions; -use crate::fs::Entry; -use digest::Digest as _; -use log::warn; -use md5::Md5; -use rrg_macro::ack; -use sha1::Sha1; -use sha2::Sha256; -use std::cmp::min; -use std::fs::File; -use std::io::Read; - -/// Hashes data writen to it using SHA-1, SHA-256 and MD5 algorithms. -struct Hasher { - /// Digest with SHA-1 hash. - sha1: Sha1, - /// Digest with SHA-256 hash. - sha256: Sha256, - /// Digest with MD5 hash. - md5: Md5, - /// Stores total number of bytes inserted into hasher. - total_byte_count: u64, -} - -impl Hasher { - pub fn new() -> Hasher { - Hasher { - sha1: sha1::Sha1::new(), - sha256: sha2::Sha256::new(), - md5: md5::Md5::new(), - total_byte_count: 0, - } - } -} - -impl std::io::Write for Hasher { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.sha1.update(buf); - self.sha256.update(buf); - self.md5.update(buf); - self.total_byte_count += buf.len() as u64; - - Ok(buf.len()) - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} - -/// Performs `hash` action on the file in `entry` and returns the result to be reported in case of success. -pub fn hash(entry: &Entry, config: &HashActionOptions) -> Option { - use rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy::*; - match config.oversized_file_policy { - SKIP => { - if entry.metadata.len() > config.max_size { - return None; - } - } - HASH_TRUNCATED => {} - }; - - let file = ack! { - File::open(&entry.path), - error: "failed to open file: {}", entry.path.display() - }?; - let mut file = file.take(config.max_size); - - let mut hasher = Hasher::new(); - let read_bytes = ack! { - std::io::copy(&mut file, &mut hasher), - error: "failed to copy data from: {}", entry.path.display() - }?; - - let expected_bytes = min(entry.metadata.len(), config.max_size); - if read_bytes != expected_bytes { - warn!( - "failed to read all data from: {}, {} bytes were read, but {} were expected", - entry.path.display(), - &read_bytes, - expected_bytes - ); - return None; - } - - Some(FileHash { - sha1: hasher.sha1.finalize().to_vec(), - sha256: hasher.sha256.finalize().to_vec(), - md5: hasher.md5.finalize().to_vec(), - num_bytes: hasher.total_byte_count, - }) -} - -#[derive(Debug)] -pub struct FileHash { - pub sha256: std::vec::Vec, - pub sha1: std::vec::Vec, - pub md5: std::vec::Vec, - pub num_bytes: u64, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_hash_values() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "some_test_data").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path: path, - }; - - let result = hash( - &entry, - &HashActionOptions { - max_size: 14, - oversized_file_policy: rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy::SKIP, - }, - ) - .unwrap(); - - assert_eq!( - result.sha1, - vec![ - 0xa6, 0x2a, 0x6d, 0x59, 0x91, 0x23, 0x8a, 0xe7, 0x2d, 0x81, - 0xfe, 0x6e, 0x47, 0x69, 0xb3, 0x04, 0x3d, 0x9f, 0xe6, 0x70 - ] - .to_vec() - ); - assert_eq!( - result.sha256, - vec![ - 0xd7, 0x6d, 0x85, 0xad, 0xca, 0x8a, 0xfa, 0xd2, 0x05, 0xed, - 0xeb, 0xc1, 0x1f, 0x9b, 0x50, 0x86, 0xbc, 0xa7, 0x5a, 0xcb, - 0x51, 0x2a, 0x74, 0x8b, 0xc7, 0x96, 0x60, 0xe1, 0x34, 0x6a, - 0xf5, 0x46 - ] - .to_vec() - ); - assert_eq!( - result.md5, - vec![ - 0xe0, 0x91, 0xb6, 0xf1, 0xa2, 0x33, 0x04, 0x9d, 0x22, 0xd2, - 0x80, 0x7f, 0xa8, 0x08, 0x6f, 0x3f - ] - .to_vec() - ); - assert_eq!(result.num_bytes, 14); - } - - #[test] - fn test_trim_file_over_max_size() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "some_test_data").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - let result = hash( - &entry, - &HashActionOptions { - max_size: 10, - oversized_file_policy: rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy::HASH_TRUNCATED, - }, - ) - .unwrap(); - - assert_eq!(result.num_bytes, 10); - } - - #[test] - fn test_skip_file_over_max_size() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("f"); - std::fs::write(&path, "some_test_data").unwrap(); - let entry = Entry { - metadata: path.metadata().unwrap(), - path, - }; - - assert!(hash( - &entry, - &HashActionOptions { - max_size: 10, - oversized_file_policy: rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy::SKIP, - }, - ) - .is_none()); - } -} diff --git a/crates/rrg/src/action/deprecated/finder/request.rs b/crates/rrg/src/action/deprecated/finder/request.rs deleted file mode 100644 index 0fe3198e..00000000 --- a/crates/rrg/src/action/deprecated/finder/request.rs +++ /dev/null @@ -1,843 +0,0 @@ -// Copyright 2020 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. - -//! Defines an internal type for client side file finder action and provides -//! a function converting proto format of the request -//! `rrg_proto::FileFinderArgs` to the internal format. - -use log::info; -use protobuf::ProtobufEnum; -use std::fmt::Write; - -#[derive(Debug)] -pub struct Request { - /// A list of paths to glob that supports `**` path recursion and - /// alternatives in form of `{a,b}`. - pub path_queries: Vec, - /// Stat options. Stat has to be executed on any kind of request. - pub stat_options: StatActionOptions, - /// Additional action to be performed. Stat action is always performed. - pub action: Option, - /// Conditions that must be met by found file for the action to - /// be performed on it. - pub conditions: Vec, - /// File content matching conditions that must be met for the action to - /// be performed. - pub contents_match_conditions: Vec, - /// Work with all kinds of files - not only with regular ones. - pub process_non_regular_files: bool, - /// Should symbolic links be followed by recursive search. - pub follow_links: bool, - /// Behavior for crossing devices when searching the filesystem. - pub xdev_mode: rrg_proto::flows::FileFinderArgs_XDev, -} - -#[derive(Debug)] -pub enum Action { - /// Get the hash of the file. - Hash(HashActionOptions), - /// Download the file. - Download(DownloadActionOptions), -} - -#[derive(Debug)] -pub struct StatActionOptions { - /// Should symbolic link be resolved if it is a target of the action. - pub follow_symlink: bool, - /// Should linux extended file attributes be collected. - pub collect_ext_attrs: bool, -} - -#[derive(Debug)] -pub struct HashActionOptions { - /// Maximum file size in bytes acceptable by the action. - pub max_size: u64, - /// Action to perform when requested file is bigger than `max_size`. - pub oversized_file_policy: rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy, -} - -#[derive(Debug)] -pub struct DownloadActionOptions { - /// Maximum file size in bytes acceptable by the action. - pub max_size: u64, - /// Action to perform when requested file is bigger than `max_size`. - pub oversized_file_policy: rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy, - /// If true, look in any defined external file stores for files before - /// downloading them, and offer any new files to external stores. This - /// should be true unless the external checks are misbehaving. - pub use_external_stores: bool, - /// Number of bytes per chunk that the downloaded file is divided into. - pub chunk_size: u64, -} - -#[derive(Debug)] -pub enum Condition { - ModificationTime { - min: Option, - max: Option, - }, - AccessTime { - min: Option, - max: Option, - }, - InodeChangeTime { - min: Option, - max: Option, - }, - Size { - min: Option, - max: Option, - }, - ExtFlags { - linux_bits_set: Option, - linux_bits_unset: Option, - osx_bits_set: Option, - osx_bits_unset: Option, - }, -} - -#[derive(Debug, PartialEq)] -pub enum MatchMode { - AllHits, - FirstHit, -} - -#[derive(Debug)] -pub struct ContentsMatchCondition { - pub regex: regex::bytes::Regex, - pub mode: MatchMode, - pub bytes_before: u64, - pub bytes_after: u64, - pub start_offset: u64, - pub length: u64, -} - -impl From for StatActionOptions { - fn from(proto: rrg_proto::flows::FileFinderStatActionOptions) -> StatActionOptions { - StatActionOptions { - follow_symlink: proto.get_resolve_links(), - collect_ext_attrs: proto.get_collect_ext_attrs(), - } - } -} - -fn time_from_micros(micros: u64) -> std::time::SystemTime { - std::time::UNIX_EPOCH + std::time::Duration::from_micros(micros) -} - -fn into_action(proto: rrg_proto::flows::FileFinderAction) -> Result, crate::request::ParseArgsError> { - // `FileFinderAction::action_type` defines which action will be performed. - // Only options from the selected action are read. - use rrg_proto::flows::FileFinderAction_Action::*; - Ok(Some(match proto.get_action_type() { - STAT => return Ok(None), - HASH => Action::try_from(proto.get_hash().to_owned())?, - DOWNLOAD => Action::try_from(proto.get_download().to_owned())?, - })) -} - -impl TryFrom for Action { - type Error = crate::request::ParseArgsError; - - fn try_from( - proto: rrg_proto::flows::FileFinderHashActionOptions, - ) -> Result { - Ok(Action::Hash(HashActionOptions { - oversized_file_policy: proto.get_oversized_file_policy(), - max_size: proto.get_max_size(), - })) - } -} - -impl TryFrom for Action { - type Error = crate::request::ParseArgsError; - - fn try_from( - proto: rrg_proto::flows::FileFinderDownloadActionOptions, - ) -> Result { - Ok(Action::Download(DownloadActionOptions { - oversized_file_policy: proto.get_oversized_file_policy(), - max_size: proto.get_max_size(), - use_external_stores: proto.get_use_external_stores(), - chunk_size: proto.get_chunk_size(), - })) - } -} - -fn get_modification_time_condition( - proto: &rrg_proto::flows::FileFinderModificationTimeCondition, -) -> Result, crate::request::ParseArgsError> { - let min = if proto.has_min_last_modified_time() { - Some(time_from_micros(proto.get_min_last_modified_time())) - } else { - None - }; - - let max = if proto.has_max_last_modified_time() { - Some(time_from_micros(proto.get_max_last_modified_time())) - } else { - None - }; - - if min.is_some() || max.is_some() { - return Ok(Some(Condition::ModificationTime { min, max })); - } - Ok(None) -} - -fn get_access_time_condition( - proto: &rrg_proto::flows::FileFinderAccessTimeCondition, -) -> Result, crate::request::ParseArgsError> { - let min = if proto.has_min_last_access_time() { - Some(time_from_micros(proto.get_min_last_access_time())) - } else { - None - }; - - let max = if proto.has_max_last_access_time() { - Some(time_from_micros(proto.get_max_last_access_time())) - } else { - None - }; - - if min.is_some() || max.is_some() { - return Ok(Some(Condition::AccessTime { min, max })); - } - Ok(None) -} - -fn get_inode_change_time_condition( - proto: &rrg_proto::flows::FileFinderInodeChangeTimeCondition, -) -> Result, crate::request::ParseArgsError> { - let min = if proto.has_min_last_inode_change_time() { - Some(time_from_micros(proto.get_min_last_inode_change_time())) - } else { - None - }; - - let max = if proto.has_max_last_inode_change_time() { - Some(time_from_micros(proto.get_max_last_inode_change_time())) - } else { - None - }; - - if min.is_some() || max.is_some() { - return Ok(Some(Condition::InodeChangeTime { min, max })); - } - Ok(None) -} - -fn get_size_condition( - proto: &rrg_proto::flows::FileFinderSizeCondition, -) -> Option { - let min = if proto.has_min_file_size() { - Some(proto.get_min_file_size()) - } else { - None - }; - - let max = if proto.has_max_file_size() { - Some(proto.get_max_file_size()) - } else { - None - }; - - if min.is_some() || max.is_some() { - return Some(Condition::Size { min, max }); - } - None -} - -fn get_ext_flags_condition( - proto: &rrg_proto::flows::FileFinderExtFlagsCondition, -) -> Option { - if proto.has_linux_bits_set() || proto.has_linux_bits_unset() || - proto.has_osx_bits_set() || proto.has_osx_bits_unset() { - return Some(Condition::ExtFlags { - linux_bits_set: Some(proto.get_linux_bits_set()), - linux_bits_unset: Some(proto.get_linux_bits_unset()), - osx_bits_set: Some(proto.get_osx_bits_set()), - osx_bits_unset: Some(proto.get_osx_bits_unset()), - }); - } - None -} - -fn parse_regex(bytes: &[u8]) -> Result { - let str = match std::str::from_utf8(bytes) { - Ok(v) => Ok(v), - Err(e) => Err(crate::request::ParseArgsError::invalid_field("regex", e)), - }?; - - match regex::bytes::Regex::new(str) { - Ok(v) => Ok(v), - Err(e) => Err(crate::request::ParseArgsError::invalid_field("regex", e)), - } -} - -fn constant_literal_to_regex( - bytes: &[u8], -) -> Result { - let mut str = String::new(); - for b in bytes { - // Unwrap used on a string which can't return I/O error. - write!(&mut str, r"\x{:x}", b).unwrap(); - } - match regex::bytes::Regex::new(&str) { - Ok(v) => Ok(v), - Err(e) => Err(crate::request::ParseArgsError::invalid_field("literal", e)), - } -} - -fn get_contents_regex_match_condition( - proto: &rrg_proto::flows::FileFinderContentsRegexMatchCondition, -) -> Result, crate::request::ParseArgsError> { - let bytes_before = proto.get_bytes_before() as u64; - let bytes_after = proto.get_bytes_after() as u64; - let start_offset = proto.get_start_offset(); - let length = proto.get_length(); - - use rrg_proto::flows::FileFinderContentsRegexMatchCondition_Mode::*; - let mode = match proto.get_mode() { - ALL_HITS => MatchMode::AllHits, - FIRST_HIT => MatchMode::FirstHit, - }; - - let regex = if proto.has_regex() { - parse_regex(proto.get_regex())? - } else { - return Ok(None); - }; - - Ok(Some(ContentsMatchCondition { - regex, - mode, - bytes_before, - bytes_after, - start_offset, - length, - })) -} - -/// Literal match is performed by generating a regex condition as they have -/// the same semantics. -fn get_contents_literal_match_condition( - proto: &rrg_proto::flows::FileFinderContentsLiteralMatchCondition, -) -> Result, crate::request::ParseArgsError> { - let bytes_before = proto.get_bytes_before() as u64; - let bytes_after = proto.get_bytes_after() as u64; - let start_offset = proto.get_start_offset(); - let length = proto.get_length(); - - use rrg_proto::flows::FileFinderContentsLiteralMatchCondition_Mode::*; - let mode = match proto.get_mode() { - ALL_HITS => MatchMode::AllHits, - FIRST_HIT => MatchMode::FirstHit, - }; - - let regex = if proto.has_literal() { - constant_literal_to_regex(proto.get_literal())? - } else { - return Ok(None); - }; - - Ok(Some(ContentsMatchCondition { - regex, - mode, - bytes_before, - bytes_after, - start_offset, - length, - })) -} - -fn get_conditions( - proto: &[rrg_proto::flows::FileFinderCondition], -) -> Result, crate::request::ParseArgsError> { - let mut conditions = vec![]; - for proto_condition in proto { - conditions.extend(get_condition(proto_condition)?); - } - Ok(conditions) -} - -fn get_condition( - proto: &rrg_proto::flows::FileFinderCondition, -) -> Result, crate::request::ParseArgsError> { - use rrg_proto::flows::FileFinderCondition_Type::*; - Ok(match proto.get_condition_type() { - MODIFICATION_TIME => { - get_modification_time_condition(proto.get_modification_time())? - } - ACCESS_TIME => { - get_access_time_condition(proto.get_access_time())? - } - INODE_CHANGE_TIME => { - get_inode_change_time_condition(proto.get_inode_change_time())? - } - SIZE => get_size_condition(proto.get_size()), - EXT_FLAGS => get_ext_flags_condition(proto.get_ext_flags()), - CONTENTS_REGEX_MATCH => None, - CONTENTS_LITERAL_MATCH => None, - }) -} - -fn get_contents_match_conditions( - proto: &[rrg_proto::flows::FileFinderCondition], -) -> Result, crate::request::ParseArgsError> { - let mut conditions = vec![]; - for proto_condition in proto { - conditions.extend(get_contents_match_condition(proto_condition)?); - } - Ok(conditions) -} - -// TODO: maybe it can return Option instead of Vec now -fn get_contents_match_condition( - proto: &rrg_proto::flows::FileFinderCondition, -) -> Result, crate::request::ParseArgsError> { - if !proto.has_condition_type() { - return Ok(None); - } - - use rrg_proto::flows::FileFinderCondition_Type::*; - Ok(match proto.get_condition_type() { - CONTENTS_REGEX_MATCH => { - get_contents_regex_match_condition(proto.get_contents_regex_match())? - } - CONTENTS_LITERAL_MATCH => { - get_contents_literal_match_condition(proto.get_contents_literal_match())? - } - _ => None, - }) -} - -/// An error type for errors where we received an unuspported path type. -#[derive(Debug)] -struct UnsupportedPathTypeError { - /// The path type that we received but we do not support. - path_type: rrg_proto::jobs::PathSpec_PathType, -} - -impl std::fmt::Display for UnsupportedPathTypeError { - - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(fmt, "unuspported path type ({})", self.path_type.value()) - } -} - -impl std::error::Error for UnsupportedPathTypeError { -} - -impl crate::request::Args for Request { - type Proto = rrg_proto::flows::FileFinderArgs; - - fn from_proto(proto: rrg_proto::flows::FileFinderArgs) -> Result { - info!("File Finder: proto request: {:#?}", &proto); - if proto.get_pathtype() != rrg_proto::jobs::PathSpec_PathType::OS { - let error = UnsupportedPathTypeError { - path_type: proto.get_pathtype(), - }; - return Err(crate::request::ParseArgsError::invalid_field("pathtype", error)); - } - - let follow_links = proto.get_follow_links(); - let process_non_regular_files = proto.get_process_non_regular_files(); - let xdev_mode = proto.get_xdev(); - let conditions = get_conditions(proto.get_conditions())?; - let contents_match_conditions = - get_contents_match_conditions(proto.get_conditions())?; - - let action = into_action(proto.get_action().to_owned())?; - let stat_options = StatActionOptions::from(proto.get_action().get_stat().to_owned()); - - Ok(Request { - path_queries: proto.get_paths().to_owned(), - stat_options, - action, - conditions, - contents_match_conditions, - follow_links, - process_non_regular_files, - xdev_mode, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::request::Args as _; - - #[test] - fn test_empty_request() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let request = Request::from_proto(proto).unwrap(); - - assert!(request.path_queries.is_empty()); - assert!(request.conditions.is_empty()); - assert_eq!(request.process_non_regular_files, false); - assert_eq!(request.follow_links, false); - assert_eq!(request.xdev_mode, rrg_proto::flows::FileFinderArgs_XDev::LOCAL); - } - - #[test] - fn test_basic_root_parameters() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_paths().push("abc".to_string()); - proto.mut_paths().push("cba".to_string()); - proto.set_process_non_regular_files(true); - proto.set_follow_links(true); - proto.set_xdev(rrg_proto::flows::FileFinderArgs_XDev::ALWAYS); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!( - request.path_queries, - vec!["abc".to_string(), "cba".to_string()] - ); - assert_eq!(request.process_non_regular_files, true); - assert_eq!(request.follow_links, true); - assert_eq!(request.xdev_mode, rrg_proto::flows::FileFinderArgs_XDev::ALWAYS); - } - - #[test] - fn test_fails_on_non_os_path_type() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_paths().push("abc".to_string()); - proto.mut_paths().push("cba".to_string()); - proto.set_process_non_regular_files(true); - proto.set_follow_links(true); - proto.set_pathtype(rrg_proto::jobs::PathSpec_PathType::REGISTRY); - proto.set_xdev(rrg_proto::flows::FileFinderArgs_XDev::ALWAYS); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let err = Request::from_proto(proto).unwrap_err(); - - match err.kind() { - crate::request::ParseArgsErrorKind::InvalidField("pathtype") => {} - e @ _ => panic!("Unexpected error type: {:?}", e), - } - } - - #[test] - fn test_default_stats_action() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!(request.stat_options.follow_symlink, false); - assert_eq!(request.stat_options.collect_ext_attrs, false); - } - - #[test] - fn test_stats_action() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - proto.mut_action().mut_stat().set_resolve_links(true); - proto.mut_action().mut_stat().set_collect_ext_attrs(true); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!(request.stat_options.follow_symlink, true); - assert_eq!(request.stat_options.collect_ext_attrs, true); - } - - #[test] - fn test_default_hash_action() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::HASH); - - let request = Request::from_proto(proto).unwrap(); - - match request.action.unwrap() { - Action::Hash(options) => { - assert_eq!( - options.oversized_file_policy, - rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy::SKIP - ); - assert_eq!(options.max_size, 500000000); - } - v @ _ => panic!("Unexpected action type: {:?}", v), - } - assert_eq!(request.stat_options.collect_ext_attrs, false); - assert_eq!(request.stat_options.follow_symlink, false); - } - - #[test] - fn test_hash_action() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::HASH); - proto.mut_action().mut_hash().set_collect_ext_attrs(true); - proto.mut_action().mut_hash().set_oversized_file_policy(rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy::HASH_TRUNCATED); - proto.mut_action().mut_hash().set_max_size(123456); - proto.mut_action().mut_stat().set_collect_ext_attrs(true); - proto.mut_action().mut_stat().set_resolve_links(true); - - let request = Request::from_proto(proto).unwrap(); - - match request.action.unwrap() { - Action::Hash(options) => { - assert_eq!( - options.oversized_file_policy, - rrg_proto::flows::FileFinderHashActionOptions_OversizedFilePolicy::HASH_TRUNCATED - ); - assert_eq!(options.max_size, 123456); - } - v @ _ => panic!("Unexpected action type: {:?}", v), - } - assert_eq!(request.stat_options.collect_ext_attrs, true); - assert_eq!(request.stat_options.follow_symlink, true); - } - - #[test] - fn test_default_download_action() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::DOWNLOAD); - - let request = Request::from_proto(proto).unwrap(); - - match request.action.unwrap() { - Action::Download(options) => { - assert_eq!(options.max_size, 500000000); - assert_eq!( - options.oversized_file_policy, - rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy::SKIP - ); - assert_eq!(options.use_external_stores, true); - assert_eq!(options.chunk_size, 524288); - } - v @ _ => panic!("Unexpected action type: {:?}", v), - } - assert_eq!(request.stat_options.collect_ext_attrs, false); - assert_eq!(request.stat_options.follow_symlink, false); - } - - #[test] - fn test_download_action() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::DOWNLOAD); - proto.mut_action().mut_download().set_max_size(12345); - proto.mut_action().mut_download().set_collect_ext_attrs(true); - proto.mut_action().mut_download().set_oversized_file_policy(rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy::DOWNLOAD_TRUNCATED); - proto.mut_action().mut_download().set_use_external_stores(false); - proto.mut_action().mut_download().set_chunk_size(5432); - proto.mut_action().mut_stat().set_collect_ext_attrs(true); - proto.mut_action().mut_stat().set_resolve_links(true); - - let request = Request::from_proto(proto).unwrap(); - - match request.action.unwrap() { - Action::Download(options) => { - assert_eq!(options.max_size, 12345); - assert_eq!( - options.oversized_file_policy, - rrg_proto::flows::FileFinderDownloadActionOptions_OversizedFilePolicy::DOWNLOAD_TRUNCATED - ); - assert_eq!(options.use_external_stores, false); - assert_eq!(options.chunk_size, 5432); - } - v @ _ => panic!("Unexpected action type: {:?}", v), - } - assert_eq!(request.stat_options.collect_ext_attrs, true); - assert_eq!(request.stat_options.follow_symlink, true); - } - - - #[test] - fn test_modification_time_condition() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let condition = proto.mut_conditions().push_default(); - condition.set_condition_type(rrg_proto::flows::FileFinderCondition_Type::MODIFICATION_TIME); - condition.mut_modification_time().set_min_last_modified_time(123); - condition.mut_modification_time().set_max_last_modified_time(234); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!(request.conditions.len(), 1); - match request.conditions.first().unwrap() { - Condition::ModificationTime { min, max } => { - assert_eq!(min.unwrap(), time_from_micros(123)); - assert_eq!(max.unwrap(), time_from_micros(234)); - } - v @ _ => panic!("Unexpected condition type: {:?}", v), - } - } - - #[test] - fn test_access_time_condition() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let condition = proto.mut_conditions().push_default(); - condition.set_condition_type(rrg_proto::flows::FileFinderCondition_Type::ACCESS_TIME); - condition.mut_access_time().set_min_last_access_time(123); - condition.mut_access_time().set_max_last_access_time(234); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!(request.conditions.len(), 1); - match request.conditions.first().unwrap() { - Condition::AccessTime { min, max } => { - assert_eq!(min.unwrap(), time_from_micros(123)); - assert_eq!(max.unwrap(), time_from_micros(234)); - } - v @ _ => panic!("Unexpected condition type: {:?}", v), - } - } - - #[test] - fn test_inode_change_time_condition() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let condition = proto.mut_conditions().push_default(); - condition.set_condition_type(rrg_proto::flows::FileFinderCondition_Type::INODE_CHANGE_TIME); - condition.mut_inode_change_time().set_min_last_inode_change_time(123); - condition.mut_inode_change_time().set_max_last_inode_change_time(234); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!(request.conditions.len(), 1); - match request.conditions.first().unwrap() { - Condition::InodeChangeTime { min, max } => { - assert_eq!(min.unwrap(), time_from_micros(123)); - assert_eq!(max.unwrap(), time_from_micros(234)); - } - v @ _ => panic!("Unexpected condition type: {:?}", v), - } - } - - #[test] - fn test_size_condition() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let condition = proto.mut_conditions().push_default(); - condition.set_condition_type(rrg_proto::flows::FileFinderCondition_Type::SIZE); - condition.mut_size().set_min_file_size(345); - condition.mut_size().set_max_file_size(456); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!(request.conditions.len(), 1); - match request.conditions.first().unwrap() { - Condition::Size { min, max } => { - assert_eq!(min.unwrap(), 345); - assert_eq!(max.unwrap(), 456); - } - v @ _ => panic!("Unexpected condition type: {:?}", v), - } - } - - #[test] - fn test_ext_flags_condition() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let condition = proto.mut_conditions().push_default(); - condition.set_condition_type(rrg_proto::flows::FileFinderCondition_Type::EXT_FLAGS); - condition.mut_ext_flags().set_linux_bits_set(111); - condition.mut_ext_flags().set_linux_bits_unset(222); - condition.mut_ext_flags().set_osx_bits_set(333); - condition.mut_ext_flags().set_osx_bits_unset(444); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!(request.conditions.len(), 1); - match request.conditions.first().unwrap() { - Condition::ExtFlags { - linux_bits_set, - linux_bits_unset, - osx_bits_set, - osx_bits_unset, - } => { - assert_eq!(linux_bits_set.unwrap(), 111); - assert_eq!(linux_bits_unset.unwrap(), 222); - assert_eq!(osx_bits_set.unwrap(), 333); - assert_eq!(osx_bits_unset.unwrap(), 444); - } - v @ _ => panic!("Unexpected condition type: {:?}", v), - } - } - - #[test] - fn test_contents_regex_match_condition() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let condition = proto.mut_conditions().push_default(); - condition.set_condition_type(rrg_proto::flows::FileFinderCondition_Type::CONTENTS_REGEX_MATCH); - condition.mut_contents_regex_match().set_regex(vec![97, 98, 99]); - condition.mut_contents_regex_match().set_mode(rrg_proto::flows::FileFinderContentsRegexMatchCondition_Mode::ALL_HITS); - condition.mut_contents_regex_match().set_bytes_before(4); - condition.mut_contents_regex_match().set_bytes_after(7); - condition.mut_contents_regex_match().set_start_offset(15); - condition.mut_contents_regex_match().set_length(42); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!(request.contents_match_conditions.len(), 1); - - let condition = request.contents_match_conditions.first().unwrap(); - assert_eq!(condition.regex.as_str(), "abc"); - assert_eq!(condition.mode, MatchMode::AllHits); - assert_eq!(condition.bytes_before, 4); - assert_eq!(condition.bytes_after, 7); - assert_eq!(condition.start_offset, 15); - assert_eq!(condition.length, 42); - } - - #[test] - fn test_invalid_utf8_sequence_error() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let condition = proto.mut_conditions().push_default(); - condition.set_condition_type(rrg_proto::flows::FileFinderCondition_Type::CONTENTS_REGEX_MATCH); - condition.mut_contents_regex_match().set_regex(vec![255, 255, 255]); - condition.mut_contents_regex_match().set_mode(rrg_proto::flows::FileFinderContentsRegexMatchCondition_Mode::ALL_HITS); - condition.mut_contents_regex_match().set_bytes_before(4); - condition.mut_contents_regex_match().set_bytes_after(7); - condition.mut_contents_regex_match().set_start_offset(15); - condition.mut_contents_regex_match().set_length(42); - - let err = Request::from_proto(proto).unwrap_err(); - - assert!(matches!(err.kind(), crate::request::ParseArgsErrorKind::InvalidField("regex"))); - } - - #[test] - fn test_contents_literal_match_condition() { - let mut proto = rrg_proto::flows::FileFinderArgs::new(); - proto.mut_action().set_action_type(rrg_proto::flows::FileFinderAction_Action::STAT); - - let condition = proto.mut_conditions().push_default(); - condition.set_condition_type(rrg_proto::flows::FileFinderCondition_Type::CONTENTS_LITERAL_MATCH); - condition.mut_contents_literal_match().set_literal(vec![99, 98, 97]); - condition.mut_contents_literal_match().set_mode(rrg_proto::flows::FileFinderContentsLiteralMatchCondition_Mode::ALL_HITS); - condition.mut_contents_literal_match().set_start_offset(6); - condition.mut_contents_literal_match().set_length(8); - condition.mut_contents_literal_match().set_bytes_before(15); - condition.mut_contents_literal_match().set_bytes_after(18); - - let request = Request::from_proto(proto).unwrap(); - - assert_eq!(request.contents_match_conditions.len(), 1); - let condition = request.contents_match_conditions.first().unwrap(); - assert_eq!(condition.regex.as_str(), r"\x63\x62\x61"); - assert_eq!(condition.mode, MatchMode::AllHits); - assert_eq!(condition.start_offset, 6); - assert_eq!(condition.length, 8); - assert_eq!(condition.bytes_before, 15); - assert_eq!(condition.bytes_after, 18); - } -} diff --git a/crates/rrg/src/action/deprecated/insttime.rs b/crates/rrg/src/action/deprecated/insttime.rs deleted file mode 100644 index 1017387e..00000000 --- a/crates/rrg/src/action/deprecated/insttime.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020 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 handler and associated types for the install date action. -//! -//! The install date action uses various heuristics to detect the operating -//! system install date. - -use std::time::{SystemTime, Duration}; - -use log::error; -use crate::session::{self, Session}; - -/// A response type for the install date action. -struct Response { - /// Install date of the operating system, or `None` if the attemps to - /// obtain install date failed. - time: Option, -} - -impl crate::response::Item for Response { - - type Proto = protobuf::well_known_types::UInt64Value; - - fn into_proto(self) -> Self::Proto { - let time = match self.time { - Some(time) => time, - None => { - error!("cannot get install date, all methods failed"); - std::time::UNIX_EPOCH - }, - }; - let since_epoch = match time.duration_since(std::time::UNIX_EPOCH) { - Ok(duration) => duration, - Err(err) => { - error!( - "install date is {} seconds earlier than Unix epoch", - err.duration().as_secs() - ); - Duration::from_secs(0) - }, - }; - - let mut proto = protobuf::well_known_types::UInt64Value::new(); - proto.set_value(since_epoch.as_secs()); - - proto - } -} - -/// Handles requests for the install date action. -pub fn handle(session: &mut S, _: ()) -> session::Result<()> { - // TODO(@panhania): Original code from [`#36]` by alex65536@ had many good - // heuristics that we should backport to `ospect::os::installed`. - // - // [`#36`]: https://github.com/google/rrg/pull/36 - session.reply(Response { - time: ospect::os::installed().ok(), - })?; - Ok(()) -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[test] - fn test_install_date() { - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, ()).is_ok()); - assert_eq!(session.reply_count(), 1); - let response: &Response = session.reply(0); - let time = response.time.unwrap(); - - assert!(time <= SystemTime::now()); - } -} diff --git a/crates/rrg/src/action/deprecated/interfaces.rs b/crates/rrg/src/action/deprecated/interfaces.rs deleted file mode 100644 index 3073c683..00000000 --- a/crates/rrg/src/action/deprecated/interfaces.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2020 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 handler and associated types for the network interfaces action. -//! -//! The interfaces action lists all network interfaces available on the machine, -//! collecting their names, MAC and IP addresses. - -use rrg_macro::warn; - -/// An item type for the network interfaces action. -#[derive(Debug)] -pub struct Item { - /// Actual information about a network interface. - iface: ospect::net::Interface, -} - -/// Handles requests for the interfaces action. -pub fn handle(session: &mut S, _: ()) -> crate::session::Result<()> -where - S: crate::session::Session, -{ - let ifaces = ospect::net::interfaces() - .map_err(crate::session::Error::action)?; - - for iface in ifaces { - session.reply(Item { - iface, - })?; - } - - Ok(()) -} - -impl crate::response::Item for Item { - - type Proto = rrg_proto::jobs::Interface; - - fn into_proto(self) -> rrg_proto::jobs::Interface { - let mut proto = rrg_proto::jobs::Interface::new(); - - let name = self.iface.name().to_string_lossy(); - if let std::borrow::Cow::Owned(_) = &name { - warn!("network interface name with invalid bytes: {:?}", name); - } - proto.set_ifname(name.to_string()); - - if let Some(mac_addr) = self.iface.mac_addr() { - proto.set_mac_address(mac_addr.octets().into()); - } else { - warn!("network interface '{}' without MAC address", name); - } - - for ip_addr in self.iface.ip_addrs() { - use rrg_proto::jobs::NetworkAddress_Family::{INET, INET6}; - - let mut ip_addr_proto = rrg_proto::jobs::NetworkAddress::new(); - match ip_addr { - std::net::IpAddr::V4(ip_addr) => { - ip_addr_proto.set_address_type(INET); - ip_addr_proto.set_packed_bytes(ip_addr.octets().into()); - }, - std::net::IpAddr::V6(ip_addr) => { - ip_addr_proto.set_address_type(INET6); - ip_addr_proto.set_packed_bytes(ip_addr.octets().into()); - }, - }; - proto.mut_addresses().push(ip_addr_proto); - } - - proto - } -} diff --git a/crates/rrg/src/action/deprecated/listdir.rs b/crates/rrg/src/action/deprecated/listdir.rs deleted file mode 100644 index 3a275ae3..00000000 --- a/crates/rrg/src/action/deprecated/listdir.rs +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2020 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 handler and associated types for the list directory action. -//! -//! A list directory action collects basic metadata for all files in the -//! provided directory. -//! -//! Note that this includes only entries directly under the specified directory. -//! For more sophisticated recursive traversals, please look into the [timeline] -//! action. -//! -//! [timeline]: ../timeline/index.html - -use crate::session::{self, Session}; - -use std::fs::{Metadata}; -use std::path::{PathBuf}; - -/// A request type for the list directory action. -pub struct Request { - /// A path to the directory ought to be listed. - path: PathBuf, -} - -/// A response type for the list directory action. -pub struct Response { - /// A full path to a particular file within the listed directory. - path: PathBuf, - /// Metadata about a particular file within the listed directory. - metadata: Metadata, -} - -/// An error type for failures that can occur during the list directory action. -#[derive(Debug)] -enum Error { - /// A failure occurred during the attempt to list a directory. - ListDir(std::io::Error), -} - -impl std::error::Error for Error { - - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use Error::*; - - match *self { - ListDir(ref error) => Some(error), - } - } -} - -impl std::fmt::Display for Error { - - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - use Error::*; - - match *self { - ListDir(ref error) => { - write!(fmt, "unable to list directory: {}", error) - } - } - } -} - -impl From for session::Error { - - fn from(error: Error) -> session::Error { - session::Error::action(error) - } -} - -/// Handles requests for the list directory action. -pub fn handle(session: &mut S, request: Request) -> session::Result<()> -where - S: Session, -{ - let entries = crate::fs::list_dir(&request.path) - .map_err(Error::ListDir)?; - - for entry in entries { - let entry = match entry { - Ok(entry) => entry, - Err(error) => { - log::warn!("failed to obtain directory entry: {error}"); - continue; - }, - }; - - session.reply(Response { - path: entry.path, - metadata: entry.metadata, - })?; - } - - Ok(()) -} - -impl crate::request::Args for Request { - - type Proto = rrg_proto::jobs::ListDirRequest; - - fn from_proto(mut proto: Self::Proto) -> Result { - let path = proto.take_pathspec().try_into() - .map_err(|error| { - crate::request::ParseArgsError::invalid_field("pathspec", error) - })?; - - Ok(Request { - path: path, - }) - } -} - -impl crate::response::Item for Response { - - type Proto = rrg_proto::jobs::StatEntry; - - fn into_proto(self) -> Self::Proto { - use rrg_proto::convert::FromLossy as _; - - let mut proto = rrg_proto::jobs::StatEntry::from_lossy(self.metadata); - proto.set_pathspec(self.path.into()); - - proto - } -} - -#[cfg(test)] -mod tests { - - use std::fs::File; - - use super::*; - - #[test] - fn test_non_existent_dir() { - let tempdir = tempfile::tempdir().unwrap(); - - let request = Request { - path: tempdir.path().join("foo").to_path_buf(), - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_err()); - } - - #[test] - fn test_empty_dir() { - let tempdir = tempfile::tempdir().unwrap(); - - let request = Request { - path: tempdir.path().to_path_buf(), - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 0); - } - - #[test] - fn test_dir_with_files() { - let tempdir = tempfile::tempdir().unwrap(); - File::create(tempdir.path().join("abc")).unwrap(); - File::create(tempdir.path().join("def")).unwrap(); - File::create(tempdir.path().join("ghi")).unwrap(); - - let request = Request { - path: tempdir.path().to_path_buf(), - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - let mut replies = session.replies().collect::>(); - replies.sort_by_key(|reply| reply.path.clone()); - - assert_eq!(replies.len(), 3); - - assert_eq!(replies[0].path, tempdir.path().join("abc")); - assert!(replies[0].metadata.is_file()); - - assert_eq!(replies[1].path, tempdir.path().join("def")); - assert!(replies[1].metadata.is_file()); - - assert_eq!(replies[2].path, tempdir.path().join("ghi")); - assert!(replies[2].metadata.is_file()); - } - - #[test] - fn test_dir_with_dirs() { - let tempdir = tempfile::tempdir().unwrap(); - std::fs::create_dir(tempdir.path().join("abc")).unwrap(); - std::fs::create_dir(tempdir.path().join("def")).unwrap(); - - let request = Request { - path: tempdir.path().to_path_buf(), - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - let mut replies = session.replies().collect::>(); - replies.sort_by_key(|reply| reply.path.clone()); - - assert_eq!(replies.len(), 2); - - assert_eq!(replies[0].path, tempdir.path().join("abc")); - assert!(replies[0].metadata.is_dir()); - - assert_eq!(replies[1].path, tempdir.path().join("def")); - assert!(replies[1].metadata.is_dir()); - } - - #[test] - fn test_dir_with_nested_dirs() { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().join("abc").join("def").join("ghi"); - - std::fs::create_dir_all(path).unwrap(); - - let request = Request { - path: tempdir.path().to_path_buf(), - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - } - - // Symlinking is supported only on Unix-like systems. - #[cfg(target_family = "unix")] - #[test] - fn test_list_dir_with_links() { - let tempdir = tempfile::tempdir().unwrap(); - let source = tempdir.path().join("abc"); - let target = tempdir.path().join("def"); - - File::create(&source).unwrap(); - std::os::unix::fs::symlink(&source, &target).unwrap(); - - let request = Request { - path: tempdir.path().to_path_buf(), - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - let mut replies = session.replies().collect::>(); - replies.sort_by_key(|reply| reply.path.clone()); - - assert_eq!(replies.len(), 2); - - assert_eq!(replies[0].path, tempdir.path().join("abc")); - assert!(replies[0].metadata.file_type().is_file()); - - assert_eq!(replies[1].path, tempdir.path().join("def")); - assert!(replies[1].metadata.file_type().is_symlink()); - } - - // macOS mangles Unicode-specific characters in filenames. - #[cfg_attr(target_os = "macos", ignore)] - #[test] - fn test_dir_with_unicode_files() { - let tempdir = tempfile::tempdir().unwrap(); - File::create(tempdir.path().join("zażółć gęślą jaźń")).unwrap(); - File::create(tempdir.path().join("што й па мору")).unwrap(); - - let request = Request { - path: tempdir.path().to_path_buf(), - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - let mut replies = session.replies().collect::>(); - replies.sort_by_key(|reply| reply.path.clone()); - - assert_eq!(replies.len(), 2); - assert_eq!(replies[0].path, tempdir.path().join("zażółć gęślą jaźń")); - assert_eq!(replies[1].path, tempdir.path().join("што й па мору")); - } -} diff --git a/crates/rrg/src/action/deprecated/network.rs b/crates/rrg/src/action/deprecated/network.rs deleted file mode 100644 index 3f16ce61..00000000 --- a/crates/rrg/src/action/deprecated/network.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2020 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 handler and associated types for the network connection action. -//! -//! The network connection action lists all the opened connections on the -//! client and the information about them (e.g. type of the connection, source -//! and target IP addresses, the process that owns the connection). - -use crate::session::{self, Session}; - -/// Arguments for the network connections action. -#[derive(Debug)] -pub struct Args { - /// Whether to return only connections in the listening state. - listening_only: bool, -} - -/// Result yielded by the network connections action. -#[derive(Debug)] -pub struct Item { - /// Metadata about the network connection. - conn: ospect::net::Connection, -} - -/// An error type for situations when the network connection action fails. -#[derive(Debug)] -struct Error(std::io::Error); - -impl std::error::Error for Error { - - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(&self.0) - } -} - -impl std::fmt::Display for Error { - - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "failed to list network connections: {}", self.0) - } -} - -impl From for session::Error { - - fn from(error: Error) -> session::Error { - session::Error::action(error) - } -} - -/// Handles execution of the network connections action. -pub fn handle(session: &mut S, args: Args) -> session::Result<()> -where - S: Session, -{ - use rrg_macro::warn; - - let pids = ospect::proc::ids() - .map_err(Error)?; - - for pid in pids { - let pid = match pid { - Ok(pid) => pid, - Err(error) => { - warn!("failed to get process identifier: {}", error); - continue; - } - }; - - let conns = match ospect::net::connections(pid) { - Ok(conns) => conns, - Err(error) => { - warn! { - "failed to list network connections for process {}: {}", - pid, error, - }; - continue; - } - }; - - for conn in conns { - let conn = match conn { - Ok(conn) => conn, - Err(error) => { - warn! { - "failed to get connection metadata for process {}: {}", - pid, error, - }; - continue; - } - }; - - if args.listening_only { - match conn { - ospect::net::Connection::Tcp(conn) if conn.state() == ospect::net::TcpState::Listen => (), - _ => continue, - } - } - - session.reply(Item { conn })?; - } - } - - Ok(()) -} - -impl crate::request::Args for Args { - - type Proto = rrg_proto::flows::ListNetworkConnectionsArgs; - - fn from_proto(proto: Self::Proto) -> Result { - Ok(Args { - listening_only: proto.get_listening_only(), - }) - } -} - -impl crate::response::Item for Item { - - type Proto = rrg_proto::sysinfo::NetworkConnection; - - fn into_proto(self) -> Self::Proto { - let mut result = rrg_proto::sysinfo::NetworkConnection::default(); - result.set_pid(self.conn.pid()); - - match self.conn { - ospect::net::Connection::Tcp(conn) => { - result.set_field_type(rrg_proto::sysinfo::NetworkConnection_Type::SOCK_STREAM); - result.set_local_address(addr_to_proto(conn.local_addr())); - result.set_remote_address(addr_to_proto(conn.remote_addr())); - result.set_state(state_to_proto(conn.state())); - } - ospect::net::Connection::Udp(conn) => { - result.set_field_type(rrg_proto::sysinfo::NetworkConnection_Type::SOCK_DGRAM); - result.set_local_address(addr_to_proto(conn.local_addr())); - } - } - - result - } -} - -// TOOD(@panhania): Migrate to `std::convert` implementation. -fn addr_to_proto(addr: std::net::SocketAddr) -> rrg_proto::sysinfo::NetworkEndpoint { - let mut result = rrg_proto::sysinfo::NetworkEndpoint::default(); - result.set_ip(addr.ip().to_string()); - result.set_port(addr.port().into()); - result -} - -// TOOD(@panhania): Migrate to `std::convert` implementation. -fn state_to_proto(state: ospect::net::TcpState) -> rrg_proto::sysinfo::NetworkConnection_State { - use ospect::net::TcpState::*; - use rrg_proto::sysinfo::NetworkConnection_State::*; - - match state { - Listen => LISTEN, - SynSent => SYN_SENT, - SynReceived => SYN_RECV, - Established => ESTABLISHED, - FinWait1 => FIN_WAIT1, - FinWait2 => FIN_WAIT2, - CloseWait => CLOSE_WAIT, - Closing => CLOSING, - LastAck => LAST_ACK, - TimeWait => TIME_WAIT, - Closed => CLOSED, - } -} diff --git a/crates/rrg/src/action/deprecated/stat.rs b/crates/rrg/src/action/deprecated/stat.rs deleted file mode 100644 index 9888a99b..00000000 --- a/crates/rrg/src/action/deprecated/stat.rs +++ /dev/null @@ -1,630 +0,0 @@ -// Copyright 2020 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 handler and associated types for the file stat action. -//! -//! A file stat action collects filesystem metadata associated with a particular -//! file. -//! -//! Note that the gathered bits of information differ across platforms, e.g. on -//! Linux there is a notion of symlinks whereas on Windows no such thing exists. -//! Therefore, on Linux the results might include additional information about -//! the symlink (like the file it points to). - -use std::fs::Metadata; -use std::path::PathBuf; - -use rrg_macro::ack; - -use crate::session::{self, Session}; - -/// A request type for the stat action. -#[derive(Debug)] -pub struct Request { - /// A path to the file to stat. - path: PathBuf, - /// Whether to collect extended file attributes. - collect_ext_attrs: bool, - /// Whether, in case of a symlink, to collect data about the linked file. - follow_symlink: bool, -} - -impl Request { - - /// Obtains a (potentially expanded) path that this request corresponds to. - /// - /// In case of requests that wish to follow symlinks, it will return a path - /// to the symlink target (in case there is such). Otherwise, it will just - /// return the requested path unchanged. - /// - /// # Errors - /// - /// This method will return an error if the path needs to be expanded but - /// the expansion fails for some reason (e.g. the requested path does not - /// exist). - fn target(&self) -> std::io::Result> { - use std::borrow::Cow::*; - - if self.follow_symlink { - self.path.canonicalize().map(Owned) - } else { - Ok(Borrowed(&self.path)) - } - } -} - -/// A response type for the stat action. -#[derive(Debug)] -pub struct Response { - /// A path to the file that the result corresponds to. - path: PathBuf, - /// Metadata about the file. - metadata: Metadata, - /// A path to the pointed file (in case of a symlink). - symlink: Option, - /// Extended attributes of the file. - ext_attrs: Vec, - /// Additional Linux-specific file flags. - #[cfg(target_os = "linux")] - flags_linux: Option, - // TODO: Add support for collecting file flags on macOS. -} - -/// An error type for failures that can occur during the stat action. -#[derive(Debug)] -enum Error { - /// A failure occurred during the attempt to collect file metadata. - Metadata(std::io::Error), -} - -impl std::error::Error for Error { - - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use Error::*; - - match *self { - Metadata(ref error) => Some(error), - } - } -} - -impl std::fmt::Display for Error { - - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - use Error::*; - - match *self { - Metadata(ref error) => { - write!(fmt, "unable to collect metadata: {}", error) - } - } - } -} - -impl From for session::Error { - - fn from(error: Error) -> session::Error { - session::Error::action(error) - } -} - -/// Handles requests for the file stat action. -pub fn handle(session: &mut S, request: Request) -> session::Result<()> -where - S: Session, -{ - let metadata = if request.follow_symlink { - std::fs::metadata(&request.path) - } else { - std::fs::symlink_metadata(&request.path) - }.map_err(Error::Metadata)?; - - let symlink = if metadata.file_type().is_symlink() { - ack! { - std::fs::read_link(&request.path), - warn: "failed to read symlink for '{}'", request.path.display() - } - } else { - None - }; - - let ext_attrs = if request.collect_ext_attrs { - ext_attrs(&request) - } else { - vec!() - }; - - #[cfg(target_os = "linux")] - let flags_linux = if !metadata.file_type().is_symlink() { - ack! { - ospect::fs::linux::flags(&request.path), - warn: "failed to collect flags for '{}'", request.path.display() - } - } else { - // Flags are available only for non-symlinks. For symlinks, the function - // would return flags mask for the target file, which can look confusing - // in the results. - None - }; - - let response = Response { - path: request.path, - metadata: metadata, - symlink: symlink, - ext_attrs: ext_attrs, - #[cfg(target_os = "linux")] - flags_linux: flags_linux, - }; - - session.reply(response)?; - - Ok(()) -} - -impl crate::request::Args for Request { - - type Proto = rrg_proto::jobs::GetFileStatRequest; - - fn from_proto(mut proto: Self::Proto) -> Result { - let path = proto.take_pathspec().try_into() - .map_err(|error| { - crate::request::ParseArgsError::invalid_field("pathspec", error) - })?; - - Ok(Request { - path: path, - follow_symlink: proto.get_follow_symlink(), - collect_ext_attrs: proto.get_collect_ext_attrs(), - }) - } -} - -impl crate::response::Item for Response { - - type Proto = rrg_proto::jobs::StatEntry; - - fn into_proto(self) -> Self::Proto { - use rrg_proto::convert::FromLossy as _; - - let mut proto = rrg_proto::jobs::StatEntry::from_lossy(self.metadata); - proto.set_pathspec(self.path.into()); - - if let Some(symlink) = self.symlink { - proto.set_symlink(symlink.to_string_lossy().into_owned()); - } - - proto.set_ext_attrs(self.ext_attrs.into_iter().map(Into::into).collect()); - - #[cfg(target_os = "linux")] - if let Some(flags_linux) = self.flags_linux { - proto.set_st_flags_linux(flags_linux); - } - - proto - } -} - -/// Collects extended attributes of a file specified by the request. -fn ext_attrs(request: &Request) -> Vec { - let path = match request.target() { - Ok(path) => path, - Err(error) => { - rrg_macro::warn! { - "failed to expand '{path}': {cause}", - path = request.path.display(), - cause = error - }; - return vec!(); - } - }; - - let ext_attrs = match ospect::fs::ext_attrs(&path) { - Ok(ext_attrs) => ext_attrs, - Err(error) => { - rrg_macro::warn! { - "failed to collect extended attributes for '{path}': {cause}", - path = request.path.display(), - cause = error - }; - return vec!(); - } - }; - - ext_attrs.filter_map(|ext_attr| match ext_attr { - Ok(ext_attr) => Some(ext_attr), - Err(error) => { - rrg_macro::warn! { - "failed to collect an extended attribute for '{path}': {cause}", - path = request.path.display(), - cause = error - }; - - None - } - }).collect() -} - -#[cfg(test)] -mod tests { - - use std::fs::File; - - use super::*; - - #[test] - fn test_handle_with_non_existent_file() { - let tempdir = tempfile::tempdir().unwrap(); - - let request = Request { - path: tempdir.path().join("foo").to_path_buf(), - follow_symlink: false, - collect_ext_attrs: false, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_err()); - } - - #[test] - fn test_handle_with_regular_file() { - let tempdir = tempfile::tempdir().unwrap(); - File::create(tempdir.path().join("foo")).unwrap(); - - let request = Request { - path: tempdir.path().join("foo").to_path_buf(), - follow_symlink: false, - collect_ext_attrs: false, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - assert_eq!(reply.path, tempdir.path().join("foo")); - assert!(reply.metadata.is_file()); - } - - // Symlinking is supported only on Unix-like systems. - #[cfg(target_family = "unix")] - #[test] - fn test_handle_with_link() { - let tempdir = tempfile::tempdir().unwrap(); - let symlink = tempdir.path().join("foo"); - let target = tempdir.path().join("bar"); - - File::create(&target).unwrap(); - std::os::unix::fs::symlink(&target, &symlink).unwrap(); - - let request = Request { - path: symlink.clone(), - follow_symlink: false, - collect_ext_attrs: false, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - assert_eq!(reply.path, symlink); - assert_eq!(reply.symlink, Some(target)); - assert!(reply.metadata.file_type().is_symlink()); - } - - // Symlinking is supported only on Unix-like systems. - #[cfg(target_family = "unix")] - #[test] - fn test_handle_with_two_links() { - use std::os::unix::fs::symlink; - - let tempdir = tempfile::tempdir().unwrap(); - let symlink_to_symlink = tempdir.path().join("foo"); - let symlink_to_target = tempdir.path().join("bar"); - let target = tempdir.path().join("baz"); - - File::create(&target).unwrap(); - symlink(&target, &symlink_to_target).unwrap(); - symlink(&symlink_to_target, &symlink_to_symlink).unwrap(); - - let request = Request { - path: symlink_to_symlink.clone(), - follow_symlink: false, - collect_ext_attrs: false, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - assert_eq!(reply.path, symlink_to_symlink); - assert_eq!(reply.symlink, Some(symlink_to_target)); - assert!(reply.metadata.file_type().is_symlink()); - } - - // Symlinking is supported only on Unix-like systems. - #[cfg(target_family = "unix")] - #[test] - fn test_handle_with_link_and_follow_symlink() { - let tempdir = tempfile::tempdir().unwrap(); - let symlink = tempdir.path().join("foo"); - let target = tempdir.path().join("bar"); - - File::create(&target).unwrap(); - std::os::unix::fs::symlink(&target, &symlink).unwrap(); - - let request = Request { - path: symlink.clone(), - follow_symlink: true, - collect_ext_attrs: false, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - assert_eq!(reply.path, symlink); - assert_eq!(reply.symlink, None); - assert!(reply.metadata.is_file()); - } - - // Symlinking is supported only on Unix-like systems. - #[cfg(target_family = "unix")] - #[test] - fn test_handle_with_two_links_and_follow_symlink() { - use std::os::unix::fs::symlink; - - let tempdir = tempfile::tempdir().unwrap(); - let symlink_to_symlink = tempdir.path().join("foo"); - let symlink_to_target = tempdir.path().join("bar"); - let target = tempdir.path().join("baz"); - - File::create(&target).unwrap(); - symlink(&target, &symlink_to_target).unwrap(); - symlink(&symlink_to_target, &symlink_to_symlink).unwrap(); - - let request = Request { - path: symlink_to_symlink.clone(), - follow_symlink: true, - collect_ext_attrs: false, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - let reply = session.reply::(0); - assert_eq!(reply.path, symlink_to_symlink); - assert_eq!(reply.symlink, None); - assert!(reply.metadata.is_file()); - } - - #[cfg(all(target_os = "linux", feature = "test-setfattr"))] - #[test] - fn test_handle_with_file_ext_attrs_on_linux() { - let tempdir = tempfile::tempdir().unwrap(); - let tempfile = tempdir.path().join("foo"); - std::fs::File::create(&tempfile).unwrap(); - - assert! { - std::process::Command::new("setfattr") - .arg("--name").arg("user.norf") - .arg("--value").arg("quux") - .arg(&tempfile) - .status() - .unwrap() - .success() - }; - - let request = Request { - path: tempfile.clone(), - follow_symlink: false, - collect_ext_attrs: true, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - assert_eq!(reply.ext_attrs.len(), 1); - assert_eq!(reply.ext_attrs[0].name, "user.norf"); - assert_eq!(reply.ext_attrs[0].value, b"quux"); - } - - #[cfg(all(target_os = "linux", feature = "test-setfattr"))] - #[test] - fn test_handle_with_symlink_ext_attrs_on_linux() { - let tempdir = tempfile::tempdir().unwrap(); - let symlink = tempdir.path().join("foo"); - let target = tempdir.path().join("bar"); - - std::fs::File::create(&target).unwrap(); - std::os::unix::fs::symlink(&target, &symlink).unwrap(); - - // Turns out, the kernel disallows setting extended attributes on a - // symlink [1]. However, the kernel itself can hypothetically set such - // bits. - // - // In order to verify that we really collect attributes for the symlink - // and no for the target, we set some attributes for the target and then - // we collect attributes of the symlink. Then, the expected result is to - // have a reply with no extended attributes. - // - // [1]: https://man7.org/linux/man-pages/man7/xattr.7.html - - assert! { - std::process::Command::new("setfattr") - .arg("--name").arg("user.norf") - .arg("--value").arg("quux") - .arg("--no-dereference") - .arg(&target) - .status() - .unwrap() - .success() - }; - - let request = Request { - path: symlink, - follow_symlink: false, - collect_ext_attrs: true, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - assert!(reply.ext_attrs.is_empty()); - } - - #[cfg(all(target_os = "linux", feature = "test-setfattr"))] - #[test] - fn test_handle_with_symlink_ext_attrs_and_follow_symlink_on_linux() { - let tempdir = tempfile::tempdir().unwrap(); - let symlink = tempdir.path().join("foo"); - let target = tempdir.path().join("bar"); - - std::fs::File::create(&target).unwrap(); - std::os::unix::fs::symlink(&target, &symlink).unwrap(); - - assert! { - std::process::Command::new("setfattr") - .arg("--name").arg("user.norf") - .arg("--value").arg("quux") - .arg(&target) - .status() - .unwrap() - .success() - }; - - let request = Request { - path: symlink.clone(), - follow_symlink: true, - collect_ext_attrs: true, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - assert_eq!(reply.ext_attrs.len(), 1); - assert_eq!(reply.ext_attrs[0].name, "user.norf"); - assert_eq!(reply.ext_attrs[0].value, b"quux"); - } - - #[cfg(all(target_os = "linux", feature = "test-chattr"))] - #[test] - fn test_handle_with_file_flags_on_linux() { - // https://elixir.bootlin.com/linux/v5.8.14/source/include/uapi/linux/fs.h#L245 - const FS_NOATIME_FL: std::os::raw::c_long = 0x00000080; - - let tempdir = tempfile::tempdir().unwrap(); - let tempfile = tempdir.path().join("foo"); - std::fs::File::create(&tempfile).unwrap(); - - assert! { - std::process::Command::new("chattr") - .arg("+A").arg(&tempfile) - .status() - .unwrap() - .success() - }; - - let request = Request { - path: tempfile, - follow_symlink: false, - collect_ext_attrs: false, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - let flags = reply.flags_linux.unwrap(); - assert_eq!(flags & FS_NOATIME_FL as u32, FS_NOATIME_FL as u32); - } - - #[cfg(all(target_os = "linux", feature = "test-chattr"))] - #[test] - fn test_handle_with_symlink_flags_on_linux() { - let tempdir = tempfile::tempdir().unwrap(); - let symlink = tempdir.path().join("foo"); - let target = tempdir.path().join("bar"); - - std::fs::File::create(&target).unwrap(); - std::os::unix::fs::symlink(&target, &symlink).unwrap(); - - assert! { - std::process::Command::new("chattr") - .arg("+d").arg(&target) - .status() - .unwrap() - .success() - }; - - let request = Request { - path: symlink, - follow_symlink: false, - collect_ext_attrs: false, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - assert_eq!(reply.flags_linux, None); - } - - #[cfg(all(target_os = "linux", feature = "test-chattr"))] - #[test] - fn test_handle_with_symlink_flags_and_follow_symlink_on_linux() { - // https://elixir.bootlin.com/linux/v5.8.14/source/include/uapi/linux/fs.h#L245 - const FS_NODUMP_FL: std::os::raw::c_long = 0x00000040; - - let tempdir = tempfile::tempdir().unwrap(); - let symlink = tempdir.path().join("foo"); - let target = tempdir.path().join("bar"); - - std::fs::File::create(&target).unwrap(); - std::os::unix::fs::symlink(&target, &symlink).unwrap(); - - assert! { - std::process::Command::new("chattr") - .arg("+d").arg(&target) - .status() - .unwrap() - .success() - }; - - let request = Request { - path: symlink, - follow_symlink: true, - collect_ext_attrs: false, - }; - - let mut session = session::FakeSession::new(); - assert!(handle(&mut session, request).is_ok()); - - assert_eq!(session.reply_count(), 1); - - let reply = session.reply::(0); - let flags = reply.flags_linux.unwrap(); - assert_eq!(flags & FS_NODUMP_FL as u32, FS_NODUMP_FL as u32); - } -} diff --git a/crates/rrg/src/action/get_file_contents.rs b/crates/rrg/src/action/get_file_contents.rs index da0a4be9..c525c459 100644 --- a/crates/rrg/src/action/get_file_contents.rs +++ b/crates/rrg/src/action/get_file_contents.rs @@ -75,7 +75,7 @@ where impl crate::request::Args for Args { - type Proto = rrg_proto::v2::get_file_contents::Args; + type Proto = rrg_proto::get_file_contents::Args; fn from_proto(mut proto: Self::Proto) -> Result { use crate::request::ParseArgsError; @@ -103,7 +103,7 @@ impl crate::request::Args for Args { impl crate::response::Item for Item { - type Proto = rrg_proto::v2::get_file_contents::Result; + type Proto = rrg_proto::get_file_contents::Result; fn into_proto(self) -> Self::Proto { let mut proto = Self::Proto::default(); diff --git a/crates/rrg/src/action/get_file_metadata.rs b/crates/rrg/src/action/get_file_metadata.rs index 0616b863..9a504909 100644 --- a/crates/rrg/src/action/get_file_metadata.rs +++ b/crates/rrg/src/action/get_file_metadata.rs @@ -85,7 +85,7 @@ where impl crate::request::Args for Args { - type Proto = rrg_proto::v2::get_file_metadata::Args; + type Proto = rrg_proto::get_file_metadata::Args; fn from_proto(mut proto: Self::Proto) -> Result { use crate::request::ParseArgsError; @@ -101,10 +101,10 @@ impl crate::request::Args for Args { impl crate::response::Item for Item { - type Proto = rrg_proto::v2::get_file_metadata::Result; + type Proto = rrg_proto::get_file_metadata::Result; fn into_proto(self) -> Self::Proto { - let mut proto = rrg_proto::v2::get_file_metadata::Result::default(); + let mut proto = rrg_proto::get_file_metadata::Result::default(); proto.set_path(self.path.into()); proto.set_metadata(self.metadata.into()); diff --git a/crates/rrg/src/action/get_filesystem_timeline.rs b/crates/rrg/src/action/get_filesystem_timeline.rs index 2537d245..8248a6d4 100644 --- a/crates/rrg/src/action/get_filesystem_timeline.rs +++ b/crates/rrg/src/action/get_filesystem_timeline.rs @@ -52,7 +52,7 @@ where .inspect(|_| { entry_count.set(entry_count.get() + 1); }) - .map(rrg_proto::v2::get_filesystem_timeline::Entry::from_lossy); + .map(rrg_proto::get_filesystem_timeline::Entry::from_lossy); for batch in crate::gzchunked::encode(entries) { let batch = batch @@ -75,7 +75,7 @@ where impl crate::request::Args for Args { - type Proto = rrg_proto::v2::get_filesystem_timeline::Args; + type Proto = rrg_proto::get_filesystem_timeline::Args; fn from_proto(mut proto: Self::Proto) -> Result { use crate::request::ParseArgsError; @@ -91,7 +91,7 @@ impl crate::request::Args for Args { impl crate::response::Item for Item { - type Proto = rrg_proto::v2::get_filesystem_timeline::Result; + type Proto = rrg_proto::get_filesystem_timeline::Result; fn into_proto(self) -> Self::Proto { let mut proto = Self::Proto::default(); @@ -102,7 +102,7 @@ impl crate::response::Item for Item { } } -impl FromLossy for rrg_proto::v2::get_filesystem_timeline::Entry { +impl FromLossy for rrg_proto::get_filesystem_timeline::Entry { fn from_lossy(entry: crate::fs::Entry) -> Self { let mut proto = Self::default(); @@ -404,7 +404,7 @@ mod tests { /// Retrieves timeline entries from the given session object. fn entries( session: &crate::session::FakeSession, - ) -> Vec { + ) -> Vec { let blob_count = session.parcel_count(crate::Sink::Blob); let reply_count = session.reply_count(); assert_eq!(blob_count, reply_count); @@ -427,7 +427,7 @@ mod tests { /// Constructs a path for the given timeline entry. fn path( - entry: &rrg_proto::v2::get_filesystem_timeline::Entry, + entry: &rrg_proto::get_filesystem_timeline::Entry, ) -> Option { rrg_proto::path::from_bytes(entry.get_path().to_owned()).ok() } diff --git a/crates/rrg/src/action/get_system_metadata.rs b/crates/rrg/src/action/get_system_metadata.rs index a6e38973..4b7abe1e 100644 --- a/crates/rrg/src/action/get_system_metadata.rs +++ b/crates/rrg/src/action/get_system_metadata.rs @@ -36,12 +36,12 @@ impl Item { impl crate::response::Item for Item { - type Proto = rrg_proto::v2::get_system_metadata::Result; + type Proto = rrg_proto::get_system_metadata::Result; - fn into_proto(self) -> rrg_proto::v2::get_system_metadata::Result { + fn into_proto(self) -> rrg_proto::get_system_metadata::Result { use rrg_proto::into_timestamp; - let mut proto = rrg_proto::v2::get_system_metadata::Result::new(); + let mut proto = rrg_proto::get_system_metadata::Result::new(); proto.set_field_type(self.kind.into()); proto.set_version(self.version); proto.set_arch(self.arch); diff --git a/crates/rrg/src/action/list_connections.rs b/crates/rrg/src/action/list_connections.rs index 9be7ad81..dc63d43a 100644 --- a/crates/rrg/src/action/list_connections.rs +++ b/crates/rrg/src/action/list_connections.rs @@ -38,10 +38,10 @@ where impl crate::response::Item for Item { - type Proto = rrg_proto::v2::list_connections::Result; + type Proto = rrg_proto::list_connections::Result; - fn into_proto(self) -> rrg_proto::v2::list_connections::Result { - let mut proto = rrg_proto::v2::list_connections::Result::new(); + fn into_proto(self) -> rrg_proto::list_connections::Result { + let mut proto = rrg_proto::list_connections::Result::new(); proto.set_connection(self.conn.into()); proto diff --git a/crates/rrg/src/action/list_interfaces.rs b/crates/rrg/src/action/list_interfaces.rs index 6f108fa7..789bf789 100644 --- a/crates/rrg/src/action/list_interfaces.rs +++ b/crates/rrg/src/action/list_interfaces.rs @@ -28,10 +28,10 @@ where impl crate::response::Item for Item { - type Proto = rrg_proto::v2::list_interfaces::Result; + type Proto = rrg_proto::list_interfaces::Result; - fn into_proto(self) -> rrg_proto::v2::list_interfaces::Result { - let mut proto = rrg_proto::v2::list_interfaces::Result::default(); + fn into_proto(self) -> rrg_proto::list_interfaces::Result { + let mut proto = rrg_proto::list_interfaces::Result::default(); proto.set_interface(self.iface.into()); proto diff --git a/crates/rrg/src/action/list_mounts.rs b/crates/rrg/src/action/list_mounts.rs index a690be53..20177eef 100644 --- a/crates/rrg/src/action/list_mounts.rs +++ b/crates/rrg/src/action/list_mounts.rs @@ -36,10 +36,10 @@ where impl crate::response::Item for Item { - type Proto = rrg_proto::v2::list_mounts::Result; + type Proto = rrg_proto::list_mounts::Result; - fn into_proto(self) -> rrg_proto::v2::list_mounts::Result { - let mut proto = rrg_proto::v2::list_mounts::Result::default(); + fn into_proto(self) -> rrg_proto::list_mounts::Result { + let mut proto = rrg_proto::list_mounts::Result::default(); proto.set_mount(self.mount.into()); proto diff --git a/crates/rrg/src/blob.rs b/crates/rrg/src/blob.rs index d79b95f2..641ca4c1 100644 --- a/crates/rrg/src/blob.rs +++ b/crates/rrg/src/blob.rs @@ -28,7 +28,7 @@ impl From> for Blob { impl crate::response::Item for Blob { - type Proto = rrg_proto::v2::blob::Blob; + type Proto = rrg_proto::blob::Blob; fn into_proto(self) -> Self::Proto { let mut proto = Self::Proto::default(); diff --git a/crates/rrg/src/request.rs b/crates/rrg/src/request.rs index 0155e888..6edce3fa 100644 --- a/crates/rrg/src/request.rs +++ b/crates/rrg/src/request.rs @@ -81,12 +81,12 @@ impl std::fmt::Display for UnknownAction { } } -impl TryFrom for Action { +impl TryFrom for Action { type Error = UnknownAction; - fn try_from(proto: rrg_proto::v2::rrg::Action) -> Result { - use rrg_proto::v2::rrg::Action::*; + fn try_from(proto: rrg_proto::rrg::Action) -> Result { + use rrg_proto::rrg::Action::*; match proto { GET_SYSTEM_METADATA => Ok(Action::GetSystemMetadata), @@ -243,7 +243,7 @@ impl Request { } use protobuf::Message as _; - let proto = rrg_proto::v2::rrg::Request::parse_from_bytes(&message.data[..]) + let proto = rrg_proto::rrg::Request::parse_from_bytes(&message.data[..]) .map_err(|error| ParseRequestError { request_id: None, kind: ParseRequestErrorKind::MalformedBytes, @@ -254,11 +254,11 @@ impl Request { } } -impl TryFrom for Request { +impl TryFrom for Request { type Error = ParseRequestError; - fn try_from(mut proto: rrg_proto::v2::rrg::Request) -> Result { + fn try_from(mut proto: rrg_proto::rrg::Request) -> Result { use rrg_proto::try_from_duration; let request_id = RequestId { @@ -388,9 +388,9 @@ impl std::fmt::Display for ParseRequestErrorKind { } } -impl From for rrg_proto::v2::rrg::Status_Error_Type { +impl From for rrg_proto::rrg::Status_Error_Type { - fn from(kind: ParseRequestErrorKind) -> rrg_proto::v2::rrg::Status_Error_Type { + fn from(kind: ParseRequestErrorKind) -> rrg_proto::rrg::Status_Error_Type { use ParseRequestErrorKind::*; match kind { @@ -507,8 +507,8 @@ mod tests { fn action_try_from_proto_all_known() { use protobuf::ProtobufEnum as _; - for action in rrg_proto::v2::rrg::Action::values() { - if *action == rrg_proto::v2::rrg::Action::UNKNOWN { + for action in rrg_proto::rrg::Action::values() { + if *action == rrg_proto::rrg::Action::UNKNOWN { continue; } @@ -518,6 +518,6 @@ mod tests { #[test] fn action_try_fromt_proto_unknown() { - assert!(Action::try_from(rrg_proto::v2::rrg::Action::UNKNOWN).is_err()); + assert!(Action::try_from(rrg_proto::rrg::Action::UNKNOWN).is_err()); } } diff --git a/crates/rrg/src/response.rs b/crates/rrg/src/response.rs index fcd2325f..fadb42ed 100644 --- a/crates/rrg/src/response.rs +++ b/crates/rrg/src/response.rs @@ -60,7 +60,7 @@ impl Reply { pub fn send_unaccounted(self) -> usize { use protobuf::Message as _; - let data = rrg_proto::v2::rrg::Response::from(self).write_to_bytes() + let data = rrg_proto::rrg::Response::from(self).write_to_bytes() // This should only fail in case we are out of memory, which we are // almost certainly not (and if we are, we have bigger issue). .expect("failed to serialize a result response"); @@ -107,7 +107,7 @@ impl Status { pub fn send_unaccounted(self) -> usize { use protobuf::Message as _; - let data = rrg_proto::v2::rrg::Response::from(self).write_to_bytes() + let data = rrg_proto::rrg::Response::from(self).write_to_bytes() // This should only fail in case we are out of memory, which we are // almost certainly not (and if we are, we have bigger issue). .expect("failed to serialize a status response"); @@ -156,7 +156,7 @@ impl<'r, 'a> Log<'r, 'a> { pub fn send_unaccounted(self) { use protobuf::Message as _; - let data = rrg_proto::v2::rrg::Response::from(self).write_to_bytes() + let data = rrg_proto::rrg::Response::from(self).write_to_bytes() // This should only fail in case we are out of memory, which we are // almost certainly not (and if we are, we have bigger issue). .expect("failed to serialize a log response"); @@ -265,12 +265,12 @@ pub enum Sink { Blob, } -impl From for rrg_proto::v2::rrg::Sink { +impl From for rrg_proto::rrg::Sink { - fn from(sink: Sink) -> rrg_proto::v2::rrg::Sink { + fn from(sink: Sink) -> rrg_proto::rrg::Sink { match sink { - Sink::Startup => rrg_proto::v2::rrg::Sink::STARTUP, - Sink::Blob => rrg_proto::v2::rrg::Sink::BLOB, + Sink::Startup => rrg_proto::rrg::Sink::STARTUP, + Sink::Blob => rrg_proto::rrg::Sink::BLOB, } } } @@ -320,7 +320,7 @@ impl Parcel { pub fn send_unaccounted(self) -> usize { use protobuf::Message as _; - let data = rrg_proto::v2::rrg::Parcel::from(self).write_to_bytes() + let data = rrg_proto::rrg::Parcel::from(self).write_to_bytes() // This should only fail in case we are out of memory, which we are // almost certainly not (and if we are, we have bigger issue). .unwrap(); @@ -337,12 +337,12 @@ impl Parcel { } } -impl From> for rrg_proto::v2::rrg::Response +impl From> for rrg_proto::rrg::Response where I: Item, { - fn from(reply: Reply) -> rrg_proto::v2::rrg::Response { - let mut proto = rrg_proto::v2::rrg::Response::new(); + fn from(reply: Reply) -> rrg_proto::rrg::Response { + let mut proto = rrg_proto::rrg::Response::new(); proto.set_flow_id(reply.request_id.flow_id()); proto.set_request_id(reply.request_id.request_id()); proto.set_response_id(reply.response_id.0); @@ -363,10 +363,10 @@ where } } -impl From for rrg_proto::v2::rrg::Response { +impl From for rrg_proto::rrg::Response { - fn from(status: Status) -> rrg_proto::v2::rrg::Response { - let mut proto = rrg_proto::v2::rrg::Response::new(); + fn from(status: Status) -> rrg_proto::rrg::Response { + let mut proto = rrg_proto::rrg::Response::new(); proto.set_flow_id(status.request_id.flow_id()); proto.set_request_id(status.request_id.request_id()); proto.set_response_id(status.response_id.0); @@ -376,10 +376,10 @@ impl From for rrg_proto::v2::rrg::Response { } } -impl From for rrg_proto::v2::rrg::Status { +impl From for rrg_proto::rrg::Status { - fn from(status: Status) -> rrg_proto::v2::rrg::Status { - let mut proto = rrg_proto::v2::rrg::Status::new(); + fn from(status: Status) -> rrg_proto::rrg::Status { + let mut proto = rrg_proto::rrg::Status::new(); if let Err(error) = status.result { proto.set_error(error.into()); } @@ -388,10 +388,10 @@ impl From for rrg_proto::v2::rrg::Status { } } -impl<'r, 'a> From> for rrg_proto::v2::rrg::Response { +impl<'r, 'a> From> for rrg_proto::rrg::Response { - fn from(log: Log<'r, 'a>) -> rrg_proto::v2::rrg::Response { - let mut proto = rrg_proto::v2::rrg::Response::new(); + fn from(log: Log<'r, 'a>) -> rrg_proto::rrg::Response { + let mut proto = rrg_proto::rrg::Response::new(); proto.set_flow_id(log.request_id.flow_id()); proto.set_request_id(log.request_id.request_id()); proto.set_log(log.into()); @@ -400,10 +400,10 @@ impl<'r, 'a> From> for rrg_proto::v2::rrg::Response { } } -impl<'r, 'a> From> for rrg_proto::v2::rrg::Log { +impl<'r, 'a> From> for rrg_proto::rrg::Log { - fn from(log: Log<'r, 'a>) -> rrg_proto::v2::rrg::Log { - let mut proto = rrg_proto::v2::rrg::Log::new(); + fn from(log: Log<'r, 'a>) -> rrg_proto::rrg::Log { + let mut proto = rrg_proto::rrg::Log::new(); proto.set_level(log.record.level().into()); proto.set_timestamp(rrg_proto::into_timestamp(log.timestamp)); proto.set_message(log.record.args().to_string()); @@ -412,11 +412,11 @@ impl<'r, 'a> From> for rrg_proto::v2::rrg::Log { } } -impl From> for rrg_proto::v2::rrg::Parcel +impl From> for rrg_proto::rrg::Parcel where I: crate::response::Item, { - fn from(parcel: Parcel) -> rrg_proto::v2::rrg::Parcel { + fn from(parcel: Parcel) -> rrg_proto::rrg::Parcel { let payload_proto = parcel.payload.into_proto(); let payload_any = protobuf::well_known_types::Any::pack(&payload_proto) // The should not really ever fail, assumming that the protobuf @@ -424,7 +424,7 @@ where // memory. .unwrap(); - let mut proto = rrg_proto::v2::rrg::Parcel::new(); + let mut proto = rrg_proto::rrg::Parcel::new(); proto.set_sink(parcel.sink.into()); proto.set_payload(payload_any); diff --git a/crates/rrg/src/session/error.rs b/crates/rrg/src/session/error.rs index 08193bb3..7d69830a 100644 --- a/crates/rrg/src/session/error.rs +++ b/crates/rrg/src/session/error.rs @@ -114,10 +114,10 @@ impl From for Error { } } -impl From for rrg_proto::v2::rrg::Status_Error { +impl From for rrg_proto::rrg::Status_Error { - fn from(error: Error) -> rrg_proto::v2::rrg::Status_Error { - let mut proto = rrg_proto::v2::rrg::Status_Error::new(); + fn from(error: Error) -> rrg_proto::rrg::Status_Error { + let mut proto = rrg_proto::rrg::Status_Error::new(); proto.set_field_type(error.kind.into()); proto.set_message(error.to_string()); @@ -125,9 +125,9 @@ impl From for rrg_proto::v2::rrg::Status_Error { } } -impl From for rrg_proto::v2::rrg::Status_Error_Type { +impl From for rrg_proto::rrg::Status_Error_Type { - fn from(kind: ErrorKind) -> rrg_proto::v2::rrg::Status_Error_Type { + fn from(kind: ErrorKind) -> rrg_proto::rrg::Status_Error_Type { use ErrorKind::*; match kind { diff --git a/crates/rrg/src/startup.rs b/crates/rrg/src/startup.rs index 2ddd294c..334c8d7a 100644 --- a/crates/rrg/src/startup.rs +++ b/crates/rrg/src/startup.rs @@ -51,9 +51,9 @@ impl Startup { } impl crate::response::Item for Startup { - type Proto = rrg_proto::v2::startup::Startup; + type Proto = rrg_proto::startup::Startup; - fn into_proto(self) -> rrg_proto::v2::startup::Startup { + fn into_proto(self) -> rrg_proto::startup::Startup { self.into() } } @@ -108,12 +108,12 @@ impl Version { } } -impl Into for Startup { +impl Into for Startup { - fn into(self) -> rrg_proto::v2::startup::Startup { + fn into(self) -> rrg_proto::startup::Startup { use rrg_proto::into_timestamp; - let mut proto = rrg_proto::v2::startup::Startup::new(); + let mut proto = rrg_proto::startup::Startup::new(); proto.set_metadata(self.metadata.into()); if let Some(path) = self.path { proto.set_path(path.into()); @@ -125,10 +125,10 @@ impl Into for Startup { } } -impl Into for Metadata { +impl Into for Metadata { - fn into(self) -> rrg_proto::v2::startup::Metadata { - let mut proto = rrg_proto::v2::startup::Metadata::new(); + fn into(self) -> rrg_proto::startup::Metadata { + let mut proto = rrg_proto::startup::Metadata::new(); proto.set_name(self.name); // TODO(@panhania): Add support for remaining fields. proto.set_version(self.version.into()); @@ -137,10 +137,10 @@ impl Into for Metadata { } } -impl Into for Version { +impl Into for Version { - fn into(self) -> rrg_proto::v2::startup::Version { - let mut proto = rrg_proto::v2::startup::Version::new(); + fn into(self) -> rrg_proto::startup::Version { + let mut proto = rrg_proto::startup::Version::new(); proto.set_major(u32::from(self.major)); proto.set_minor(u32::from(self.minor)); proto.set_patch(u32::from(self.patch)); diff --git a/vendor/grr b/vendor/grr deleted file mode 160000 index 7f93f8bd..00000000 --- a/vendor/grr +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7f93f8bd44984ac6e9a0a49e9c709864489799ab