You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tourmaline/src/task/chrooting.rs

62 lines
1.5 KiB
Rust

use std::{
ffi::{c_int, CString},
io,
os::unix::prelude::OsStrExt,
path::{Path, PathBuf},
};
use libc::CLONE_FS;
use tokio::task::JoinHandle;
use crate::error::ChrootError;
pub struct ChrootedTask {
root_path: PathBuf,
}
impl ChrootedTask {
/// Creates a new chrooted thread with the given path
pub fn new<P: Into<PathBuf>>(root_path: P) -> Self {
Self {
root_path: root_path.into(),
}
}
/// Runs the given future in a new chroot
pub fn run<F, T>(self, call: F) -> JoinHandle<Result<T, ChrootError>>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
let root_path = self.root_path;
let handle = std::thread::spawn(move || {
unsafe {
init_chroot(&root_path)?;
}
Ok(call())
});
tokio::task::spawn_blocking(|| handle.join().unwrap())
}
}
unsafe fn init_chroot(path: &Path) -> Result<(), ChrootError> {
handle_err_code(libc::unshare(CLONE_FS)).map_err(ChrootError::Unshare)?;
let path_str = CString::new(path.as_os_str().as_bytes().to_vec()).unwrap();
handle_err_code(libc::chroot(
path_str.as_bytes_with_nul().as_ptr() as *const libc::c_char
))
.map_err(ChrootError::Chroot)?;
std::env::set_current_dir(path).map_err(ChrootError::ChDir)?;
std::env::set_var("PWD", "/");
Ok(())
}
fn handle_err_code(code: c_int) -> Result<(), io::Error> {
if code != 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}