diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:20:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:20:39 +0000 |
commit | 1376c5a617be5c25655d0d7cb63e3beaa5a6e026 (patch) | |
tree | 3bb8d61aee02bc7a15eab3f36e3b921afc2075d0 /library/std/src/sys | |
parent | Releasing progress-linux version 1.69.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.tar.xz rustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.zip |
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src/sys')
41 files changed, 1164 insertions, 183 deletions
diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs index 403a5e627..a5fcbdf39 100644 --- a/library/std/src/sys/common/alloc.rs +++ b/library/std/src/sys/common/alloc.rs @@ -22,6 +22,7 @@ pub const MIN_ALIGN: usize = 8; #[cfg(any( target_arch = "x86_64", target_arch = "aarch64", + target_arch = "loongarch64", target_arch = "mips64", target_arch = "s390x", target_arch = "sparc64", diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs index 29fc0835d..2b8782ddf 100644 --- a/library/std/src/sys/common/mod.rs +++ b/library/std/src/sys/common/mod.rs @@ -12,6 +12,7 @@ pub mod alloc; pub mod small_c_string; +pub mod thread_local; #[cfg(test)] mod tests; diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs new file mode 100644 index 000000000..e229eb16a --- /dev/null +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -0,0 +1,254 @@ +#[doc(hidden)] +#[macro_export] +#[allow_internal_unstable( + thread_local_internals, + cfg_target_thread_local, + thread_local, + libstd_thread_internals +)] +#[allow_internal_unsafe] +macro_rules! __thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + #[cfg_attr(not(bootstrap), inline)] + #[deny(unsafe_op_in_unsafe_fn)] + unsafe fn __getit( + _init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + const INIT_EXPR: $t = $init; + // If the platform has support for `#[thread_local]`, use it. + #[thread_local] + static mut VAL: $t = INIT_EXPR; + + // If a dtor isn't needed we can do something "very raw" and + // just get going. + if !$crate::mem::needs_drop::<$t>() { + unsafe { + return $crate::option::Option::Some(&VAL) + } + } + + // 0 == dtor not registered + // 1 == dtor registered, dtor not run + // 2 == dtor registered and is running or has run + #[thread_local] + static mut STATE: $crate::primitive::u8 = 0; + + unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) { + let ptr = ptr as *mut $t; + + unsafe { + $crate::debug_assert_eq!(STATE, 1); + STATE = 2; + $crate::ptr::drop_in_place(ptr); + } + } + + unsafe { + match STATE { + // 0 == we haven't registered a destructor, so do + // so now. + 0 => { + $crate::thread::__LocalKeyInner::<$t>::register_dtor( + $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8, + destroy, + ); + STATE = 1; + $crate::option::Option::Some(&VAL) + } + // 1 == the destructor is registered and the value + // is valid, so return the pointer. + 1 => $crate::option::Option::Some(&VAL), + // otherwise the destructor has already run, so we + // can't give access. + _ => $crate::option::Option::None, + } + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}; + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => { + { + #[inline] + fn __init() -> $t { $init } + + #[cfg_attr(not(bootstrap), inline)] + unsafe fn __getit( + init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + #[thread_local] + static __KEY: $crate::thread::__LocalKeyInner<$t> = + $crate::thread::__LocalKeyInner::<$t>::new(); + + // FIXME: remove the #[allow(...)] marker when macros don't + // raise warning for missing/extraneous unsafe blocks anymore. + // See https://github.com/rust-lang/rust/issues/74838. + #[allow(unused_unsafe)] + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing default value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + } + }; + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::__thread_local_inner!(@key $t, $($init)*); + } +} + +#[doc(hidden)] +pub mod fast { + use super::super::lazy::LazyKeyInner; + use crate::cell::Cell; + use crate::sys::thread_local_dtor::register_dtor; + use crate::{fmt, mem, panic}; + + #[derive(Copy, Clone)] + enum DtorState { + Unregistered, + Registered, + RunningOrHasRun, + } + + // This data structure has been carefully constructed so that the fast path + // only contains one branch on x86. That optimization is necessary to avoid + // duplicated tls lookups on OSX. + // + // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 + pub struct Key<T> { + // If `LazyKeyInner::get` returns `None`, that indicates either: + // * The value has never been initialized + // * The value is being recursively initialized + // * The value has already been destroyed or is being destroyed + // To determine which kind of `None`, check `dtor_state`. + // + // This is very optimizer friendly for the fast path - initialized but + // not yet dropped. + inner: LazyKeyInner<T>, + + // Metadata to keep track of the state of the destructor. Remember that + // this variable is thread-local, not global. + dtor_state: Cell<DtorState>, + } + + impl<T> fmt::Debug for Key<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Key").finish_non_exhaustive() + } + } + + impl<T> Key<T> { + pub const fn new() -> Key<T> { + Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } + } + + // note that this is just a publicly-callable function only for the + // const-initialized form of thread locals, basically a way to call the + // free `register_dtor` function defined elsewhere in std. + pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + unsafe { + register_dtor(a, dtor); + } + } + + pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { + // SAFETY: See the definitions of `LazyKeyInner::get` and + // `try_initialize` for more information. + // + // The caller must ensure no mutable references are ever active to + // the inner cell or the inner T when this is called. + // The `try_initialize` is dependant on the passed `init` function + // for this. + unsafe { + match self.inner.get() { + Some(val) => Some(val), + None => self.try_initialize(init), + } + } + } + + // `try_initialize` is only called once per fast thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + // + // Macos: Inlining this function can cause two `tlv_get_addr` calls to + // be performed for every call to `Key::get`. + // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 + #[inline(never)] + unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { + // SAFETY: See comment above (this function doc). + if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } { + // SAFETY: See comment above (this function doc). + Some(unsafe { self.inner.initialize(init) }) + } else { + None + } + } + + // `try_register_dtor` is only called once per fast thread local + // variable, except in corner cases where thread_local dtors reference + // other thread_local's, or it is being recursively initialized. + unsafe fn try_register_dtor(&self) -> bool { + match self.dtor_state.get() { + DtorState::Unregistered => { + // SAFETY: dtor registration happens before initialization. + // Passing `self` as a pointer while using `destroy_value<T>` + // is safe because the function will build a pointer to a + // Key<T>, which is the type of self and so find the correct + // size. + unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) }; + self.dtor_state.set(DtorState::Registered); + true + } + DtorState::Registered => { + // recursively initialized + true + } + DtorState::RunningOrHasRun => false, + } + } + } + + unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) { + let ptr = ptr as *mut Key<T>; + + // SAFETY: + // + // The pointer `ptr` has been built just above and comes from + // `try_register_dtor` where it is originally a Key<T> coming from `self`, + // making it non-NUL and of the correct type. + // + // Right before we run the user destructor be sure to set the + // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This + // causes future calls to `get` to run `try_initialize_drop` again, + // which will now fail, and return `None`. + // + // Wrap the call in a catch to ensure unwinding is caught in the event + // a panic takes place in a destructor. + if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe { + let value = (*ptr).inner.take(); + (*ptr).dtor_state.set(DtorState::RunningOrHasRun); + drop(value); + })) { + rtabort!("thread local panicked on drop"); + } + } +} diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs new file mode 100644 index 000000000..1fee84a04 --- /dev/null +++ b/library/std/src/sys/common/thread_local/mod.rs @@ -0,0 +1,109 @@ +//! The following module declarations are outside cfg_if because the internal +//! `__thread_local_internal` macro does not seem to be exported properly when using cfg_if +#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")] + +#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] +mod fast_local; +#[cfg(all( + not(target_thread_local), + not(all(target_family = "wasm", not(target_feature = "atomics"))) +))] +mod os_local; +#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] +mod static_local; + +#[cfg(not(test))] +cfg_if::cfg_if! { + if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] { + #[doc(hidden)] + pub use static_local::statik::Key; + } else if #[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] { + #[doc(hidden)] + pub use fast_local::fast::Key; + } else if #[cfg(all(not(target_thread_local), not(all(target_family = "wasm", not(target_feature = "atomics")))))] { + #[doc(hidden)] + pub use os_local::os::Key; + } +} + +#[doc(hidden)] +#[cfg(test)] +pub use realstd::thread::__LocalKeyInner as Key; + +mod lazy { + use crate::cell::UnsafeCell; + use crate::hint; + use crate::mem; + + pub struct LazyKeyInner<T> { + inner: UnsafeCell<Option<T>>, + } + + impl<T> LazyKeyInner<T> { + pub const fn new() -> LazyKeyInner<T> { + LazyKeyInner { inner: UnsafeCell::new(None) } + } + + pub unsafe fn get(&self) -> Option<&'static T> { + // SAFETY: The caller must ensure no reference is ever handed out to + // the inner cell nor mutable reference to the Option<T> inside said + // cell. This make it safe to hand a reference, though the lifetime + // of 'static is itself unsafe, making the get method unsafe. + unsafe { (*self.inner.get()).as_ref() } + } + + /// The caller must ensure that no reference is active: this method + /// needs unique access. + pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T { + // Execute the initialization up front, *then* move it into our slot, + // just in case initialization fails. + let value = init(); + let ptr = self.inner.get(); + + // SAFETY: + // + // note that this can in theory just be `*ptr = Some(value)`, but due to + // the compiler will currently codegen that pattern with something like: + // + // ptr::drop_in_place(ptr) + // ptr::write(ptr, Some(value)) + // + // Due to this pattern it's possible for the destructor of the value in + // `ptr` (e.g., if this is being recursively initialized) to re-access + // TLS, in which case there will be a `&` and `&mut` pointer to the same + // value (an aliasing violation). To avoid setting the "I'm running a + // destructor" flag we just use `mem::replace` which should sequence the + // operations a little differently and make this safe to call. + // + // The precondition also ensures that we are the only one accessing + // `self` at the moment so replacing is fine. + unsafe { + let _ = mem::replace(&mut *ptr, Some(value)); + } + + // SAFETY: With the call to `mem::replace` it is guaranteed there is + // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked` + // will never be reached. + unsafe { + // After storing `Some` we want to get a reference to the contents of + // what we just stored. While we could use `unwrap` here and it should + // always work it empirically doesn't seem to always get optimized away, + // which means that using something like `try_with` can pull in + // panicking code and cause a large size bloat. + match *ptr { + Some(ref x) => x, + None => hint::unreachable_unchecked(), + } + } + } + + /// The other methods hand out references while taking &self. + /// As such, callers of this method must ensure no `&` and `&mut` are + /// available and used at the same time. + #[allow(unused)] + pub unsafe fn take(&mut self) -> Option<T> { + // SAFETY: See doc comment for this method. + unsafe { (*self.inner.get()).take() } + } + } +} diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs new file mode 100644 index 000000000..1442a397e --- /dev/null +++ b/library/std/src/sys/common/thread_local/os_local.rs @@ -0,0 +1,197 @@ +#[doc(hidden)] +#[macro_export] +#[allow_internal_unstable( + thread_local_internals, + cfg_target_thread_local, + thread_local, + libstd_thread_internals +)] +#[allow_internal_unsafe] +macro_rules! __thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + #[cfg_attr(not(bootstrap), inline)] + #[deny(unsafe_op_in_unsafe_fn)] + unsafe fn __getit( + _init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + const INIT_EXPR: $t = $init; + + // On platforms without `#[thread_local]` we fall back to the + // same implementation as below for os thread locals. + #[inline] + const fn __init() -> $t { INIT_EXPR } + static __KEY: $crate::thread::__LocalKeyInner<$t> = + $crate::thread::__LocalKeyInner::new(); + #[allow(unused_unsafe)] + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = _init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing initial value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}; + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => { + { + #[inline] + fn __init() -> $t { $init } + + // `#[inline] does not work on windows-gnu due to linking errors around dllimports. + // See https://github.com/rust-lang/rust/issues/109797. + #[cfg_attr(not(windows), inline)] + unsafe fn __getit( + init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + static __KEY: $crate::thread::__LocalKeyInner<$t> = + $crate::thread::__LocalKeyInner::new(); + + // FIXME: remove the #[allow(...)] marker when macros don't + // raise warning for missing/extraneous unsafe blocks anymore. + // See https://github.com/rust-lang/rust/issues/74838. + #[allow(unused_unsafe)] + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing default value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + } + }; + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::__thread_local_inner!(@key $t, $($init)*); + } +} + +#[doc(hidden)] +pub mod os { + use super::super::lazy::LazyKeyInner; + use crate::cell::Cell; + use crate::sys_common::thread_local_key::StaticKey as OsStaticKey; + use crate::{fmt, marker, panic, ptr}; + + /// Use a regular global static to store this key; the state provided will then be + /// thread-local. + pub struct Key<T> { + // OS-TLS key that we'll use to key off. + os: OsStaticKey, + marker: marker::PhantomData<Cell<T>>, + } + + impl<T> fmt::Debug for Key<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Key").finish_non_exhaustive() + } + } + + unsafe impl<T> Sync for Key<T> {} + + struct Value<T: 'static> { + inner: LazyKeyInner<T>, + key: &'static Key<T>, + } + + impl<T: 'static> Key<T> { + #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] + pub const fn new() -> Key<T> { + Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData } + } + + /// It is a requirement for the caller to ensure that no mutable + /// reference is active when this method is called. + pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { + // SAFETY: See the documentation for this method. + let ptr = unsafe { self.os.get() as *mut Value<T> }; + if ptr.addr() > 1 { + // SAFETY: the check ensured the pointer is safe (its destructor + // is not running) + it is coming from a trusted source (self). + if let Some(ref value) = unsafe { (*ptr).inner.get() } { + return Some(value); + } + } + // SAFETY: At this point we are sure we have no value and so + // initializing (or trying to) is safe. + unsafe { self.try_initialize(init) } + } + + // `try_initialize` is only called once per os thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { + // SAFETY: No mutable references are ever handed out meaning getting + // the value is ok. + let ptr = unsafe { self.os.get() as *mut Value<T> }; + if ptr.addr() == 1 { + // destructor is running + return None; + } + + let ptr = if ptr.is_null() { + // If the lookup returned null, we haven't initialized our own + // local copy, so do that now. + let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self })); + // SAFETY: At this point we are sure there is no value inside + // ptr so setting it will not affect anyone else. + unsafe { + self.os.set(ptr as *mut u8); + } + ptr + } else { + // recursive initialization + ptr + }; + + // SAFETY: ptr has been ensured as non-NUL just above an so can be + // dereferenced safely. + unsafe { Some((*ptr).inner.initialize(init)) } + } + } + + unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) { + // SAFETY: + // + // The OS TLS ensures that this key contains a null value when this + // destructor starts to run. We set it back to a sentinel value of 1 to + // ensure that any future calls to `get` for this thread will return + // `None`. + // + // Note that to prevent an infinite loop we reset it back to null right + // before we return from the destructor ourselves. + // + // Wrap the call in a catch to ensure unwinding is caught in the event + // a panic takes place in a destructor. + if let Err(_) = panic::catch_unwind(|| unsafe { + let ptr = Box::from_raw(ptr as *mut Value<T>); + let key = ptr.key; + key.os.set(ptr::invalid_mut(1)); + drop(ptr); + key.os.set(ptr::null_mut()); + }) { + rtabort!("thread local panicked on drop"); + } + } +} diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs new file mode 100644 index 000000000..ec4f2a12b --- /dev/null +++ b/library/std/src/sys/common/thread_local/static_local.rs @@ -0,0 +1,115 @@ +#[doc(hidden)] +#[macro_export] +#[allow_internal_unstable( + thread_local_internals, + cfg_target_thread_local, + thread_local, + libstd_thread_internals +)] +#[allow_internal_unsafe] +macro_rules! __thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + #[inline] // see comments below + #[deny(unsafe_op_in_unsafe_fn)] + unsafe fn __getit( + _init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + const INIT_EXPR: $t = $init; + + // wasm without atomics maps directly to `static mut`, and dtors + // aren't implemented because thread dtors aren't really a thing + // on wasm right now + // + // FIXME(#84224) this should come after the `target_thread_local` + // block. + static mut VAL: $t = INIT_EXPR; + unsafe { $crate::option::Option::Some(&VAL) } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}; + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => { + { + #[inline] + fn __init() -> $t { $init } + #[inline] + unsafe fn __getit( + init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + static __KEY: $crate::thread::__LocalKeyInner<$t> = + $crate::thread::__LocalKeyInner::new(); + + // FIXME: remove the #[allow(...)] marker when macros don't + // raise warning for missing/extraneous unsafe blocks anymore. + // See https://github.com/rust-lang/rust/issues/74838. + #[allow(unused_unsafe)] + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing default value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + } + }; + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::__thread_local_inner!(@key $t, $($init)*); + } +} + +/// On some targets like wasm there's no threads, so no need to generate +/// thread locals and we can instead just use plain statics! +#[doc(hidden)] +pub mod statik { + use super::super::lazy::LazyKeyInner; + use crate::fmt; + + pub struct Key<T> { + inner: LazyKeyInner<T>, + } + + unsafe impl<T> Sync for Key<T> {} + + impl<T> fmt::Debug for Key<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Key").finish_non_exhaustive() + } + } + + impl<T> Key<T> { + pub const fn new() -> Key<T> { + Key { inner: LazyKeyInner::new() } + } + + pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> { + // SAFETY: The caller must ensure no reference is ever handed out to + // the inner cell nor mutable reference to the Option<T> inside said + // cell. This make it safe to hand a reference, though the lifetime + // of 'static is itself unsafe, making the get method unsafe. + let value = unsafe { + match self.inner.get() { + Some(ref value) => value, + None => self.inner.initialize(init), + } + }; + + Some(value) + } + } +} diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index c966f2177..cf0b27176 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -202,7 +202,7 @@ impl OpenOptions { create: false, create_new: false, // system-specific - mode: 0x777, + mode: 0o777, } } diff --git a/library/std/src/sys/hermit/futex.rs b/library/std/src/sys/hermit/futex.rs index b64c174b0..427d8ff6f 100644 --- a/library/std/src/sys/hermit/futex.rs +++ b/library/std/src/sys/hermit/futex.rs @@ -16,7 +16,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - let r = unsafe { abi::futex_wait( - futex.as_mut_ptr(), + futex.as_ptr(), expected, timespec.as_ref().map_or(null(), |t| t as *const abi::timespec), abi::FUTEX_RELATIVE_TIMEOUT, @@ -28,12 +28,12 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - #[inline] pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { abi::futex_wake(futex.as_mut_ptr(), 1) > 0 } + unsafe { abi::futex_wake(futex.as_ptr(), 1) > 0 } } #[inline] pub fn futex_wake_all(futex: &AtomicU32) { unsafe { - abi::futex_wake(futex.as_mut_ptr(), i32::MAX); + abi::futex_wake(futex.as_ptr(), i32::MAX); } } diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index d34a4cfed..c7cb84667 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -15,7 +15,6 @@ #![allow(missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn)] -use crate::intrinsics; use crate::os::raw::c_char; pub mod alloc; @@ -76,9 +75,18 @@ pub fn abort_internal() -> ! { } } -// FIXME: just a workaround to test the system pub fn hashmap_random_keys() -> (u64, u64) { - (1, 2) + let mut buf = [0; 16]; + let mut slice = &mut buf[..]; + while !slice.is_empty() { + let res = cvt(unsafe { abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) }) + .expect("failed to generate random hashmap keys"); + slice = &mut slice[res as usize..]; + } + + let key1 = buf[..8].try_into().unwrap(); + let key2 = buf[8..].try_into().unwrap(); + (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) } // This function is needed by the panic runtime. The symbol is named in diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs index 5fb6281aa..d6f64a297 100644 --- a/library/std/src/sys/hermit/net.rs +++ b/library/std/src/sys/hermit/net.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use crate::cmp; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; @@ -146,18 +146,35 @@ impl Socket { Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) } - fn recv_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<usize> { - let ret = - cvt(unsafe { netc::recv(self.0.as_raw_fd(), buf.as_mut_ptr(), buf.len(), flags) })?; - Ok(ret as usize) + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: i32) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv( + self.0.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut u8, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance(ret as usize); + } + Ok(()) } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { - self.recv_with_flags(buf, 0) + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) } pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { - self.recv_with_flags(buf, netc::MSG_PEEK) + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index c080c176a..e767b2866 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -76,3 +76,12 @@ cfg_if::cfg_if! { pub mod c; } } + +cfg_if::cfg_if! { + // Fuchsia components default to full backtrace. + if #[cfg(target_os = "fuchsia")] { + pub const FULL_BACKTRACE_DEFAULT: bool = true; + } else { + pub const FULL_BACKTRACE_DEFAULT: bool = false; + } +} diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs index 0d934318c..01505e944 100644 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs @@ -3,7 +3,6 @@ use crate::arch::asm; use crate::cell::UnsafeCell; use crate::cmp; -use crate::convert::TryInto; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut}; use crate::ptr::{self, NonNull}; diff --git a/library/std/src/sys/sgx/fd.rs b/library/std/src/sys/sgx/fd.rs index e5dc5b5ad..0c02a1076 100644 --- a/library/std/src/sys/sgx/fd.rs +++ b/library/std/src/sys/sgx/fd.rs @@ -1,7 +1,7 @@ use fortanix_sgx_abi::Fd; use super::abi::usercalls; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::sys::{AsInner, FromInner, IntoInner}; @@ -30,6 +30,10 @@ impl FileDesc { usercalls::read(self.fd, &mut [IoSliceMut::new(buf)]) } + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|b| self.read(b), buf) + } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { usercalls::read(self.fd, bufs) } diff --git a/library/std/src/sys/sgx/net.rs b/library/std/src/sys/sgx/net.rs index 4c4cd7d1d..923be5eb9 100644 --- a/library/std/src/sys/sgx/net.rs +++ b/library/std/src/sys/sgx/net.rs @@ -1,6 +1,6 @@ use crate::error; use crate::fmt; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; use crate::sys::fd::FileDesc; @@ -144,6 +144,10 @@ impl TcpStream { self.inner.inner.read(buf) } + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.inner.read_buf(buf) + } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.inner.inner.read_vectored(bufs) } diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs index 1b98ef993..7d7bfae14 100644 --- a/library/std/src/sys/solid/net.rs +++ b/library/std/src/sys/solid/net.rs @@ -2,7 +2,7 @@ use super::abi; use crate::{ cmp, ffi::CStr, - io::{self, ErrorKind, IoSlice, IoSliceMut}, + io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}, mem, net::{Shutdown, SocketAddr}, ptr, str, @@ -294,19 +294,30 @@ impl Socket { self.0.duplicate().map(Socket) } - fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> { + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { let ret = cvt(unsafe { - netc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) + netc::recv(self.0.raw(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) })?; - Ok(ret as usize) + unsafe { + buf.advance(ret as usize); + } + Ok(()) } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { - self.recv_with_flags(buf, 0) + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) } pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { - self.recv_with_flags(buf, MSG_PEEK) + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 9874af4d3..ce5c048f2 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -469,6 +469,15 @@ impl<'a> Read for &'a FileDesc { fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { (**self).read_buf(cursor) } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + (**self).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } } impl AsInner<OwnedFd> for FileDesc { diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 7566fafda..abef170dd 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -34,7 +34,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; target_os = "watchos", ))] use crate::sys::weak::syscall; -#[cfg(any(target_os = "android", target_os = "macos"))] +#[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))] use crate::sys::weak::weak; use libc::{c_int, mode_t}; @@ -43,6 +43,7 @@ use libc::{c_int, mode_t}; target_os = "macos", target_os = "ios", target_os = "watchos", + target_os = "solaris", all(target_os = "linux", target_env = "gnu") ))] use libc::c_char; @@ -1497,8 +1498,8 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { // Android has `linkat` on newer versions, but we happen to know `link` // always has the correct behavior, so it's here as well. cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - } else if #[cfg(target_os = "macos")] { - // On MacOS, older versions (<=10.9) lack support for linkat while newer + } else if #[cfg(any(target_os = "macos", target_os = "solaris"))] { + // MacOS (<=10.9) and Solaris 10 lack support for linkat while newer // versions have it. We want to use linkat if it is available, so we use weak! // to check. `linkat` is preferable to `link` because it gives us a flag to // specify how symlinks should be handled. We pass 0 as the flags argument, @@ -1892,7 +1893,7 @@ mod remove_dir_impl { // file descriptor is automatically closed by libc::closedir() now, so give up ownership let new_parent_fd = dir_fd.into_raw_fd(); // a valid root is not needed because we do not call any functions involving the full path - // of the DirEntrys. + // of the `DirEntry`s. let dummy_root = PathBuf::new(); let inner = InnerReadDir { dirp, root: dummy_root }; Ok((ReadDir::new(inner), new_parent_fd)) diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs index 8d5b54021..d310be6c7 100644 --- a/library/std/src/sys/unix/futex.rs +++ b/library/std/src/sys/unix/futex.rs @@ -273,8 +273,6 @@ pub mod zircon { #[cfg(target_os = "fuchsia")] pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { - use crate::convert::TryFrom; - // Sleep forever if the timeout is longer than fits in a i64. let deadline = timeout .and_then(|d| { diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 73b9bef7e..16c8e0c0e 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -17,11 +17,9 @@ //! Once it has obtained all necessary pieces and brought any wrapper types into a state where they //! can be safely bypassed it will attempt to use the `copy_file_range(2)`, //! `sendfile(2)` or `splice(2)` syscalls to move data directly between file descriptors. -//! Since those syscalls have requirements that cannot be fully checked in advance and -//! gathering additional information about file descriptors would require additional syscalls -//! anyway it simply attempts to use them one after another (guided by inaccurate hints) to -//! figure out which one works and falls back to the generic read-write copy loop if none of them -//! does. +//! Since those syscalls have requirements that cannot be fully checked in advance it attempts +//! to use them one after another (guided by hints) to figure out which one works and +//! falls back to the generic read-write copy loop if none of them does. //! Once a working syscall is found for a pair of file descriptors it will be called in a loop //! until the copy operation is completed. //! @@ -84,14 +82,10 @@ pub(crate) fn copy_spec<R: Read + ?Sized, W: Write + ?Sized>( /// The methods on this type only provide hints, due to `AsRawFd` and `FromRawFd` the inferred /// type may be wrong. enum FdMeta { - /// We obtained the FD from a type that can contain any type of `FileType` and queried the metadata - /// because it is cheaper than probing all possible syscalls (reader side) Metadata(Metadata), Socket, Pipe, - /// We don't have any metadata, e.g. because the original type was `File` which can represent - /// any `FileType` and we did not query the metadata either since it did not seem beneficial - /// (writer side) + /// We don't have any metadata because the stat syscall failed NoneObtained, } @@ -131,6 +125,39 @@ impl FdMeta { } } +/// Returns true either if changes made to the source after a sendfile/splice call won't become +/// visible in the sink or the source has explicitly opted into such behavior (e.g. by splicing +/// a file into a pipe, the pipe being the source in this case). +/// +/// This will prevent File -> Pipe and File -> Socket splicing/sendfile optimizations to uphold +/// the Read/Write API semantics of io::copy. +/// +/// Note: This is not 100% airtight, the caller can use the RawFd conversion methods to turn a +/// regular file into a TcpSocket which will be treated as a socket here without checking. +fn safe_kernel_copy(source: &FdMeta, sink: &FdMeta) -> bool { + match (source, sink) { + // Data arriving from a socket is safe because the sender can't modify the socket buffer. + // Data arriving from a pipe is safe(-ish) because either the sender *copied* + // the bytes into the pipe OR explicitly performed an operation that enables zero-copy, + // thus promising not to modify the data later. + (FdMeta::Socket, _) => true, + (FdMeta::Pipe, _) => true, + (FdMeta::Metadata(meta), _) + if meta.file_type().is_fifo() || meta.file_type().is_socket() => + { + true + } + // Data going into non-pipes/non-sockets is safe because the "later changes may become visible" issue + // only happens for pages sitting in send buffers or pipes. + (_, FdMeta::Metadata(meta)) + if !meta.file_type().is_fifo() && !meta.file_type().is_socket() => + { + true + } + _ => false, + } +} + struct CopyParams(FdMeta, Option<RawFd>); struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> { @@ -186,7 +213,8 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> { // So we just try and fallback if needed. // If current file offsets + write sizes overflow it may also fail, we do not try to fix that and instead // fall back to the generic copy loop. - if input_meta.potential_sendfile_source() { + if input_meta.potential_sendfile_source() && safe_kernel_copy(&input_meta, &output_meta) + { let result = sendfile_splice(SpliceMode::Sendfile, readfd, writefd, max_write); result.update_take(reader); @@ -197,7 +225,9 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> { } } - if input_meta.maybe_fifo() || output_meta.maybe_fifo() { + if (input_meta.maybe_fifo() || output_meta.maybe_fifo()) + && safe_kernel_copy(&input_meta, &output_meta) + { let result = sendfile_splice(SpliceMode::Splice, readfd, writefd, max_write); result.update_take(reader); @@ -298,13 +328,13 @@ impl CopyRead for &File { impl CopyWrite for File { fn properties(&self) -> CopyParams { - CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd())) + CopyParams(fd_to_meta(self), Some(self.as_raw_fd())) } } impl CopyWrite for &File { fn properties(&self) -> CopyParams { - CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd())) + CopyParams(fd_to_meta(*self), Some(self.as_raw_fd())) } } @@ -401,13 +431,13 @@ impl CopyRead for StdinLock<'_> { impl CopyWrite for StdoutLock<'_> { fn properties(&self) -> CopyParams { - CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd())) + CopyParams(fd_to_meta(self), Some(self.as_raw_fd())) } } impl CopyWrite for StderrLock<'_> { fn properties(&self) -> CopyParams { - CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd())) + CopyParams(fd_to_meta(self), Some(self.as_raw_fd())) } } diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs index 3fe849e23..a524270e3 100644 --- a/library/std/src/sys/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/unix/kernel_copy/tests.rs @@ -83,6 +83,48 @@ fn copies_append_mode_sink() -> Result<()> { Ok(()) } +#[test] +fn dont_splice_pipes_from_files() -> Result<()> { + // splicing to a pipe and then modifying the source could lead to changes + // becoming visible in an unexpected order. + + use crate::io::SeekFrom; + use crate::os::unix::fs::FileExt; + use crate::process::{ChildStdin, ChildStdout}; + use crate::sys_common::FromInner; + + let (read_end, write_end) = crate::sys::pipe::anon_pipe()?; + + let mut read_end = ChildStdout::from_inner(read_end); + let mut write_end = ChildStdin::from_inner(write_end); + + let tmp_path = tmpdir(); + let file = tmp_path.join("to_be_modified"); + let mut file = + crate::fs::OpenOptions::new().create_new(true).read(true).write(true).open(file)?; + + const SZ: usize = libc::PIPE_BUF as usize; + + // put data in page cache + let mut buf: [u8; SZ] = [0x01; SZ]; + file.write_all(&buf).unwrap(); + + // copy page into pipe + file.seek(SeekFrom::Start(0)).unwrap(); + assert!(io::copy(&mut file, &mut write_end).unwrap() == SZ as u64); + + // modify file + buf[0] = 0x02; + file.write_at(&buf, 0).unwrap(); + + // read from pipe + read_end.read_exact(buf.as_mut_slice()).unwrap(); + + assert_eq!(buf[0], 0x01, "data in pipe should reflect the original, not later modifications"); + + Ok(()) +} + #[bench] fn bench_file_to_file_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 8e05b618d..573bfa658 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -1,6 +1,6 @@ use crate::cmp; use crate::ffi::CStr; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; @@ -242,19 +242,35 @@ impl Socket { self.0.duplicate().map(Socket) } - fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> { + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { let ret = cvt(unsafe { - libc::recv(self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) + libc::recv( + self.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut c_void, + buf.capacity(), + flags, + ) })?; - Ok(ret as usize) + unsafe { + buf.advance(ret as usize); + } + Ok(()) } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { - self.recv_with_flags(buf, 0) + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) } pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { - self.recv_with_flags(buf, MSG_PEEK) + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { @@ -427,6 +443,17 @@ impl Socket { Ok(passcred != 0) } + #[cfg(target_os = "freebsd")] + pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { + setsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT, passcred as libc::c_int) + } + + #[cfg(target_os = "freebsd")] + pub fn passcred(&self) -> io::Result<bool> { + let passcred: libc::c_int = getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; + Ok(passcred != 0) + } + #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as libc::c_int; diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 21b035fb3..a345af76f 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -7,7 +7,6 @@ mod tests; use crate::os::unix::prelude::*; -use crate::convert::TryFrom; use crate::error::Error as StdError; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; @@ -115,7 +114,10 @@ pub fn set_errno(e: i32) { /// Gets a detailed string description for the given error number. pub fn error_string(errno: i32) -> String { extern "C" { - #[cfg_attr(any(target_os = "linux", target_env = "newlib"), link_name = "__xpg_strerror_r")] + #[cfg_attr( + all(any(target_os = "linux", target_env = "newlib"), not(target_env = "ohos")), + link_name = "__xpg_strerror_r" + )] fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; } diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs index a744d0ab6..dc17c9fac 100644 --- a/library/std/src/sys/unix/pipe.rs +++ b/library/std/src/sys/unix/pipe.rs @@ -1,4 +1,4 @@ -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; @@ -49,6 +49,10 @@ impl AnonPipe { self.0.read(buf) } + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.0.read_vectored(bufs) } diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs index d4c7e58b3..e45c380a0 100644 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -166,7 +166,6 @@ impl Process { } pub fn wait(&mut self) -> io::Result<ExitStatus> { - use crate::default::Default; use crate::sys::process::zircon::*; let mut proc_info: zx_info_process_t = Default::default(); @@ -199,7 +198,6 @@ impl Process { } pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { - use crate::default::Default; use crate::sys::process::zircon::*; let mut proc_info: zx_info_process_t = Default::default(); diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index a6fe07873..0f347ffab 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -20,7 +20,8 @@ pub fn hashmap_random_keys() -> (u64, u64) { not(target_os = "netbsd"), not(target_os = "fuchsia"), not(target_os = "redox"), - not(target_os = "vxworks") + not(target_os = "vxworks"), + not(target_os = "emscripten") ))] mod imp { use crate::fs::File; @@ -174,7 +175,7 @@ mod imp { } } -#[cfg(target_os = "openbsd")] +#[cfg(any(target_os = "openbsd", target_os = "emscripten"))] mod imp { use crate::sys::os::errno; diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index b3626c564..a26f20795 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -1,4 +1,4 @@ -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem::ManuallyDrop; use crate::os::unix::io::FromRawFd; use crate::sys::fd::FileDesc; @@ -18,6 +18,10 @@ impl io::Read for Stdin { unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) } } + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_buf(buf) } + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_vectored(bufs) } } diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 0f11de8f5..6f5358340 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -174,6 +174,34 @@ impl From<libc::timespec> for Timespec { } } +#[cfg(all( + target_os = "linux", + target_env = "gnu", + target_pointer_width = "32", + not(target_arch = "riscv32") +))] +#[repr(C)] +pub(in crate::sys::unix) struct __timespec64 { + pub(in crate::sys::unix) tv_sec: i64, + #[cfg(target_endian = "big")] + _padding: i32, + pub(in crate::sys::unix) tv_nsec: i32, + #[cfg(target_endian = "little")] + _padding: i32, +} + +#[cfg(all( + target_os = "linux", + target_env = "gnu", + target_pointer_width = "32", + not(target_arch = "riscv32") +))] +impl From<__timespec64> for Timespec { + fn from(t: __timespec64) -> Timespec { + Timespec::new(t.tv_sec, t.tv_nsec.into()) + } +} + #[cfg(any( all(target_os = "macos", any(not(target_arch = "aarch64"))), target_os = "ios", @@ -352,29 +380,23 @@ mod inner { impl Timespec { pub fn now(clock: libc::clockid_t) -> Timespec { // Try to use 64-bit time in preparation for Y2038. - #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32"))] + #[cfg(all( + target_os = "linux", + target_env = "gnu", + target_pointer_width = "32", + not(target_arch = "riscv32") + ))] { use crate::sys::weak::weak; // __clock_gettime64 was added to 32-bit arches in glibc 2.34, // and it handles both vDSO calls and ENOSYS fallbacks itself. - weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int); - - #[repr(C)] - struct __timespec64 { - tv_sec: i64, - #[cfg(target_endian = "big")] - _padding: i32, - tv_nsec: i32, - #[cfg(target_endian = "little")] - _padding: i32, - } + weak!(fn __clock_gettime64(libc::clockid_t, *mut super::__timespec64) -> libc::c_int); if let Some(clock_gettime64) = __clock_gettime64.get() { let mut t = MaybeUninit::uninit(); cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); - let t = unsafe { t.assume_init() }; - return Timespec::new(t.tv_sec, t.tv_nsec as i64); + return Timespec::from(unsafe { t.assume_init() }); } } diff --git a/library/std/src/sys/unsupported/net.rs b/library/std/src/sys/unsupported/net.rs index a5204a084..bbc52703f 100644 --- a/library/std/src/sys/unsupported/net.rs +++ b/library/std/src/sys/unsupported/net.rs @@ -1,5 +1,5 @@ use crate::fmt; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::unsupported; use crate::time::Duration; @@ -39,6 +39,10 @@ impl TcpStream { self.0 } + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.0 } diff --git a/library/std/src/sys/unsupported/pipe.rs b/library/std/src/sys/unsupported/pipe.rs index 0bba673b4..d7d8f297a 100644 --- a/library/std/src/sys/unsupported/pipe.rs +++ b/library/std/src/sys/unsupported/pipe.rs @@ -1,4 +1,4 @@ -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; pub struct AnonPipe(!); @@ -7,6 +7,10 @@ impl AnonPipe { self.0 } + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.0 } diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs index 0b9c8e61d..191db4b60 100644 --- a/library/std/src/sys/wasi/fd.rs +++ b/library/std/src/sys/wasi/fd.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] use super::err2io; -use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; use crate::net::Shutdown; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; @@ -46,6 +46,22 @@ impl WasiFd { unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) } } + pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + unsafe { + let bufs = [wasi::Iovec { + buf: buf.as_mut().as_mut_ptr() as *mut u8, + buf_len: buf.capacity(), + }]; + match wasi::fd_read(self.as_raw_fd() as wasi::Fd, &bufs) { + Ok(n) => { + buf.advance(n); + Ok(()) + } + Err(e) => Err(err2io(e)), + } + } + } + pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) } } diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index d4866bbc3..3a205267e 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -441,7 +441,7 @@ impl File { } pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - crate::io::default_read_buf(|buf| self.read(buf), cursor) + self.fd.read_buf(cursor) } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs index cf4ebba1a..59d94a368 100644 --- a/library/std/src/sys/wasi/net.rs +++ b/library/std/src/sys/wasi/net.rs @@ -3,7 +3,7 @@ use super::err2io; use super::fd::WasiFd; use crate::fmt; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::unsupported; @@ -91,6 +91,10 @@ impl TcpStream { self.read_vectored(&mut [IoSliceMut::new(buf)]) } + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.socket().as_inner().read_buf(buf) + } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.socket().as_inner().read(bufs) } 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<u16>) -> io::Result<Vec<u16>> { +pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> { use crate::ptr; use crate::sys::windows::fill_utf16_buf; @@ -324,6 +325,8 @@ pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> { 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<u16>) -> io::Result<Vec<u16>> { 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<u16> = full_path.into(); + path.push(0); + path + } else { + path + } }, ) }, @@ -350,7 +359,9 @@ pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> { |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<u16> = 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<u16>) -> io::Result<Vec<u16>> { ) }, // 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<IO_APC_ROUTINE>, + ApcContext: *mut c_void, + IoStatusBlock: &mut IO_STATUS_BLOCK, + Buffer: *mut crate::mem::MaybeUninit<u8>, + Length: ULONG, + ByteOffset: Option<&LARGE_INTEGER>, + Key: Option<&ULONG>, + ) -> NTSTATUS; + pub fn NtWriteFile( + FileHandle: BorrowedHandle<'_>, + Event: HANDLE, + ApcRoutine: Option<IO_APC_ROUTINE>, + 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<IO_APC_ROUTINE>, - ApcContext: *mut c_void, - IoStatusBlock: &mut IO_STATUS_BLOCK, - Buffer: *mut crate::mem::MaybeUninit<u8>, - Length: ULONG, - ByteOffset: Option<&LARGE_INTEGER>, - Key: Option<&ULONG> - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - pub fn NtWriteFile( - FileHandle: BorrowedHandle<'_>, - Event: HANDLE, - ApcRoutine: Option<IO_APC_ROUTINE>, - 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<FileAttr> { - 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<FileAttr> { @@ -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::<u8>::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::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>(); // Zero the header to ensure it's fully initialized, including reserved parameters. *db = mem::zeroed(); - let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>(); - let mut i = 0; + let reparse_target_slice = { + let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>(); + // 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::<c::WCHAR>(); + 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<usize> { (**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<usize> { + 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<usize> { + 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<usize> { - 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<Vec<u16>> { + 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<u16>, prefer_verbatim: bool) -> io::Result<Vec<u16>> { // 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<Vec<u16>> { // \\?\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<Vec<u16>> { |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<usize> { 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<Vec<u16>> { 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) |