Skip to content

Commit b217479

Browse files
committed
Add WCStr
1 parent f1f6989 commit b217479

File tree

2 files changed

+51
-20
lines changed

2 files changed

+51
-20
lines changed

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

+21-18
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::sync::Arc;
1212
use crate::sys::handle::Handle;
1313
use crate::sys::pal::api::{self, WinError, set_file_information_by_handle};
1414
use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul};
15-
use crate::sys::path::maybe_verbatim;
15+
use crate::sys::path::{WCStr, maybe_verbatim};
1616
use crate::sys::time::SystemTime;
1717
use crate::sys::{Align8, c, cvt};
1818
use crate::sys_common::{AsInner, FromInner, IntoInner};
@@ -298,10 +298,12 @@ impl OpenOptions {
298298
impl File {
299299
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
300300
let path = maybe_verbatim(path)?;
301+
// SAFETY: maybe_verbatim returns null-terminated strings
302+
let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) };
301303
Self::open_native(&path, opts)
302304
}
303305

304-
fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result<File> {
306+
fn open_native(path: &WCStr, opts: &OpenOptions) -> io::Result<File> {
305307
let creation = opts.get_creation_mode()?;
306308
let handle = unsafe {
307309
c::CreateFileW(
@@ -1212,7 +1214,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
12121214
}
12131215
}
12141216

1215-
pub fn unlink(path: &[u16]) -> io::Result<()> {
1217+
pub fn unlink(path: &WCStr) -> io::Result<()> {
12161218
if unsafe { c::DeleteFileW(path.as_ptr()) } == 0 {
12171219
let err = api::get_last_error();
12181220
// if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove
@@ -1235,7 +1237,7 @@ pub fn unlink(path: &[u16]) -> io::Result<()> {
12351237
}
12361238
}
12371239

1238-
pub fn rename(old: &[u16], new: &[u16]) -> io::Result<()> {
1240+
pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> {
12391241
if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 {
12401242
let err = api::get_last_error();
12411243
// if `MoveFileExW` fails with ERROR_ACCESS_DENIED then try to move
@@ -1249,7 +1251,8 @@ pub fn rename(old: &[u16], new: &[u16]) -> io::Result<()> {
12491251

12501252
// Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation`
12511253
// This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
1252-
let Ok(new_len_without_nul_in_bytes): Result<u32, _> = ((new.len() - 1) * 2).try_into()
1254+
let Ok(new_len_without_nul_in_bytes): Result<u32, _> =
1255+
((new.count_bytes() - 1) * 2).try_into()
12531256
else {
12541257
return Err(err).io_result();
12551258
};
@@ -1278,7 +1281,7 @@ pub fn rename(old: &[u16], new: &[u16]) -> io::Result<()> {
12781281

12791282
new.as_ptr().copy_to_nonoverlapping(
12801283
(&raw mut (*file_rename_info).FileName).cast::<u16>(),
1281-
new.len(),
1284+
new.count_bytes(),
12821285
);
12831286
}
12841287

@@ -1305,12 +1308,12 @@ pub fn rename(old: &[u16], new: &[u16]) -> io::Result<()> {
13051308
Ok(())
13061309
}
13071310

1308-
pub fn rmdir(p: &[u16]) -> io::Result<()> {
1311+
pub fn rmdir(p: &WCStr) -> io::Result<()> {
13091312
cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
13101313
Ok(())
13111314
}
13121315

1313-
pub fn remove_dir_all(path: &[u16]) -> io::Result<()> {
1316+
pub fn remove_dir_all(path: &WCStr) -> io::Result<()> {
13141317
// Open a file or directory without following symlinks.
13151318
let mut opts = OpenOptions::new();
13161319
opts.access_mode(c::FILE_LIST_DIRECTORY);
@@ -1328,7 +1331,7 @@ pub fn remove_dir_all(path: &[u16]) -> io::Result<()> {
13281331
remove_dir_all_iterative(file).io_result()
13291332
}
13301333

1331-
pub fn readlink(path: &[u16]) -> io::Result<PathBuf> {
1334+
pub fn readlink(path: &WCStr) -> io::Result<PathBuf> {
13321335
// Open the link with no access mode, instead of generic read.
13331336
// By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
13341337
// this is needed for a common case.
@@ -1373,17 +1376,17 @@ pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()>
13731376
}
13741377

13751378
#[cfg(not(target_vendor = "uwp"))]
1376-
pub fn link(original: &[u16], link: &[u16]) -> io::Result<()> {
1379+
pub fn link(original: &WCStr, link: &WCStr) -> io::Result<()> {
13771380
cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
13781381
Ok(())
13791382
}
13801383

13811384
#[cfg(target_vendor = "uwp")]
1382-
pub fn link(_original: &[u16], _link: &[u16]) -> io::Result<()> {
1385+
pub fn link(_original: &WCStr, _link: &WCStr) -> io::Result<()> {
13831386
return Err(io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP"));
13841387
}
13851388

1386-
pub fn stat(path: &[u16]) -> io::Result<FileAttr> {
1389+
pub fn stat(path: &WCStr) -> io::Result<FileAttr> {
13871390
match metadata(path, ReparsePoint::Follow) {
13881391
Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => {
13891392
if let Ok(attrs) = lstat(path) {
@@ -1397,7 +1400,7 @@ pub fn stat(path: &[u16]) -> io::Result<FileAttr> {
13971400
}
13981401
}
13991402

1400-
pub fn lstat(path: &[u16]) -> io::Result<FileAttr> {
1403+
pub fn lstat(path: &WCStr) -> io::Result<FileAttr> {
14011404
metadata(path, ReparsePoint::Open)
14021405
}
14031406

@@ -1413,7 +1416,7 @@ impl ReparsePoint {
14131416
}
14141417
}
14151418

1416-
fn metadata(path: &[u16], reparse: ReparsePoint) -> io::Result<FileAttr> {
1419+
fn metadata(path: &WCStr, reparse: ReparsePoint) -> io::Result<FileAttr> {
14171420
let mut opts = OpenOptions::new();
14181421
// No read or write permissions are necessary
14191422
opts.access_mode(0);
@@ -1473,7 +1476,7 @@ fn metadata(path: &[u16], reparse: ReparsePoint) -> io::Result<FileAttr> {
14731476
}
14741477
}
14751478

1476-
pub fn set_perm(p: &[u16], perm: FilePermissions) -> io::Result<()> {
1479+
pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> {
14771480
unsafe {
14781481
cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
14791482
Ok(())
@@ -1489,7 +1492,7 @@ fn get_path(f: &File) -> io::Result<PathBuf> {
14891492
)
14901493
}
14911494

1492-
pub fn canonicalize(p: &[u16]) -> io::Result<PathBuf> {
1495+
pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
14931496
let mut opts = OpenOptions::new();
14941497
// No read or write permissions are necessary
14951498
opts.access_mode(0);
@@ -1499,7 +1502,7 @@ pub fn canonicalize(p: &[u16]) -> io::Result<PathBuf> {
14991502
get_path(&f)
15001503
}
15011504

1502-
pub fn copy(from: &[u16], to: &[u16]) -> io::Result<u64> {
1505+
pub fn copy(from: &WCStr, to: &WCStr) -> io::Result<u64> {
15031506
unsafe extern "system" fn callback(
15041507
_TotalFileSize: i64,
15051508
_TotalBytesTransferred: i64,
@@ -1612,7 +1615,7 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> {
16121615
}
16131616

16141617
// Try to see if a file exists but, unlike `exists`, report I/O errors.
1615-
pub fn exists(path: &[u16]) -> io::Result<bool> {
1618+
pub fn exists(path: &WCStr) -> io::Result<bool> {
16161619
// Open the file to ensure any symlinks are followed to their target.
16171620
let mut opts = OpenOptions::new();
16181621
// No read, write, etc access rights are needed.

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

+30-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,38 @@ mod tests;
1010
pub const MAIN_SEP_STR: &str = "\\";
1111
pub const MAIN_SEP: char = '\\';
1212

13+
/// A null terminated wide string.
14+
#[repr(transparent)]
15+
pub struct WCStr([u16]);
16+
17+
impl WCStr {
18+
/// Convert a slice to a WCStr without checks.
19+
///
20+
/// Though it is memory safe, the slice should also not contain interior nulls
21+
/// as this may lead to unwanted truncation.
22+
///
23+
/// # Safety
24+
///
25+
/// The slice must end in a null.
26+
pub unsafe fn from_wchars_with_null_unchecked(s: &[u16]) -> &Self {
27+
unsafe { &*(s as *const [u16] as *const Self) }
28+
}
29+
30+
pub fn as_ptr(&self) -> *const u16 {
31+
self.0.as_ptr()
32+
}
33+
34+
pub fn count_bytes(&self) -> usize {
35+
self.0.len()
36+
}
37+
}
38+
1339
#[inline]
14-
pub fn with_native_path<T>(path: &Path, f: &dyn Fn(&[u16]) -> io::Result<T>) -> io::Result<T> {
40+
pub fn with_native_path<T>(path: &Path, f: &dyn Fn(&WCStr) -> io::Result<T>) -> io::Result<T> {
1541
let path = maybe_verbatim(path)?;
16-
f(&path)
42+
// SAFETY: maybe_verbatim returns null-terminated strings
43+
let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) };
44+
f(path)
1745
}
1846

1947
#[inline]

0 commit comments

Comments
 (0)