From 1376c5a617be5c25655d0d7cb63e3beaa5a6e026 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:20:39 +0200 Subject: Merging upstream version 1.70.0+dfsg1. Signed-off-by: Daniel Baumann --- library/std/src/sys/windows/args.rs | 23 ++++++--- library/std/src/sys/windows/c.rs | 87 +++++++++++++++------------------ library/std/src/sys/windows/c/errors.rs | 2 +- library/std/src/sys/windows/fs.rs | 46 +++++++++++++---- library/std/src/sys/windows/handle.rs | 9 ++++ library/std/src/sys/windows/net.rs | 28 ++++++++--- library/std/src/sys/windows/path.rs | 65 +++++++++++++++--------- library/std/src/sys/windows/pipe.rs | 24 ++++++++- library/std/src/sys/windows/process.rs | 12 ++--- 9 files changed, 191 insertions(+), 105 deletions(-) (limited to 'library/std/src/sys/windows') diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs index 30356fa85..43c0cdb65 100644 --- a/library/std/src/sys/windows/args.rs +++ b/library/std/src/sys/windows/args.rs @@ -11,10 +11,11 @@ use crate::fmt; use crate::io; use crate::num::NonZeroU16; use crate::os::windows::prelude::*; -use crate::path::PathBuf; -use crate::sys::c; +use crate::path::{Path, PathBuf}; +use crate::sys::path::get_long_path; use crate::sys::process::ensure_no_nuls; use crate::sys::windows::os::current_exe; +use crate::sys::{c, to_u16s}; use crate::sys_common::wstr::WStrUnits; use crate::vec; @@ -311,7 +312,7 @@ pub(crate) fn make_bat_command_line( /// Takes a path and tries to return a non-verbatim path. /// /// This is necessary because cmd.exe does not support verbatim paths. -pub(crate) fn to_user_path(mut path: Vec) -> io::Result> { +pub(crate) fn to_user_path(path: &Path) -> io::Result> { use crate::ptr; use crate::sys::windows::fill_utf16_buf; @@ -324,6 +325,8 @@ pub(crate) fn to_user_path(mut path: Vec) -> io::Result> { const N: u16 = b'N' 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. const LEGACY_MAX_PATH: usize = 260; if path.len() > LEGACY_MAX_PATH { @@ -337,7 +340,13 @@ pub(crate) fn to_user_path(mut path: Vec) -> io::Result> { fill_utf16_buf( |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()), |full_path: &[u16]| { - if full_path == &path[4..path.len() - 1] { full_path.into() } else { path } + if full_path == &path[4..path.len() - 1] { + let mut path: Vec = full_path.into(); + path.push(0); + path + } else { + path + } }, ) }, @@ -350,7 +359,9 @@ pub(crate) fn to_user_path(mut path: Vec) -> io::Result> { |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()), |full_path: &[u16]| { if full_path == &path[6..path.len() - 1] { - full_path.into() + let mut path: Vec = full_path.into(); + path.push(0); + path } else { // Restore the 'C' in "UNC". path[6] = b'C' as u16; @@ -360,6 +371,6 @@ pub(crate) fn to_user_path(mut path: Vec) -> io::Result> { ) }, // For everything else, leave the path unchanged. - _ => Ok(path), + _ => get_long_path(path, false), } } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 5d150eca0..1f4092ad7 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -296,7 +296,6 @@ pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _; pub const STATUS_PENDING: NTSTATUS = 0x103 as _; pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _; -pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _; // Equivalent to the `NT_SUCCESS` C preprocessor macro. // See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values @@ -1282,6 +1281,46 @@ extern "system" { ) -> NTSTATUS; } +#[link(name = "ntdll")] +extern "system" { + pub fn NtCreateFile( + FileHandle: *mut HANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: *const OBJECT_ATTRIBUTES, + IoStatusBlock: *mut IO_STATUS_BLOCK, + AllocationSize: *mut i64, + FileAttributes: ULONG, + ShareAccess: ULONG, + CreateDisposition: ULONG, + CreateOptions: ULONG, + EaBuffer: *mut c_void, + EaLength: ULONG, + ) -> NTSTATUS; + pub fn NtReadFile( + FileHandle: BorrowedHandle<'_>, + Event: HANDLE, + ApcRoutine: Option, + ApcContext: *mut c_void, + IoStatusBlock: &mut IO_STATUS_BLOCK, + Buffer: *mut crate::mem::MaybeUninit, + Length: ULONG, + ByteOffset: Option<&LARGE_INTEGER>, + Key: Option<&ULONG>, + ) -> NTSTATUS; + pub fn NtWriteFile( + FileHandle: BorrowedHandle<'_>, + Event: HANDLE, + ApcRoutine: Option, + ApcContext: *mut c_void, + IoStatusBlock: &mut IO_STATUS_BLOCK, + Buffer: *const u8, + Length: ULONG, + ByteOffset: Option<&LARGE_INTEGER>, + Key: Option<&ULONG>, + ) -> NTSTATUS; + pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> ULONG; +} + // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. compat_fn_with_fallback! { @@ -1322,52 +1361,6 @@ compat_fn_optional! { compat_fn_with_fallback! { pub static NTDLL: &CStr = ansi_str!("ntdll"); - pub fn NtCreateFile( - FileHandle: *mut HANDLE, - DesiredAccess: ACCESS_MASK, - ObjectAttributes: *const OBJECT_ATTRIBUTES, - IoStatusBlock: *mut IO_STATUS_BLOCK, - AllocationSize: *mut i64, - FileAttributes: ULONG, - ShareAccess: ULONG, - CreateDisposition: ULONG, - CreateOptions: ULONG, - EaBuffer: *mut c_void, - EaLength: ULONG - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - pub fn NtReadFile( - FileHandle: BorrowedHandle<'_>, - Event: HANDLE, - ApcRoutine: Option, - ApcContext: *mut c_void, - IoStatusBlock: &mut IO_STATUS_BLOCK, - Buffer: *mut crate::mem::MaybeUninit, - Length: ULONG, - ByteOffset: Option<&LARGE_INTEGER>, - Key: Option<&ULONG> - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - pub fn NtWriteFile( - FileHandle: BorrowedHandle<'_>, - Event: HANDLE, - ApcRoutine: Option, - ApcContext: *mut c_void, - IoStatusBlock: &mut IO_STATUS_BLOCK, - Buffer: *const u8, - Length: ULONG, - ByteOffset: Option<&LARGE_INTEGER>, - Key: Option<&ULONG> - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - pub fn RtlNtStatusToDosError( - Status: NTSTATUS - ) -> ULONG { - Status as ULONG - } pub fn NtCreateKeyedEvent( KeyedEventHandle: LPHANDLE, DesiredAccess: ACCESS_MASK, diff --git a/library/std/src/sys/windows/c/errors.rs b/library/std/src/sys/windows/c/errors.rs index 23dcc119d..ad8da19b6 100644 --- a/library/std/src/sys/windows/c/errors.rs +++ b/library/std/src/sys/windows/c/errors.rs @@ -12,7 +12,7 @@ pub const ERROR_RESOURCE_CALL_TIMED_OUT: DWORD = 5910; pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: DWORD = 8014; pub const DNS_ERROR_RECORD_TIMED_OUT: DWORD = 9705; -// The followiung list was obtained from +// The following list was obtained from // `/usr/x86_64-w64-mingw32/include/winerror.h` // in the Debian package // mingw-w64_6.0.0-3_all.deb diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index d2c597664..956db577d 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -1236,7 +1236,17 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { } pub fn stat(path: &Path) -> io::Result { - metadata(path, ReparsePoint::Follow) + match metadata(path, ReparsePoint::Follow) { + Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { + if let Ok(attrs) = lstat(path) { + if !attrs.file_type().is_symlink() { + return Ok(attrs); + } + } + Err(err) + } + result => result, + } } pub fn lstat(path: &Path) -> io::Result { @@ -1393,24 +1403,40 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); let f = File::open(junction, &opts)?; let h = f.as_inner().as_raw_handle(); - unsafe { let mut data = Align8([MaybeUninit::::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let data_ptr = data.0.as_mut_ptr(); + let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE); let db = data_ptr.cast::(); // Zero the header to ensure it's fully initialized, including reserved parameters. *db = mem::zeroed(); - let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::(); - let mut i = 0; + let reparse_target_slice = { + let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::(); + // Compute offset in bytes and then divide so that we round down + // rather than hit any UB (admittedly this arithmetic should work + // out so that this isn't necessary) + let buf_len_bytes = usize::try_from(data_end.byte_offset_from(buf_start)).unwrap(); + let buf_len_wchars = buf_len_bytes / core::mem::size_of::(); + core::slice::from_raw_parts_mut(buf_start, buf_len_wchars) + }; + // FIXME: this conversion is very hacky - let v = br"\??\"; - let v = v.iter().map(|x| *x as u16); - for c in v.chain(original.as_os_str().encode_wide()) { - *buf.add(i) = c; + let iter = br"\??\" + .iter() + .map(|x| *x as u16) + .chain(original.as_os_str().encode_wide()) + .chain(core::iter::once(0)); + let mut i = 0; + for c in iter { + if i >= reparse_target_slice.len() { + return Err(crate::io::const_io_error!( + crate::io::ErrorKind::InvalidFilename, + "Input filename is too long" + )); + } + reparse_target_slice[i] = c; i += 1; } - *buf.add(i) = 0; - i += 1; (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT; (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD; (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD; diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs index ae33d48c6..b290f4070 100644 --- a/library/std/src/sys/windows/handle.rs +++ b/library/std/src/sys/windows/handle.rs @@ -327,7 +327,16 @@ impl<'a> Read for &'a Handle { (**self).read(buf) } + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { (**self).read_vectored(bufs) } + + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } } diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index e0701a498..ee1f5482b 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -1,7 +1,7 @@ #![unstable(issue = "none", feature = "windows_net")] use crate::cmp; -use crate::io::{self, IoSlice, IoSliceMut, Read}; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read}; use crate::mem; use crate::net::{Shutdown, SocketAddr}; use crate::os::windows::io::{ @@ -214,28 +214,38 @@ impl Socket { Ok(Self(self.0.try_clone()?)) } - fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result { + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. - let length = cmp::min(buf.len(), i32::MAX as usize) as i32; - let result = - unsafe { c::recv(self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags) }; + let length = cmp::min(buf.capacity(), i32::MAX as usize) as i32; + let result = unsafe { + c::recv(self.as_raw_socket(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) + }; match result { c::SOCKET_ERROR => { let error = unsafe { c::WSAGetLastError() }; if error == c::WSAESHUTDOWN { - Ok(0) + Ok(()) } else { Err(io::Error::from_raw_os_error(error)) } } - _ => Ok(result as usize), + _ => { + unsafe { buf.advance(result as usize) }; + Ok(()) + } } } pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { self.recv_with_flags(buf, 0) } @@ -277,7 +287,9 @@ impl Socket { } pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.recv_with_flags(buf, c::MSG_PEEK) + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), c::MSG_PEEK)?; + Ok(buf.len()) } fn recv_from_with_flags( diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index beeca1917..c3573d14c 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -220,6 +220,19 @@ fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { /// /// This path may or may not have a verbatim prefix. pub(crate) fn maybe_verbatim(path: &Path) -> io::Result> { + let path = to_u16s(path)?; + get_long_path(path, true) +} + +/// Get a normalized absolute path that can bypass path length limits. +/// +/// Setting prefer_verbatim to true suggests a stronger preference for verbatim +/// paths even when not strictly necessary. This allows the Windows API to avoid +/// repeating our work. However, if the path may be given back to users or +/// passed to other application then it's preferable to use non-verbatim paths +/// when possible. Non-verbatim paths are better understood by users and handled +/// by more software. +pub(crate) fn get_long_path(mut path: Vec, prefer_verbatim: bool) -> io::Result> { // Normally the MAX_PATH is 260 UTF-16 code units (including the NULL). // However, for APIs such as CreateDirectory[1], the limit is 248. // @@ -243,7 +256,6 @@ pub(crate) fn maybe_verbatim(path: &Path) -> io::Result> { // \\?\UNC\ const UNC_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP, U, N, C, SEP]; - let mut path = to_u16s(path)?; if path.starts_with(VERBATIM_PREFIX) || path.starts_with(NT_PREFIX) || path == &[0] { // Early return for paths that are already verbatim or empty. return Ok(path); @@ -275,29 +287,34 @@ pub(crate) fn maybe_verbatim(path: &Path) -> io::Result> { |mut absolute| { path.clear(); - // Secondly, add the verbatim prefix. This is easier here because we know the - // path is now absolute and fully normalized (e.g. `/` has been changed to `\`). - let prefix = match absolute { - // C:\ => \\?\C:\ - [_, COLON, SEP, ..] => VERBATIM_PREFIX, - // \\.\ => \\?\ - [SEP, SEP, DOT, SEP, ..] => { - absolute = &absolute[4..]; - VERBATIM_PREFIX - } - // Leave \\?\ and \??\ as-is. - [SEP, SEP, QUERY, SEP, ..] | [SEP, QUERY, QUERY, SEP, ..] => &[], - // \\ => \\?\UNC\ - [SEP, SEP, ..] => { - absolute = &absolute[2..]; - UNC_PREFIX - } - // Anything else we leave alone. - _ => &[], - }; - - path.reserve_exact(prefix.len() + absolute.len() + 1); - path.extend_from_slice(prefix); + // Only prepend the prefix if needed. + if prefer_verbatim || absolute.len() + 1 >= LEGACY_MAX_PATH { + // Secondly, add the verbatim prefix. This is easier here because we know the + // path is now absolute and fully normalized (e.g. `/` has been changed to `\`). + let prefix = match absolute { + // C:\ => \\?\C:\ + [_, COLON, SEP, ..] => VERBATIM_PREFIX, + // \\.\ => \\?\ + [SEP, SEP, DOT, SEP, ..] => { + absolute = &absolute[4..]; + VERBATIM_PREFIX + } + // Leave \\?\ and \??\ as-is. + [SEP, SEP, QUERY, SEP, ..] | [SEP, QUERY, QUERY, SEP, ..] => &[], + // \\ => \\?\UNC\ + [SEP, SEP, ..] => { + absolute = &absolute[2..]; + UNC_PREFIX + } + // Anything else we leave alone. + _ => &[], + }; + + path.reserve_exact(prefix.len() + absolute.len() + 1); + path.extend_from_slice(prefix); + } else { + path.reserve_exact(absolute.len() + 1); + } path.extend_from_slice(absolute); path.push(0); }, diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs index 7b25edaa5..0780b2958 100644 --- a/library/std/src/sys/windows/pipe.rs +++ b/library/std/src/sys/windows/pipe.rs @@ -1,7 +1,7 @@ use crate::os::windows::prelude::*; use crate::ffi::OsStr; -use crate::io::{self, IoSlice, IoSliceMut, Read}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; use crate::mem; use crate::path::Path; use crate::ptr; @@ -252,6 +252,28 @@ impl AnonPipe { } } + pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + let result = unsafe { + let len = crate::cmp::min(buf.capacity(), c::DWORD::MAX as usize) as c::DWORD; + self.alertable_io_internal(c::ReadFileEx, buf.as_mut().as_mut_ptr() as _, len) + }; + + match result { + // The special treatment of BrokenPipe is to deal with Windows + // pipe semantics, which yields this error when *reading* from + // a pipe after the other end has closed; we interpret that as + // EOF on the pipe. + Err(ref e) if e.kind() == io::ErrorKind::BrokenPipe => Ok(()), + Err(e) => Err(e), + Ok(n) => { + unsafe { + buf.advance(n); + } + Ok(()) + } + } + } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.inner.read_vectored(bufs) } diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index 10bc949e1..1c73b64e2 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -266,11 +266,7 @@ impl Command { let (program, mut cmd_str) = if is_batch_file { ( command_prompt()?, - args::make_bat_command_line( - &args::to_user_path(program)?, - &self.args, - self.force_quotes_enabled, - )?, + args::make_bat_command_line(&program, &self.args, self.force_quotes_enabled)?, ) } else { let cmd_str = make_command_line(&self.program, &self.args, self.force_quotes_enabled)?; @@ -410,7 +406,7 @@ fn resolve_exe<'a>( if has_exe_suffix { // The application name is a path to a `.exe` file. // Let `CreateProcessW` figure out if it exists or not. - return path::maybe_verbatim(Path::new(exe_path)); + return args::to_user_path(Path::new(exe_path)); } let mut path = PathBuf::from(exe_path); @@ -422,7 +418,7 @@ fn resolve_exe<'a>( // It's ok to use `set_extension` here because the intent is to // remove the extension that was just added. path.set_extension(""); - return path::maybe_verbatim(&path); + return args::to_user_path(&path); } } else { ensure_no_nuls(exe_path)?; @@ -510,7 +506,7 @@ where /// Check if a file exists without following symlinks. fn program_exists(path: &Path) -> Option> { unsafe { - let path = path::maybe_verbatim(path).ok()?; + let path = args::to_user_path(path).ok()?; // Getting attributes using `GetFileAttributesW` does not follow symlinks // and it will almost always be successful if the link exists. // There are some exceptions for special system files (e.g. the pagefile) -- cgit v1.2.3