Skip to content

Commit

Permalink
Rollup merge of rust-lang#107978 - ChrisDenton:nt-to-win32, r=m-ou-se
Browse files Browse the repository at this point in the history
Correctly convert an NT path to a Win32 path in `read_link`

This can be done by simply changing the `\??\` prefix to `\\?\`.

Currently it 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).

r? libs
  • Loading branch information
Dylan-DPC authored May 3, 2023
2 parents cad92b4 + 178f7bb commit 02d8fa7
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 12 deletions.
6 changes: 5 additions & 1 deletion library/std/src/fs/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand All @@ -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;
Expand Down
27 changes: 16 additions & 11 deletions library/std/src/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ impl File {
fn reparse_point(
&self,
space: &mut Align8<[MaybeUninit<u8>]>,
) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> {
unsafe {
let mut bytes = 0;
cvt({
Expand All @@ -496,7 +496,7 @@ impl File {
)
})?;
const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
Ok((bytes, space.0.as_mut_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
}
}

Expand All @@ -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::<u16>(),
ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
(*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::<u16>(),
ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
(*info).SubstituteNameOffset / 2,
(*info).SubstituteNameLength / 2,
false,
Expand All @@ -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)))
}
}

Expand Down

0 comments on commit 02d8fa7

Please sign in to comment.