pg_embedded_setup_unpriv/cache/operations/
copy.rs1use camino::Utf8Path;
6use color_eyre::eyre::Context;
7use std::fs;
8use std::io;
9use std::path::Path;
10use tracing::debug;
11
12use crate::error::BootstrapResult;
13
14const LOG_TARGET: &str = "pg_embed::cache";
16
17pub fn copy_from_cache(source: &Utf8Path, target: &Utf8Path) -> BootstrapResult<()> {
46 log_copy_start(source, target);
47
48 fs::create_dir_all(target)
49 .with_context(|| format!("failed to create target directory for cache copy: {target}"))?;
50
51 copy_dir_recursive(source.as_std_path(), target.as_std_path())
52 .with_context(|| format!("failed to copy cached binaries from {source} to {target}"))?;
53
54 log_copy_complete(source, target);
55 Ok(())
56}
57
58fn log_copy_start(source: &Utf8Path, target: &Utf8Path) {
60 debug!(
61 target: LOG_TARGET,
62 source = %source,
63 target = %target,
64 "copying binaries from cache"
65 );
66}
67
68fn log_copy_complete(source: &Utf8Path, target: &Utf8Path) {
70 debug!(
71 target: LOG_TARGET,
72 source = %source,
73 target = %target,
74 "cache copy completed"
75 );
76}
77
78pub(crate) fn copy_dir_recursive(src: &Path, dst: &Path) -> io::Result<()> {
82 if !dst.exists() {
83 fs::create_dir_all(dst)?;
84 }
85
86 for dir_entry in fs::read_dir(src)? {
87 let entry = dir_entry?;
88 let file_type = entry.file_type()?;
89 let src_path = entry.path();
90 let dst_path = dst.join(entry.file_name());
91
92 if file_type.is_dir() {
93 copy_dir_recursive(&src_path, &dst_path)?;
94 } else if file_type.is_symlink() {
95 copy_symlink(&src_path, &dst_path)?;
96 } else {
97 copy_file_with_permissions(&src_path, &dst_path)?;
98 }
99 }
100
101 copy_permissions(src, dst);
102
103 Ok(())
104}
105
106fn copy_file_with_permissions(src: &Path, dst: &Path) -> io::Result<()> {
108 fs::copy(src, dst)?;
109 copy_permissions(src, dst);
110 Ok(())
111}
112
113fn copy_permissions(src: &Path, dst: &Path) {
115 let Ok(metadata) = fs::metadata(src) else {
116 return;
117 };
118 if let Err(err) = fs::set_permissions(dst, metadata.permissions()) {
119 log_permission_copy_failure(src, dst, &err);
120 }
121}
122
123fn log_permission_copy_failure(src: &Path, dst: &Path, err: &io::Error) {
125 debug!(
126 target: LOG_TARGET,
127 src = %src.display(),
128 dst = %dst.display(),
129 error = %err,
130 "failed to copy permissions (best effort)"
131 );
132}
133
134#[cfg(unix)]
136fn copy_symlink(src: &Path, dst: &Path) -> io::Result<()> {
137 let target = fs::read_link(src)?;
138 std::os::unix::fs::symlink(&target, dst)?;
139 Ok(())
140}
141
142#[cfg(not(unix))]
143fn copy_symlink(src: &Path, dst: &Path) -> io::Result<()> {
144 if src.is_file() {
147 fs::copy(src, dst)?;
148 } else if src.is_dir() {
149 copy_dir_recursive(src, dst)?;
150 } else {
151 return Err(io::Error::new(
152 io::ErrorKind::NotFound,
153 format!(
154 "symlink target does not exist or cannot be read: {}",
155 src.display()
156 ),
157 ));
158 }
159 Ok(())
160}