Skip to content

Commit e60067e

Browse files
authored
Rollup merge of rust-lang#107978 - ChrisDenton:nt-to-win32, r=m-ou-se
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
2 parents 392b1ce + 109a47f commit e60067e

File tree

3 files changed

+26
-14
lines changed

3 files changed

+26
-14
lines changed

library/std/src/fs/tests.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,7 @@ fn symlink_noexist() {
919919

920920
#[test]
921921
fn read_link() {
922+
let tmpdir = tmpdir();
922923
if cfg!(windows) {
923924
// directory symlink
924925
assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData"));
@@ -933,8 +934,11 @@ fn read_link() {
933934
Path::new(r"C:\Users")
934935
);
935936
}
937+
// Check that readlink works with non-drive paths on Windows.
938+
let link = tmpdir.join("link_unc");
939+
check!(symlink_dir(r"\\localhost\c$\", &link));
940+
assert_eq!(check!(fs::read_link(&link)), Path::new(r"\\localhost\c$\"));
936941
}
937-
let tmpdir = tmpdir();
938942
let link = tmpdir.join("link");
939943
if !got_symlink_permission(&tmpdir) {
940944
return;

library/std/src/sys/windows/args.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ pub(crate) fn make_bat_command_line(
313313
///
314314
/// This is necessary because cmd.exe does not support verbatim paths.
315315
pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
316+
from_wide_to_user_path(to_u16s(path)?)
317+
}
318+
pub(crate) fn from_wide_to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
316319
use crate::ptr;
317320
use crate::sys::windows::fill_utf16_buf;
318321

@@ -325,8 +328,6 @@ pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
325328
const N: u16 = b'N' as _;
326329
const C: u16 = b'C' as _;
327330

328-
let mut path = to_u16s(path)?;
329-
330331
// Early return if the path is too long to remove the verbatim prefix.
331332
const LEGACY_MAX_PATH: usize = 260;
332333
if path.len() > LEGACY_MAX_PATH {

library/std/src/sys/windows/fs.rs

+18-11
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ impl File {
477477
fn reparse_point(
478478
&self,
479479
space: &mut Align8<[MaybeUninit<u8>]>,
480-
) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
480+
) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> {
481481
unsafe {
482482
let mut bytes = 0;
483483
cvt({
@@ -496,7 +496,7 @@ impl File {
496496
)
497497
})?;
498498
const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
499-
Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
499+
Ok((bytes, space.0.as_mut_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
500500
}
501501
}
502502

@@ -506,22 +506,22 @@ impl File {
506506
unsafe {
507507
let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
508508
c::IO_REPARSE_TAG_SYMLINK => {
509-
let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
510-
ptr::addr_of!((*buf).rest).cast();
509+
let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER =
510+
ptr::addr_of_mut!((*buf).rest).cast();
511511
assert!(info.is_aligned());
512512
(
513-
ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
513+
ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
514514
(*info).SubstituteNameOffset / 2,
515515
(*info).SubstituteNameLength / 2,
516516
(*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
517517
)
518518
}
519519
c::IO_REPARSE_TAG_MOUNT_POINT => {
520-
let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
521-
ptr::addr_of!((*buf).rest).cast();
520+
let info: *mut c::MOUNT_POINT_REPARSE_BUFFER =
521+
ptr::addr_of_mut!((*buf).rest).cast();
522522
assert!(info.is_aligned());
523523
(
524-
ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
524+
ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
525525
(*info).SubstituteNameOffset / 2,
526526
(*info).SubstituteNameLength / 2,
527527
false,
@@ -535,13 +535,20 @@ impl File {
535535
}
536536
};
537537
let subst_ptr = path_buffer.add(subst_off.into());
538-
let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize);
538+
let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize);
539539
// Absolute paths start with an NT internal namespace prefix `\??\`
540540
// We should not let it leak through.
541541
if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
542-
subst = &subst[4..];
542+
// Turn `\??\` into `\\?\` (a verbatim path).
543+
subst[1] = b'\\' as u16;
544+
// Attempt to convert to a more user-friendly path.
545+
let user = super::args::from_wide_to_user_path(
546+
subst.iter().copied().chain([0]).collect(),
547+
)?;
548+
Ok(PathBuf::from(OsString::from_wide(&user.strip_suffix(&[0]).unwrap_or(&user))))
549+
} else {
550+
Ok(PathBuf::from(OsString::from_wide(subst)))
543551
}
544-
Ok(PathBuf::from(OsString::from_wide(subst)))
545552
}
546553
}
547554

0 commit comments

Comments
 (0)