exp: cross #2
This commit is contained in:
177
src/context/unshare.rs
Normal file
177
src/context/unshare.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
use super::api::{Context, ContextCommand, ContextDriver};
|
||||
use log::debug;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct UnshareDriver {
|
||||
pub path: String,
|
||||
pub parent: Option<Arc<super::api::Context>>,
|
||||
}
|
||||
|
||||
/// Recursively copy a directory and all its contents
|
||||
fn copy_dir_recursive(src: &Path, dest: &Path) -> io::Result<()> {
|
||||
// Create the destination directory
|
||||
std::fs::create_dir_all(dest)?;
|
||||
|
||||
// Iterate through the source directory
|
||||
for entry in std::fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let src_path = entry.path();
|
||||
let dest_path = dest.join(entry.file_name());
|
||||
|
||||
if src_path.is_dir() {
|
||||
// Recursively copy subdirectories
|
||||
copy_dir_recursive(&src_path, &dest_path)?;
|
||||
} else {
|
||||
// Copy files
|
||||
std::fs::copy(&src_path, &dest_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ContextDriver for UnshareDriver {
|
||||
fn ensure_available(&self, src: &Path, dest_root: &str) -> io::Result<PathBuf> {
|
||||
// Construct the destination path inside the chroot
|
||||
let dest_dir = Path::new(&self.path).join(dest_root.trim_start_matches('/'));
|
||||
debug!(
|
||||
"unshare/ensure_available: copy '{}' to '{}'",
|
||||
src.display(),
|
||||
dest_dir.display()
|
||||
);
|
||||
|
||||
// Ensure the destination directory exists
|
||||
std::fs::create_dir_all(&dest_dir)?;
|
||||
|
||||
// Get the filename from the source path
|
||||
let filename = src
|
||||
.file_name()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Invalid source path"))?;
|
||||
|
||||
// Construct the full destination path
|
||||
let dest_path = dest_dir.join(filename);
|
||||
|
||||
// Copy the file or directory into the chroot
|
||||
if src.is_dir() {
|
||||
copy_dir_recursive(src, &dest_path)?;
|
||||
debug!(
|
||||
"Copied directory {} to {}",
|
||||
src.display(),
|
||||
dest_path.display()
|
||||
);
|
||||
} else {
|
||||
std::fs::copy(src, &dest_path)?;
|
||||
debug!("Copied file {} to {}", src.display(), dest_path.display());
|
||||
}
|
||||
|
||||
// Return the path as it appears inside the chroot (without the chroot prefix)
|
||||
Ok(Path::new(dest_root).join(filename))
|
||||
}
|
||||
|
||||
fn retrieve_path(&self, _src: &Path, _dest: &Path) -> io::Result<()> {
|
||||
// TODO: Implement chroot file retrieval logic
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"retrieve_path not yet implemented for chroot",
|
||||
))
|
||||
}
|
||||
|
||||
fn list_files(&self, _path: &Path) -> io::Result<Vec<PathBuf>> {
|
||||
// TODO: Implement chroot file listing logic
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"list_files not yet implemented for chroot",
|
||||
))
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[String],
|
||||
env: &[(String, String)],
|
||||
cwd: Option<&str>,
|
||||
) -> io::Result<std::process::ExitStatus> {
|
||||
self.command(program, args, env, cwd).status()
|
||||
}
|
||||
|
||||
fn run_output(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[String],
|
||||
env: &[(String, String)],
|
||||
cwd: Option<&str>,
|
||||
) -> io::Result<std::process::Output> {
|
||||
self.command(program, args, env, cwd).output()
|
||||
}
|
||||
|
||||
fn create_temp_dir(&self) -> io::Result<String> {
|
||||
// Create a temporary directory inside the chroot
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
let work_dir_name = format!("pkh-build-{}", timestamp);
|
||||
let work_dir_inside_chroot = format!("/tmp/{}", work_dir_name);
|
||||
|
||||
// Create the directory on the host filesystem
|
||||
let host_path = Path::new(&self.path).join("tmp").join(&work_dir_name);
|
||||
std::fs::create_dir_all(&host_path)?;
|
||||
|
||||
debug!(
|
||||
"Created work directory: {} (host: {})",
|
||||
work_dir_inside_chroot,
|
||||
host_path.display()
|
||||
);
|
||||
|
||||
// Return the path as it appears inside the chroot
|
||||
Ok(work_dir_inside_chroot)
|
||||
}
|
||||
|
||||
fn copy_path(&self, src: &Path, dest: &Path) -> io::Result<()> {
|
||||
let host_src = Path::new(&self.path).join(src.to_string_lossy().trim_start_matches('/'));
|
||||
let host_dest = Path::new(&self.path).join(dest.to_string_lossy().trim_start_matches('/'));
|
||||
self.parent().copy_path(&host_src, &host_dest)
|
||||
}
|
||||
|
||||
fn read_file(&self, path: &Path) -> io::Result<String> {
|
||||
let host_path = Path::new(&self.path).join(path.to_string_lossy().trim_start_matches('/'));
|
||||
self.parent().read_file(&host_path)
|
||||
}
|
||||
|
||||
fn write_file(&self, path: &Path, content: &str) -> io::Result<()> {
|
||||
let host_path = Path::new(&self.path).join(path.to_string_lossy().trim_start_matches('/'));
|
||||
self.parent().write_file(&host_path, content)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnshareDriver {
|
||||
fn parent(&self) -> &Context {
|
||||
self.parent
|
||||
.as_ref()
|
||||
.expect("UnshareDriver requires a parent context")
|
||||
}
|
||||
|
||||
fn command(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[String],
|
||||
env: &[(String, String)],
|
||||
cwd: Option<&str>,
|
||||
) -> ContextCommand<'_> {
|
||||
let mut cmd = self.parent().command("sudo");
|
||||
cmd.args(env.iter().map(|(k, v)| format!("{k}={v}")));
|
||||
|
||||
cmd.arg("unshare").arg("-R").arg(&self.path);
|
||||
|
||||
if let Some(dir) = cwd {
|
||||
cmd.arg("-w").arg(dir);
|
||||
}
|
||||
|
||||
cmd.arg(program).args(args);
|
||||
|
||||
cmd
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user