From 6e377849c09a310b6eef50ebd91c1f014d41ab73 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sun, 12 Feb 2023 12:05:56 +0000 Subject: [PATCH 1/2] Correctly convert an NT path to a Win32 path This can be done by simply changing the `\??\` prefix to `\\?\` and then attempting to convert to a user path. Currently it simply strips off the prefix which could lead to the wrong path being returned (e.g. if it's not a drive path or if the path contains trailing spaces, etc). --- library/std/src/fs/tests.rs | 6 +++++- library/std/src/sys/windows/fs.rs | 27 ++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 401def18458..a8a0b9f122d 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -919,6 +919,7 @@ fn symlink_noexist() { #[test] fn read_link() { + let tmpdir = tmpdir(); if cfg!(windows) { // directory symlink assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData")); @@ -933,8 +934,11 @@ fn read_link() { Path::new(r"C:\Users") ); } + // Check that readlink works with non-drive paths on Windows. + let link = tmpdir.join("link_unc"); + check!(symlink_dir(r"\\localhost\c$\", &link)); + assert_eq!(check!(fs::read_link(&link)), Path::new(r"\\localhost\c$\")); } - let tmpdir = tmpdir(); let link = tmpdir.join("link"); if !got_symlink_permission(&tmpdir) { return; diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index f99cdfbecfb..ff05d31915c 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -477,7 +477,7 @@ impl File { fn reparse_point( &self, space: &mut Align8<[MaybeUninit]>, - ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> { + ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> { unsafe { let mut bytes = 0; cvt({ @@ -496,7 +496,7 @@ impl File { ) })?; const _: () = assert!(core::mem::align_of::() <= 8); - Ok((bytes, space.0.as_ptr().cast::())) + Ok((bytes, space.0.as_mut_ptr().cast::())) } } @@ -506,22 +506,22 @@ impl File { unsafe { let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { c::IO_REPARSE_TAG_SYMLINK => { - let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = - ptr::addr_of!((*buf).rest).cast(); + let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = + ptr::addr_of_mut!((*buf).rest).cast(); assert!(info.is_aligned()); ( - ptr::addr_of!((*info).PathBuffer).cast::(), + ptr::addr_of_mut!((*info).PathBuffer).cast::(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, ) } c::IO_REPARSE_TAG_MOUNT_POINT => { - let info: *const c::MOUNT_POINT_REPARSE_BUFFER = - ptr::addr_of!((*buf).rest).cast(); + let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = + ptr::addr_of_mut!((*buf).rest).cast(); assert!(info.is_aligned()); ( - ptr::addr_of!((*info).PathBuffer).cast::(), + ptr::addr_of_mut!((*info).PathBuffer).cast::(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, false, @@ -535,13 +535,18 @@ impl File { } }; let subst_ptr = path_buffer.add(subst_off.into()); - let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize); + let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize); // Absolute paths start with an NT internal namespace prefix `\??\` // We should not let it leak through. if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) { - subst = &subst[4..]; + // Turn `\??\` into `\\?\` (a verbatim path). + subst[1] = b'\\' as u16; + // Attempt to convert to a more user-friendly path. + let user = super::args::to_user_path(subst.iter().copied().chain([0]).collect())?; + Ok(PathBuf::from(OsString::from_wide(&user))) + } else { + Ok(PathBuf::from(OsString::from_wide(subst))) } - Ok(PathBuf::from(OsString::from_wide(subst))) } } From 109a47fc9d63a5e093bd36423e290fe8bc18ae25 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 3 May 2023 11:20:59 +0100 Subject: [PATCH 2/2] Use `from_wide_to_user_path` in `read_link` --- library/std/src/sys/windows/args.rs | 5 +++-- library/std/src/sys/windows/fs.rs | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs index 43c0cdb657e..5bfd8b52ed0 100644 --- a/library/std/src/sys/windows/args.rs +++ b/library/std/src/sys/windows/args.rs @@ -313,6 +313,9 @@ pub(crate) fn make_bat_command_line( /// /// This is necessary because cmd.exe does not support verbatim paths. pub(crate) fn to_user_path(path: &Path) -> io::Result> { + from_wide_to_user_path(to_u16s(path)?) +} +pub(crate) fn from_wide_to_user_path(mut path: Vec) -> io::Result> { use crate::ptr; use crate::sys::windows::fill_utf16_buf; @@ -325,8 +328,6 @@ pub(crate) fn to_user_path(path: &Path) -> io::Result> { const N: u16 = b'N' as _; const C: u16 = b'C' as _; - let mut path = to_u16s(path)?; - // Early return if the path is too long to remove the verbatim prefix. const LEGACY_MAX_PATH: usize = 260; if path.len() > LEGACY_MAX_PATH { diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index ff05d31915c..fe052c8281b 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -542,8 +542,10 @@ impl File { // Turn `\??\` into `\\?\` (a verbatim path). subst[1] = b'\\' as u16; // Attempt to convert to a more user-friendly path. - let user = super::args::to_user_path(subst.iter().copied().chain([0]).collect())?; - Ok(PathBuf::from(OsString::from_wide(&user))) + let user = super::args::from_wide_to_user_path( + subst.iter().copied().chain([0]).collect(), + )?; + Ok(PathBuf::from(OsString::from_wide(&user.strip_suffix(&[0]).unwrap_or(&user)))) } else { Ok(PathBuf::from(OsString::from_wide(subst))) }