context: copy symlinks as symlinks and improve error messages
This commit is contained in:
+74
-7
@@ -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
|
||||
),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user