Skip to content

Commit bf9e946

Browse files
committed
Windows implementation of NativePath
1 parent 5cf0aa6 commit bf9e946

File tree

4 files changed

+140
-7
lines changed

4 files changed

+140
-7
lines changed

library/std/src/os/windows/ffi.rs

+36
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
#![stable(feature = "rust1", since = "1.0.0")]
5555

5656
use crate::ffi::{OsStr, OsString};
57+
#[cfg(not(target_os = "uefi"))]
58+
use crate::path::NativePath;
5759
use crate::sealed::Sealed;
5860
use crate::sys::os_str::Buf;
5961
use crate::sys_common::wtf8::Wtf8Buf;
@@ -134,3 +136,37 @@ impl OsStrExt for OsStr {
134136
self.as_inner().inner.encode_wide()
135137
}
136138
}
139+
140+
/// On Windows `NativePath` wraps a wide string for use in filesystem function calls.
141+
/// These strings are `&[u16]` slices.
142+
///
143+
/// # Wide strings
144+
///
145+
/// Filesystem paths in Windows are encoded as UTF-16 strings.
146+
/// However, because the kernel does not verify validity this may contain invalid UTF-16.
147+
/// Therefore we use the term "wide string" for potentially invalid UTF-16.
148+
#[cfg(windows)]
149+
#[unstable(feature = "fs_native_path", issue = "108979")]
150+
pub trait NativePathExt: Sealed {
151+
/// Wrap a Windows wide string as a `NativePath`.
152+
/// The `wide` string must be null terminated and must not otherwise contain nulls.
153+
fn from_wide(wide: &[u16]) -> &NativePath;
154+
/// Wrap a Windows wide string as a `NativePath` without checking for null termination or internal nulls.
155+
unsafe fn from_wide_unchecked(wide: &[u16]) -> &NativePath;
156+
/// Unwrap the `NativePath` to return the inner wide string.
157+
fn into_wide(&self) -> &[u16];
158+
}
159+
#[cfg(windows)]
160+
#[unstable(feature = "fs_native_path", issue = "108979")]
161+
impl NativePathExt for NativePath {
162+
fn from_wide(wide: &[u16]) -> &NativePath {
163+
assert_eq!(crate::sys::unrolled_find_u16s(0, wide), Some(wide.len().saturating_sub(1)));
164+
unsafe { Self::from_wide_unchecked(wide) }
165+
}
166+
unsafe fn from_wide_unchecked(wide: &[u16]) -> &NativePath {
167+
&*(wide as *const [u16] as *const Self)
168+
}
169+
fn into_wide(&self) -> &[u16] {
170+
unsafe { &*(self as *const Self as *const [u16]) }
171+
}
172+
}

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

+7-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use crate::fs::{self, Metadata, OpenOptions};
88
use crate::io;
9-
use crate::path::Path;
9+
use crate::path::AsPath;
1010
use crate::sealed::Sealed;
1111
use crate::sys;
1212
use crate::sys_common::{AsInner, AsInnerMut, IntoInner};
@@ -578,8 +578,9 @@ impl FileTimesExt for fs::FileTimes {
578578
///
579579
/// [symlink-security]: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links
580580
#[stable(feature = "symlink", since = "1.1.0")]
581-
pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
582-
sys::fs::symlink_inner(original.as_ref(), link.as_ref(), false)
581+
pub fn symlink_file<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
582+
original
583+
.with_path(|original| link.with_path(|link| sys::fs::symlink_inner(original, link, false)))
583584
}
584585

585586
/// Creates a new symlink to a directory on the filesystem.
@@ -617,6 +618,7 @@ pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io:
617618
///
618619
/// [symlink-security]: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links
619620
#[stable(feature = "symlink", since = "1.1.0")]
620-
pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
621-
sys::fs::symlink_inner(original.as_ref(), link.as_ref(), true)
621+
pub fn symlink_dir<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
622+
original
623+
.with_path(|original| link.with_path(|link| sys::fs::symlink_inner(original, link, true)))
622624
}

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

+60-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::ptr;
1111
use crate::slice;
1212
use crate::sync::Arc;
1313
use crate::sys::handle::Handle;
14+
use crate::sys::path::NativePath;
1415
use crate::sys::time::SystemTime;
1516
use crate::sys::{c, cvt, Align8};
1617
use crate::sys_common::{AsInner, FromInner, IntoInner};
@@ -19,6 +20,60 @@ use crate::thread;
1920
use super::{api, to_u16s, IoResult};
2021
use crate::sys::path::maybe_verbatim;
2122

23+
/// The crate-public interface
24+
pub(crate) mod fs_imp {
25+
use crate::io;
26+
use crate::path::AsPath;
27+
use crate::path::PathBuf;
28+
use crate::sys::fs;
29+
pub(crate) use crate::sys::fs::{
30+
DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
31+
ReadDir,
32+
};
33+
pub(crate) fn remove_file<P: AsPath>(path: P) -> io::Result<()> {
34+
path.with_path(fs::unlink)
35+
}
36+
pub(crate) fn symlink_metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
37+
path.with_path(fs::lstat)
38+
}
39+
pub(crate) fn metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
40+
path.with_path(fs::stat)
41+
}
42+
pub(crate) fn rename<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<()> {
43+
from.with_path(|from| to.with_path(|to| fs::rename(from, to)))
44+
}
45+
pub(crate) fn hard_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
46+
original.with_path(|original| link.with_path(|link| fs::link(original, link)))
47+
}
48+
pub(crate) fn soft_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
49+
original.with_path(|original| link.with_path(|link| fs::symlink(original, link)))
50+
}
51+
pub(crate) fn remove_dir<P: AsPath>(path: P) -> io::Result<()> {
52+
path.with_path(fs::rmdir)
53+
}
54+
pub(crate) fn read_dir<P: AsPath>(path: P) -> io::Result<ReadDir> {
55+
path.with_path(fs::readdir)
56+
}
57+
pub(crate) fn set_permissions<P: AsPath>(path: P, perms: FilePermissions) -> io::Result<()> {
58+
path.with_path(|path| fs::set_perm(path, perms))
59+
}
60+
pub(crate) fn copy<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<u64> {
61+
from.with_path(|from| to.with_path(|to| fs::copy(from, to)))
62+
}
63+
pub(crate) fn canonicalize<P: AsPath>(path: P) -> io::Result<PathBuf> {
64+
path.with_path(fs::canonicalize)
65+
}
66+
pub(crate) fn remove_dir_all<P: AsPath>(path: P) -> io::Result<()> {
67+
path.with_path(fs::remove_dir_all)
68+
}
69+
pub(crate) fn read_link<P: AsPath>(path: P) -> io::Result<PathBuf> {
70+
path.with_path(fs::readlink)
71+
}
72+
pub(crate) fn try_exists<P: AsPath>(path: P) -> io::Result<bool> {
73+
path.with_path(fs::try_exists)
74+
}
75+
}
76+
2277
pub struct File {
2378
handle: Handle,
2479
}
@@ -294,8 +349,12 @@ impl OpenOptions {
294349
}
295350

296351
impl File {
297-
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
352+
pub(super) fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
298353
let path = maybe_verbatim(path)?;
354+
let path = unsafe { NativePath::new_unchecked(&path) };
355+
Self::open_native(path, opts)
356+
}
357+
pub fn open_native(path: &NativePath, opts: &OpenOptions) -> io::Result<File> {
299358
let creation = opts.get_creation_mode()?;
300359
let handle = unsafe {
301360
c::CreateFileW(

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

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::ffi::{OsStr, OsString};
22
use crate::io;
3-
use crate::path::{Path, PathBuf, Prefix};
3+
use crate::path::{AsPath, Path, PathBuf, Prefix};
44
use crate::ptr;
55
use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s};
66

@@ -10,6 +10,42 @@ mod tests;
1010
pub const MAIN_SEP_STR: &str = "\\";
1111
pub const MAIN_SEP: char = '\\';
1212

13+
/// A null-terminated `[u16]` string.
14+
#[unstable(feature = "fs_native_path_internals", issue = "none")]
15+
#[repr(transparent)]
16+
#[derive(Debug)]
17+
pub struct NativePath(pub [u16]);
18+
impl NativePath {
19+
pub(crate) unsafe fn new_unchecked(native: &[u16]) -> &Self {
20+
unsafe { &*(native as *const [u16] as *const Self) }
21+
}
22+
pub(crate) fn as_ptr(&self) -> *const u16 {
23+
self.0.as_ptr()
24+
}
25+
}
26+
27+
#[unstable(feature = "fs_native_path_internals", issue = "none")]
28+
impl<P: AsRef<Path>> AsPath for P {
29+
fn with_path<T, F: FnOnce(&Path) -> io::Result<T>>(self, f: F) -> io::Result<T> {
30+
f(self.as_ref())
31+
}
32+
fn with_native_path<T, F: Fn(&NativePath) -> io::Result<T>>(self, f: F) -> io::Result<T> {
33+
let path = maybe_verbatim(self.as_ref())?;
34+
f(unsafe { NativePath::new_unchecked(&path) })
35+
}
36+
}
37+
38+
#[unstable(feature = "fs_native_path_internals", issue = "none")]
39+
impl AsPath for &crate::path::NativePath {
40+
fn with_path<T, F: FnOnce(&Path) -> io::Result<T>>(self, f: F) -> io::Result<T> {
41+
let path = os2path(&self.0.0[..self.0.0.len().saturating_sub(1)]);
42+
f(&path)
43+
}
44+
fn with_native_path<T, F: Fn(&NativePath) -> io::Result<T>>(self, f: F) -> io::Result<T> {
45+
f(&self.0)
46+
}
47+
}
48+
1349
#[inline]
1450
pub fn is_sep_byte(b: u8) -> bool {
1551
b == b'/' || b == b'\\'

0 commit comments

Comments
 (0)