Skip to content

Commit 98d416a

Browse files
committed
Enable copying symlinks when using remote docker.
This handles the reads the link dst from the symlink when using temporary directories, and creates a softlink to the path (keeping relative and absolute symlinks valid) in the temporary directory, which is then copied with the same permissions over the dst filesystem. This should not keep the same metadata, but since we fingerprint source files, this should not matter.
1 parent e583132 commit 98d416a

File tree

1 file changed

+64
-14
lines changed

1 file changed

+64
-14
lines changed

src/docker/remote.rs

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,16 @@ fn copy_volume_files_nocache(
171171
container: &str,
172172
src: &Path,
173173
dst: &Path,
174+
copy_symlinks: bool,
174175
msg_info: MessageInfo,
175176
) -> Result<ExitStatus> {
176177
// avoid any cached directories when copying
177178
// see https://bford.info/cachedir/
178179
// SAFETY: safe, single-threaded execution.
179180
let tempdir = unsafe { temp::TempDir::new()? };
180181
let temppath = tempdir.path();
181-
copy_dir(src, temppath, 0, |e, _| is_cachedir(e))?;
182+
let had_symlinks = copy_dir(src, temppath, copy_symlinks, 0, |e, _| is_cachedir(e))?;
183+
warn_symlinks(had_symlinks, msg_info)?;
182184
copy_volume_files(engine, container, temppath, dst, msg_info)
183185
}
184186

@@ -234,10 +236,18 @@ pub fn copy_volume_container_cargo(
234236
}
235237

236238
// recursively copy a directory into another
237-
fn copy_dir<Skip>(src: &Path, dst: &Path, depth: u32, skip: Skip) -> Result<()>
239+
fn copy_dir<Skip>(
240+
src: &Path,
241+
dst: &Path,
242+
copy_symlinks: bool,
243+
depth: u32,
244+
skip: Skip,
245+
) -> Result<bool>
238246
where
239247
Skip: Copy + Fn(&fs::DirEntry, u32) -> bool,
240248
{
249+
let mut had_symlinks = false;
250+
241251
for entry in fs::read_dir(src)? {
242252
let file = entry?;
243253
if skip(&file, depth) {
@@ -248,13 +258,47 @@ where
248258
let dst_path = dst.join(file.file_name());
249259
if file.file_type()?.is_file() {
250260
fs::copy(&src_path, &dst_path)?;
251-
} else {
261+
} else if file.file_type()?.is_dir() {
252262
fs::create_dir(&dst_path).ok();
253-
copy_dir(&src_path, &dst_path, depth + 1, skip)?;
263+
had_symlinks = copy_dir(&src_path, &dst_path, copy_symlinks, depth + 1, skip)?;
264+
} else if copy_symlinks {
265+
had_symlinks = true;
266+
let link_dst = fs::read_link(src_path)?;
267+
268+
#[cfg(target_family = "unix")]
269+
{
270+
std::os::unix::fs::symlink(link_dst, dst_path)?;
271+
}
272+
273+
#[cfg(target_family = "windows")]
274+
{
275+
let link_dst_absolute = if link_dst.is_absolute() {
276+
link_dst
277+
} else {
278+
// we cannot fail even if the linked to path does not exist.
279+
src.join(link_dst)
280+
};
281+
if link_dst_absolute.is_dir() {
282+
std::os::windows::fs::symlink_dir(link_dst, dst_path)?;
283+
} else {
284+
// symlink_file handles everything that isn't a directory
285+
std::os::windows::fs::symlink_file(link_dst, dst_path)?;
286+
}
287+
}
288+
} else {
289+
had_symlinks = true;
254290
}
255291
}
256292

257-
Ok(())
293+
Ok(had_symlinks)
294+
}
295+
296+
fn warn_symlinks(had_symlinks: bool, msg_info: MessageInfo) -> Result<()> {
297+
if had_symlinks {
298+
shell::warn("copied directory contained symlinks. if the volume the link points to was not mounted, the remote build may fail", msg_info)
299+
} else {
300+
Ok(())
301+
}
258302
}
259303

260304
// copy over files needed for all targets in the toolchain that should never change
@@ -284,20 +328,25 @@ fn copy_volume_container_rust_base(
284328
let tempdir = unsafe { temp::TempDir::new()? };
285329
let temppath = tempdir.path();
286330
fs::create_dir_all(&temppath.join(&rustlib))?;
287-
copy_dir(&sysroot.join("lib"), &temppath.join("lib"), 0, |e, d| {
288-
d == 0 && e.file_name() == "rustlib"
289-
})?;
331+
let mut had_symlinks = copy_dir(
332+
&sysroot.join("lib"),
333+
&temppath.join("lib"),
334+
true,
335+
0,
336+
|e, d| d == 0 && e.file_name() == "rustlib",
337+
)?;
290338

291339
// next, copy the src/etc directories inside rustlib
292-
copy_dir(
340+
had_symlinks |= copy_dir(
293341
&sysroot.join(&rustlib),
294342
&temppath.join(&rustlib),
343+
true,
295344
0,
296345
|e, d| d == 0 && !(e.file_name() == "src" || e.file_name() == "etc"),
297346
)?;
298347
copy_volume_files(engine, container, &temppath.join("lib"), &dst, msg_info)?;
299348

300-
Ok(())
349+
warn_symlinks(had_symlinks, msg_info)
301350
}
302351

303352
fn copy_volume_container_rust_manifest(
@@ -316,15 +365,16 @@ fn copy_volume_container_rust_manifest(
316365
let tempdir = unsafe { temp::TempDir::new()? };
317366
let temppath = tempdir.path();
318367
fs::create_dir_all(&temppath.join(&rustlib))?;
319-
copy_dir(
368+
let had_symlinks = copy_dir(
320369
&sysroot.join(&rustlib),
321370
&temppath.join(&rustlib),
371+
true,
322372
0,
323373
|e, d| d != 0 || e.file_type().map(|t| !t.is_file()).unwrap_or(true),
324374
)?;
325375
copy_volume_files(engine, container, &temppath.join("lib"), &dst, msg_info)?;
326376

327-
Ok(())
377+
warn_symlinks(had_symlinks, msg_info)
328378
}
329379

330380
// copy over the toolchain for a specific triple
@@ -570,7 +620,7 @@ fn copy_volume_container_project(
570620
if copy_cache {
571621
copy_volume_files(engine, container, src, dst, msg_info)
572622
} else {
573-
copy_volume_files_nocache(engine, container, src, dst, msg_info)
623+
copy_volume_files_nocache(engine, container, src, dst, true, msg_info)
574624
}
575625
};
576626
match volume {
@@ -809,7 +859,7 @@ pub(crate) fn run(
809859
if copy_cache {
810860
copy_volume_files(engine, &container, src, dst, msg_info)
811861
} else {
812-
copy_volume_files_nocache(engine, &container, src, dst, msg_info)
862+
copy_volume_files_nocache(engine, &container, src, dst, true, msg_info)
813863
}
814864
};
815865
let mount_prefix_path = mount_prefix.as_ref();

0 commit comments

Comments
 (0)