about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChris Denton <chris@chrisdenton.dev>2023-02-12 12:05:56 +0000
committerChris Denton <chris@chrisdenton.dev>2023-05-03 10:24:56 +0100
commit6e377849c09a310b6eef50ebd91c1f014d41ab73 (patch)
tree1b17781bad1484f5b797202fd344f265a4175ac3
parentcad92b4c979760b088ffd18e6c89801668c944f2 (diff)
downloadrust-6e377849c09a310b6eef50ebd91c1f014d41ab73.tar.gz
rust-6e377849c09a310b6eef50ebd91c1f014d41ab73.zip
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).
-rw-r--r--library/std/src/fs/tests.rs6
-rw-r--r--library/std/src/sys/windows/fs.rs27
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<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({
@@ -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>()))
         }
     }
 
@@ -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,
@@ -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)))
         }
     }