Rollup merge of #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
This commit is contained in:
commit
068807a5b8
@ -919,6 +919,7 @@ fn symlink_noexist() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn read_link() {
|
fn read_link() {
|
||||||
|
let tmpdir = tmpdir();
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
// directory symlink
|
// directory symlink
|
||||||
assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData"));
|
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")
|
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");
|
let link = tmpdir.join("link");
|
||||||
if !got_symlink_permission(&tmpdir) {
|
if !got_symlink_permission(&tmpdir) {
|
||||||
return;
|
return;
|
||||||
|
@ -313,6 +313,9 @@ pub(crate) fn make_bat_command_line(
|
|||||||
///
|
///
|
||||||
/// This is necessary because cmd.exe does not support verbatim paths.
|
/// This is necessary because cmd.exe does not support verbatim paths.
|
||||||
pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
|
pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
|
||||||
|
from_wide_to_user_path(to_u16s(path)?)
|
||||||
|
}
|
||||||
|
pub(crate) fn from_wide_to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
|
||||||
use crate::ptr;
|
use crate::ptr;
|
||||||
use crate::sys::windows::fill_utf16_buf;
|
use crate::sys::windows::fill_utf16_buf;
|
||||||
|
|
||||||
@ -325,8 +328,6 @@ pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
|
|||||||
const N: u16 = b'N' as _;
|
const N: u16 = b'N' as _;
|
||||||
const C: u16 = b'C' 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.
|
// Early return if the path is too long to remove the verbatim prefix.
|
||||||
const LEGACY_MAX_PATH: usize = 260;
|
const LEGACY_MAX_PATH: usize = 260;
|
||||||
if path.len() > LEGACY_MAX_PATH {
|
if path.len() > LEGACY_MAX_PATH {
|
||||||
|
@ -477,7 +477,7 @@ impl File {
|
|||||||
fn reparse_point(
|
fn reparse_point(
|
||||||
&self,
|
&self,
|
||||||
space: &mut Align8<[MaybeUninit<u8>]>,
|
space: &mut Align8<[MaybeUninit<u8>]>,
|
||||||
) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
|
) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut bytes = 0;
|
let mut bytes = 0;
|
||||||
cvt({
|
cvt({
|
||||||
@ -496,7 +496,7 @@ impl File {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
|
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>()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,22 +506,22 @@ impl File {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
|
let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
|
||||||
c::IO_REPARSE_TAG_SYMLINK => {
|
c::IO_REPARSE_TAG_SYMLINK => {
|
||||||
let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
|
let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER =
|
||||||
ptr::addr_of!((*buf).rest).cast();
|
ptr::addr_of_mut!((*buf).rest).cast();
|
||||||
assert!(info.is_aligned());
|
assert!(info.is_aligned());
|
||||||
(
|
(
|
||||||
ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
|
ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
|
||||||
(*info).SubstituteNameOffset / 2,
|
(*info).SubstituteNameOffset / 2,
|
||||||
(*info).SubstituteNameLength / 2,
|
(*info).SubstituteNameLength / 2,
|
||||||
(*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
|
(*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
c::IO_REPARSE_TAG_MOUNT_POINT => {
|
c::IO_REPARSE_TAG_MOUNT_POINT => {
|
||||||
let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
|
let info: *mut c::MOUNT_POINT_REPARSE_BUFFER =
|
||||||
ptr::addr_of!((*buf).rest).cast();
|
ptr::addr_of_mut!((*buf).rest).cast();
|
||||||
assert!(info.is_aligned());
|
assert!(info.is_aligned());
|
||||||
(
|
(
|
||||||
ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
|
ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
|
||||||
(*info).SubstituteNameOffset / 2,
|
(*info).SubstituteNameOffset / 2,
|
||||||
(*info).SubstituteNameLength / 2,
|
(*info).SubstituteNameLength / 2,
|
||||||
false,
|
false,
|
||||||
@ -535,13 +535,20 @@ impl File {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let subst_ptr = path_buffer.add(subst_off.into());
|
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 `\??\`
|
// Absolute paths start with an NT internal namespace prefix `\??\`
|
||||||
// We should not let it leak through.
|
// We should not let it leak through.
|
||||||
if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
|
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::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)))
|
||||||
}
|
}
|
||||||
Ok(PathBuf::from(OsString::from_wide(subst)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user