diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..aa3a4ad --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "rust-lang.rust" +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b5bcfae --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,241 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clone-shim" +version = "0.1.0" +dependencies = [ + "env_logger", + "libc", + "log", + "nix", + "thiserror", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "libc" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1c80731 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "clone-shim" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +log = "0.4" +env_logger = "0.9" +thiserror = "1" + +libc = "0.2.117" +nix = "0.23.1" diff --git a/src/clone.rs b/src/clone.rs new file mode 100644 index 0000000..10167b6 --- /dev/null +++ b/src/clone.rs @@ -0,0 +1,130 @@ +use std::fs::File; +use std::marker::PhantomData; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +use libc::{pid_t, syscall, SYS_clone3}; +use nix::errno::Errno; +pub use nix::sched::CloneFlags; +use nix::sys::signal::Signal; +use nix::unistd::Pid; + +pub struct CloneArgs<'a> { + pub flags: CloneFlags, + + pub pidfd: Option<&'a mut File>, + pidfd_int: RawFd, + + pub child_tid: Option<&'a mut Pid>, + child_tid_int: pid_t, + + pub parent_tid: Option<&'a mut Pid>, + parent_tid_int: pid_t, + + pub exit_signal: Option, + + pub stack: Option<&'a mut [u8]>, + pub set_tid: Option<&'a [Pid]>, + pub cgroup: Option<&'a File>, +} + +#[repr(C)] +struct CloneArgsInternal<'a> { + flags: u64, + pidfd: u64, + child_tid: u64, + parent_tid: u64, + exit_signal: u64, + stack: u64, + stack_size: u64, + tls: u64, + set_tid: u64, + set_tid_size: u64, + cgroup: u64, + + phantom: PhantomData<&'a ()>, +} + +impl<'a: 'b, 'b: 'c, 'c> CloneArgs<'a> { + pub fn new(flags: CloneFlags) -> CloneArgs<'a> { + CloneArgs { + flags, + + pidfd: None, + pidfd_int: 0, + + child_tid: None, + child_tid_int: 0, + + parent_tid: None, + parent_tid_int: 0, + + exit_signal: None, + + stack: None, + set_tid: None, + cgroup: None, + } + } + + fn process(&'b mut self) -> CloneArgsInternal<'c> { + CloneArgsInternal { + flags: self.flags.bits() as u64, + pidfd: self + .pidfd + .as_ref() + .map(|_| (self.pidfd_int) as *mut RawFd as u64) + .unwrap_or(0), + child_tid: self + .child_tid + .as_ref() + .map(|_| self.child_tid_int as *mut pid_t as u64) + .unwrap_or(0), + parent_tid: self + .parent_tid + .as_ref() + .map(|_| self.parent_tid_int as *mut pid_t as u64) + .unwrap_or(0), + exit_signal: self.exit_signal.map(|s| s as i32 as u64).unwrap_or(0), + stack: self + .stack + .as_mut() + .map(|s| s.as_mut_ptr() as u64) + .unwrap_or(0), + stack_size: self.stack.as_ref().map(|s| s.len() as u64).unwrap_or(0), + tls: 0, + set_tid: self + .set_tid + .as_ref() + .map(|s| s.as_ptr() as u64) + .unwrap_or(0), + set_tid_size: self.set_tid.as_ref().map(|s| s.len() as u64).unwrap_or(0), + cgroup: self.cgroup.map(|c| c.as_raw_fd() as u64).unwrap_or(0), + + phantom: PhantomData, + } + } + + fn finalise(&mut self) { + if let Some(r) = &mut self.pidfd { + **r = unsafe { File::from_raw_fd(self.pidfd_int) }; + } + if let Some(r) = &mut self.child_tid { + **r = Pid::from_raw(self.child_tid_int); + } + if let Some(r) = &mut self.parent_tid { + **r = Pid::from_raw(self.parent_tid_int); + } + } +} + +pub fn clone3(mut args: CloneArgs) -> nix::Result { + let args_int: CloneArgsInternal = args.process(); + + let result = unsafe { syscall(SYS_clone3, &args_int, std::mem::size_of_val(&args_int)) }; + + let out = Errno::result(result).map(|p| Pid::from_raw(p as i32))?; + + args.finalise(); + + Ok(out) +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..83b5e76 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,7 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("{msg}: {src}")] + Nix { msg: &'static str, src: nix::Error }, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..fe7890f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,28 @@ +use log::info; + +mod clone; +mod error; + +use clone::{clone3, CloneArgs, CloneFlags}; +use error::Error; + +use nix::unistd::Pid; + +fn main() -> Result<(), Error> { + let env = env_logger::Env::new().filter_or("LOG", "info"); + env_logger::init_from_env(env); + + info!("getting started"); + + if clone3(CloneArgs::new(CloneFlags::empty())).map_err(|e| Error::Nix { + msg: "clone3", + src: e, + })? != Pid::from_raw(0) + { + info!("hello from the child"); + } else { + info!("hello from the parent"); + } + + Ok(()) +}