diff --git a/src/compat.rs b/src/compat.rs index e08ceea..a61f24e 100644 --- a/src/compat.rs +++ b/src/compat.rs @@ -255,7 +255,7 @@ fn compat_state_update_2() { } #[cfg_attr(test, derive(Debug, PartialEq))] -#[derive(Clone)] +#[derive(Copy, Clone)] pub(crate) struct Compatibility { abi: ABI, pub(crate) level: Option, diff --git a/src/lib.rs b/src/lib.rs index 3c26100..8e6590c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -343,4 +343,24 @@ mod tests { false, ); } + + #[test] + fn ruleset_created_try_clone() { + check_ruleset_support( + ABI::V1, + Some(ABI::V1), + move |ruleset: Ruleset| -> _ { + Ok(ruleset + .handle_access(AccessFs::Execute)? + .create()? + .add_rule(PathBeneath::new( + PathFd::new("/usr").unwrap(), + AccessFs::Execute, + ))? + .try_clone()? + .restrict_self()?) + }, + false, + ); + } } diff --git a/src/ruleset.rs b/src/ruleset.rs index f802c3c..154fee4 100644 --- a/src/ruleset.rs +++ b/src/ruleset.rs @@ -653,6 +653,29 @@ impl RulesetCreated { }; Ok(body()?) } + + /// Creates a new [`RulesetCreated`] instance that shares the same underlying + /// landlock ruleset file descriptor as the existing [`RulesetCreated`] instance. + /// Rule modification will affect both [`RulesetCreated`] instances simultaneously. + /// + /// On error, returns [`std::io::Error`]. + pub fn try_clone(&self) -> std::io::Result { + let mut dup_fd = -1; + + if self.fd != -1 { + dup_fd = unsafe { libc::fcntl(self.fd, libc::F_DUPFD_CLOEXEC, 0) }; + if dup_fd == -1 { + return Err(Error::last_os_error()); + } + } + + Ok(RulesetCreated { + fd: dup_fd, + no_new_privs: self.no_new_privs, + requested_handled_fs: self.requested_handled_fs, + compat: self.compat, + }) + } } impl Drop for RulesetCreated {