Skip to content

Commit 4a6cb75

Browse files
committed
🐛 Normalize Windows symlink path separators
Windows CreateSymbolicLinkW requires native backslash separators. Normalize forward slashes in symlink targets and link paths before calling symlink_dir/symlink_file to prevent invalid symlink creation.
1 parent 4b031b2 commit 4a6cb75

1 file changed

Lines changed: 28 additions & 4 deletions

File tree

pna/src/fs.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,45 @@ pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Resu
2929
#[cfg(windows)]
3030
fn inner(original: &Path, link: &Path) -> io::Result<()> {
3131
use std::borrow::Cow;
32+
use std::ffi::OsString;
33+
use std::os::windows::ffi::{OsStrExt, OsStringExt};
34+
use std::path::PathBuf;
35+
36+
fn normalize_windows_separators(path: &Path) -> Cow<'_, Path> {
37+
let wide: Vec<u16> = path.as_os_str().encode_wide().collect();
38+
if !wide.iter().any(|&unit| unit == u16::from(b'/')) {
39+
return Cow::Borrowed(path);
40+
}
41+
let normalized = wide
42+
.into_iter()
43+
.map(|unit| {
44+
if unit == u16::from(b'/') {
45+
u16::from(b'\\')
46+
} else {
47+
unit
48+
}
49+
})
50+
.collect::<Vec<_>>();
51+
Cow::Owned(PathBuf::from(OsString::from_wide(&normalized)))
52+
}
53+
54+
let original = normalize_windows_separators(original);
55+
let link = normalize_windows_separators(link);
3256
// Symlink targets are resolved relative to the link's parent directory,
3357
// not the current working directory. Resolve before checking is_dir()
3458
// so that relative targets pick the correct symlink type.
3559
let is_dir = if original.is_relative() {
3660
link.parent()
37-
.map(|p| Cow::Owned(p.join(original)))
38-
.unwrap_or(Cow::Borrowed(original))
61+
.map(|p| p.join(original.as_ref()))
62+
.unwrap_or_else(|| original.as_ref().to_path_buf())
3963
.is_dir()
4064
} else {
4165
original.is_dir()
4266
};
4367
if is_dir {
44-
os::windows::fs::symlink_dir(original, link)
68+
os::windows::fs::symlink_dir(original.as_ref(), link.as_ref())
4569
} else {
46-
os::windows::fs::symlink_file(original, link)
70+
os::windows::fs::symlink_file(original.as_ref(), link.as_ref())
4771
}
4872
}
4973
#[cfg(target_os = "wasi")]

0 commit comments

Comments
 (0)