Skip to content

Commit a8b37df

Browse files
committed
Windows implementation of NativePath
1 parent 59c426c commit a8b37df

File tree

7 files changed

+176
-31
lines changed

7 files changed

+176
-31
lines changed

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

Lines changed: 36 additions & 0 deletions
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

Lines changed: 7 additions & 5 deletions
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

Lines changed: 70 additions & 18 deletions
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_native_path(fs::lstat)
38+
}
39+
pub(crate) fn metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
40+
path.with_native_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_native_path(fs::canonicalize)
65+
}
66+
pub(crate) fn remove_dir_all<P: AsPath>(path: P) -> io::Result<()> {
67+
path.with_native_path(fs::remove_dir_all)
68+
}
69+
pub(crate) fn read_link<P: AsPath>(path: P) -> io::Result<PathBuf> {
70+
path.with_native_path(fs::readlink)
71+
}
72+
pub(crate) fn try_exists<P: AsPath>(path: P) -> io::Result<bool> {
73+
path.with_native_path(fs::try_exists)
74+
}
75+
}
76+
2277
pub struct File {
2378
handle: Handle,
2479
}
@@ -294,8 +349,7 @@ impl OpenOptions {
294349
}
295350

296351
impl File {
297-
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
298-
let path = maybe_verbatim(path)?;
352+
pub fn open_native(path: &NativePath, opts: &OpenOptions) -> io::Result<File> {
299353
let creation = opts.get_creation_mode()?;
300354
let handle = unsafe {
301355
c::CreateFileW(
@@ -1132,16 +1186,16 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
11321186
}
11331187

11341188
/// Open a file or directory without following symlinks.
1135-
fn open_link(path: &Path, access_mode: u32) -> io::Result<File> {
1189+
fn open_link(path: &NativePath, access_mode: u32) -> io::Result<File> {
11361190
let mut opts = OpenOptions::new();
11371191
opts.access_mode(access_mode);
11381192
// `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
11391193
// `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
11401194
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
1141-
File::open(path, &opts)
1195+
File::open_native(path, &opts)
11421196
}
11431197

1144-
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
1198+
pub fn remove_dir_all(path: &NativePath) -> io::Result<()> {
11451199
let file = open_link(path, c::DELETE | c::FILE_LIST_DIRECTORY)?;
11461200

11471201
// Test if the file is not a directory or a symlink to a directory.
@@ -1241,14 +1295,14 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io
12411295
Ok(())
12421296
}
12431297

1244-
pub fn readlink(path: &Path) -> io::Result<PathBuf> {
1298+
pub fn readlink(path: &NativePath) -> io::Result<PathBuf> {
12451299
// Open the link with no access mode, instead of generic read.
12461300
// By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
12471301
// this is needed for a common case.
12481302
let mut opts = OpenOptions::new();
12491303
opts.access_mode(0);
12501304
opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
1251-
let file = File::open(path, &opts)?;
1305+
let file = File::open_native(path, &opts)?;
12521306
file.readlink()
12531307
}
12541308

@@ -1301,7 +1355,7 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
13011355
));
13021356
}
13031357

1304-
pub fn stat(path: &Path) -> io::Result<FileAttr> {
1358+
pub fn stat(path: &NativePath) -> io::Result<FileAttr> {
13051359
match metadata(path, ReparsePoint::Follow) {
13061360
Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => {
13071361
if let Ok(attrs) = lstat(path) {
@@ -1315,7 +1369,7 @@ pub fn stat(path: &Path) -> io::Result<FileAttr> {
13151369
}
13161370
}
13171371

1318-
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
1372+
pub fn lstat(path: &NativePath) -> io::Result<FileAttr> {
13191373
metadata(path, ReparsePoint::Open)
13201374
}
13211375

@@ -1331,7 +1385,7 @@ impl ReparsePoint {
13311385
}
13321386
}
13331387

1334-
fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
1388+
fn metadata(path: &NativePath, reparse: ReparsePoint) -> io::Result<FileAttr> {
13351389
let mut opts = OpenOptions::new();
13361390
// No read or write permissions are necessary
13371391
opts.access_mode(0);
@@ -1340,7 +1394,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
13401394
// Attempt to open the file normally.
13411395
// If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`.
13421396
// If the fallback fails for any reason we return the original error.
1343-
match File::open(path, &opts) {
1397+
match File::open_native(path, &opts) {
13441398
Ok(file) => file.file_attr(),
13451399
Err(e)
13461400
if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)]
@@ -1353,8 +1407,6 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
13531407
// However, there are special system files, such as
13541408
// `C:\hiberfil.sys`, that are locked in a way that denies even that.
13551409
unsafe {
1356-
let path = maybe_verbatim(path)?;
1357-
13581410
// `FindFirstFileW` accepts wildcard file names.
13591411
// Fortunately wildcards are not valid file names and
13601412
// `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
@@ -1403,13 +1455,13 @@ fn get_path(f: &File) -> io::Result<PathBuf> {
14031455
)
14041456
}
14051457

1406-
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
1458+
pub fn canonicalize(p: &NativePath) -> io::Result<PathBuf> {
14071459
let mut opts = OpenOptions::new();
14081460
// No read or write permissions are necessary
14091461
opts.access_mode(0);
14101462
// This flag is so we can open directories too
14111463
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
1412-
let f = File::open(p, &opts)?;
1464+
let f = File::open_native(p, &opts)?;
14131465
get_path(&f)
14141466
}
14151467

@@ -1467,7 +1519,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
14671519
let mut opts = OpenOptions::new();
14681520
opts.write(true);
14691521
opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
1470-
let f = File::open(junction, &opts)?;
1522+
let f = File::open_native(&maybe_verbatim(junction)?, &opts)?;
14711523
let h = f.as_inner().as_raw_handle();
14721524
unsafe {
14731525
let mut data =
@@ -1525,14 +1577,14 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
15251577
}
15261578

15271579
// Try to see if a file exists but, unlike `exists`, report I/O errors.
1528-
pub fn try_exists(path: &Path) -> io::Result<bool> {
1580+
pub fn try_exists(path: &NativePath) -> io::Result<bool> {
15291581
// Open the file to ensure any symlinks are followed to their target.
15301582
let mut opts = OpenOptions::new();
15311583
// No read, write, etc access rights are needed.
15321584
opts.access_mode(0);
15331585
// Backup semantics enables opening directories as well as files.
15341586
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
1535-
match File::open(path, &opts) {
1587+
match File::open_native(path, &opts) {
15361588
Err(e) => match e.kind() {
15371589
// The file definitely does not exist
15381590
io::ErrorKind::NotFound => Ok(false),

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::os::windows::prelude::*;
33
use crate::ffi::OsStr;
44
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
55
use crate::mem;
6-
use crate::path::Path;
76
use crate::ptr;
87
use crate::slice;
98
use crate::sync::atomic::AtomicUsize;
@@ -12,6 +11,7 @@ use crate::sys::c;
1211
use crate::sys::fs::{File, OpenOptions};
1312
use crate::sys::handle::Handle;
1413
use crate::sys::hashmap_random_keys;
14+
use crate::sys::path::NativePathBuf;
1515
use crate::sys_common::{FromInner, IntoInner};
1616

1717
////////////////////////////////////////////////////////////////////////////////
@@ -161,7 +161,8 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
161161
bInheritHandle: their_handle_inheritable as i32,
162162
};
163163
opts.security_attributes(&mut sa);
164-
let theirs = File::open(Path::new(&name), &opts)?;
164+
let path = NativePathBuf::from_os_str(&name)?;
165+
let theirs = File::open_native(&path, &opts)?;
165166
let theirs = AnonPipe { inner: theirs.into_inner() };
166167

167168
Ok(Pipes {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::sys::c::{self, NonZeroDWORD, EXIT_FAILURE, EXIT_SUCCESS};
2323
use crate::sys::cvt;
2424
use crate::sys::fs::{File, OpenOptions};
2525
use crate::sys::handle::Handle;
26-
use crate::sys::path;
26+
use crate::sys::path::{self, NativePathBuf};
2727
use crate::sys::pipe::{self, AnonPipe};
2828
use crate::sys::stdio;
2929
use crate::sys_common::process::{CommandEnv, CommandEnvs};
@@ -597,7 +597,8 @@ impl Stdio {
597597
opts.read(stdio_id == c::STD_INPUT_HANDLE);
598598
opts.write(stdio_id != c::STD_INPUT_HANDLE);
599599
opts.security_attributes(&mut sa);
600-
File::open(Path::new(r"\\.\NUL"), &opts).map(|file| file.into_inner())
600+
File::open_native(&NativePathBuf::from_os_str(r"\\.\NUL")?, &opts)
601+
.map(|file| file.into_inner())
601602
}
602603
}
603604
}

0 commit comments

Comments
 (0)