diff --git a/src/linux_sandbox.rs b/src/linux_sandbox.rs index b0405e4..1adf085 100644 --- a/src/linux_sandbox.rs +++ b/src/linux_sandbox.rs @@ -7,8 +7,8 @@ use std::path::{Path, PathBuf}; use std::process::Command; use landlock::{ - ABI, Access, AccessFs, CompatLevel, Compatible, Ruleset, RulesetAttr, RulesetCreatedAttr, - RulesetStatus, path_beneath_rules, + path_beneath_rules, Access, AccessFs, CompatLevel, Compatible, Ruleset, RulesetAttr, + RulesetCreatedAttr, RulesetStatus, ABI, }; pub const HELPER_ARG: &str = "__catdesk_landlock_exec"; @@ -204,13 +204,26 @@ pub fn apply_workspace_landlock( Ok(()) } -pub fn helper_command(command: &str, workspace: &Path) -> io::Result<(Command, PathBuf)> { - let executable = std::env::current_exe().map_err(|error| { +fn helper_executable() -> io::Result { + let proc_self_exe = PathBuf::from("/proc/self/exe"); + if proc_self_exe.metadata().is_ok() { + // Keep self-reexec working when the on-disk CatDesk binary is replaced + // while this process is still running. current_exe() may then resolve + // to a deleted pathname, while /proc/self/exe still references the + // live executable inode. + return Ok(proc_self_exe); + } + + std::env::current_exe().map_err(|error| { io::Error::new( error.kind(), format!("failed to locate CatDesk executable for Landlock helper: {error}"), ) - })?; + }) +} + +pub fn helper_command(command: &str, workspace: &Path) -> io::Result<(Command, PathBuf)> { + let executable = helper_executable()?; let scratch_dir = std::env::temp_dir().join(format!("catdesk-sandbox-{}", uuid::Uuid::new_v4())); @@ -276,6 +289,16 @@ mod tests { assert!(!HELPER_ARG.contains(char::is_whitespace)); } + #[test] + fn helper_command_prefers_proc_self_exe_when_available() { + let (command, scratch) = + helper_command("true", Path::new(".")).expect("prepare Landlock helper command"); + if Path::new("/proc/self/exe").metadata().is_ok() { + assert_eq!(command.get_program(), OsStr::new("/proc/self/exe")); + } + std::fs::remove_dir_all(scratch).expect("remove scratch directory"); + } + #[test] fn helper_command_creates_private_scratch_directory() { use std::os::unix::fs::PermissionsExt;