From 3cad2b2f7e860e26bdecb2a2580ac5760aab4a51 Mon Sep 17 00:00:00 2001 From: Sebastien Tardif Date: Tue, 23 Jun 2026 22:03:17 -0700 Subject: [PATCH] std: implement Windows set_permissions_nofollow via reparse open Route path set_perm through handle-based SetFileInformationByHandle (follow reparse points with BACKUP_SEMANTICS), matching File::set_permissions. Add set_perm_nofollow with FILE_FLAG_OPEN_REPARSE_POINT so unstable fs::set_permissions_nofollow works on Windows for symlinks/junctions (WSL/interop), not only Unix. Closes #557 Signed-off-by: Sebastien Tardif --- library/std/src/sys/fs/mod.rs | 9 +++++++-- library/std/src/sys/fs/windows.rs | 20 ++++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 0c297c5766b82..a3f32ede24cba 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -137,10 +137,15 @@ pub fn set_permissions_nofollow(path: &Path, perm: crate::fs::Permissions) -> io options.open(path)?.set_permissions(perm) } -#[cfg(any(not(unix), target_os = "vxworks"))] +#[cfg(windows)] +pub fn set_permissions_nofollow(path: &Path, perm: crate::fs::Permissions) -> io::Result<()> { + with_native_path(path, &|path| imp::set_perm_nofollow(path, perm.0.clone())) +} + +#[cfg(any(not(any(unix, windows)), target_os = "vxworks"))] pub fn set_permissions_nofollow(_path: &Path, _perm: crate::fs::Permissions) -> io::Result<()> { crate::unimplemented!( - "`set_permissions_nofollow` is currently only implemented on Unix platforms" + "`set_permissions_nofollow` is currently only implemented on Unix and Windows platforms" ) } diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index e3e7b081b47d5..802ac15426df8 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1558,10 +1558,22 @@ fn metadata(path: &WCStr, reparse: ReparsePoint) -> io::Result { } pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { - unsafe { - cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?; - Ok(()) - } + // Open the target (follow reparse points) and set attributes via handle so + // behavior matches `File::set_permissions` rather than ambiguous path APIs. + let mut opts = OpenOptions::new(); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); + let file = File::open_native(p, &opts)?; + file.set_permissions(perm) +} + +pub fn set_perm_nofollow(p: &WCStr, perm: FilePermissions) -> io::Result<()> { + let mut opts = OpenOptions::new(); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); + // `FILE_FLAG_OPEN_REPARSE_POINT` so permissions apply to the link/junction itself. + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); + let file = File::open_native(p, &opts)?; + file.set_permissions(perm) } pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> {