context: copy symlinks as symlinks and improve error messages

This commit is contained in:
2026-07-17 10:12:46 +02:00
parent 2b27b7b06e
commit c4b59a4376
11 changed files with 382 additions and 54 deletions
+74 -7
View File
@@ -1,6 +1,8 @@
use super::api::{Context, ContextCommand, ContextDriver};
use log::debug;
use std::fs;
use std::io;
use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf};
use std::sync::Arc;
@@ -9,23 +11,88 @@ pub struct UnshareDriver {
pub parent: Option<Arc<super::api::Context>>,
}
/// Recursively copy a directory and all its contents
/// Recursively copy a directory and all its contents.
///
/// Symlinks are copied as symlinks (preserving the link target rather than
/// following it), so that dangling links do not abort the copy.
fn copy_dir_recursive(src: &Path, dest: &Path) -> io::Result<()> {
// Create the destination directory
std::fs::create_dir_all(dest)?;
std::fs::create_dir_all(dest).map_err(|e| {
io::Error::new(
e.kind(),
format!("Failed to create directory '{}': {}", dest.display(), e),
)
})?;
// Iterate through the source directory
for entry in std::fs::read_dir(src)? {
let entry = entry?;
let read = std::fs::read_dir(src).map_err(|e| {
io::Error::new(
e.kind(),
format!("Failed to read directory '{}': {}", src.display(), e),
)
})?;
for entry in read {
let entry = entry.map_err(|e| {
io::Error::new(
e.kind(),
format!("Failed to read entry in '{}': {}", src.display(), e),
)
})?;
let src_path = entry.path();
let dest_path = dest.join(entry.file_name());
if src_path.is_dir() {
// Handle symlinks explicitly: reproduce the link itself instead of
// following it. This avoids failing on dangling/absolute symlinks.
let metadata = match std::fs::symlink_metadata(&src_path) {
Ok(m) => m,
Err(e) => {
return Err(io::Error::new(
e.kind(),
format!(
"Failed to read metadata of '{}': {}",
src_path.display(),
e
),
))
}
};
if metadata.file_type().is_symlink() {
let target = std::fs::read_link(&src_path).map_err(|e| {
io::Error::new(
e.kind(),
format!("Failed to read symlink '{}': {}", src_path.display(), e),
)
})?;
// Remove an existing destination entry (e.g. from a previous attempt)
let _ = fs::remove_file(&dest_path);
symlink(&target, &dest_path).map_err(|e| {
io::Error::new(
e.kind(),
format!(
"Failed to create symlink '{}' -> '{}': {}",
dest_path.display(),
target.display(),
e
),
)
})?;
} else 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)?;
// Copy regular files
std::fs::copy(&src_path, &dest_path).map_err(|e| {
io::Error::new(
e.kind(),
format!(
"Failed to copy '{}' to '{}': {}",
src_path.display(),
dest_path.display(),
e
),
)
})?;
}
}