summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:39 +0000
commit1376c5a617be5c25655d0d7cb63e3beaa5a6e026 (patch)
tree3bb8d61aee02bc7a15eab3f36e3b921afc2075d0 /library/std/src/sys
parentReleasing progress-linux version 1.69.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-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')
-rw-r--r--library/std/src/sys/common/alloc.rs1
-rw-r--r--library/std/src/sys/common/mod.rs1
-rw-r--r--library/std/src/sys/common/thread_local/fast_local.rs254
-rw-r--r--library/std/src/sys/common/thread_local/mod.rs109
-rw-r--r--library/std/src/sys/common/thread_local/os_local.rs197
-rw-r--r--library/std/src/sys/common/thread_local/static_local.rs115
-rw-r--r--library/std/src/sys/hermit/fs.rs2
-rw-r--r--library/std/src/sys/hermit/futex.rs6
-rw-r--r--library/std/src/sys/hermit/mod.rs14
-rw-r--r--library/std/src/sys/hermit/net.rs31
-rw-r--r--library/std/src/sys/mod.rs9
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/alloc.rs1
-rw-r--r--library/std/src/sys/sgx/fd.rs6
-rw-r--r--library/std/src/sys/sgx/net.rs6
-rw-r--r--library/std/src/sys/solid/net.rs23
-rw-r--r--library/std/src/sys/unix/fd.rs9
-rw-r--r--library/std/src/sys/unix/fs.rs9
-rw-r--r--library/std/src/sys/unix/futex.rs2
-rw-r--r--library/std/src/sys/unix/kernel_copy.rs62
-rw-r--r--library/std/src/sys/unix/kernel_copy/tests.rs42
-rw-r--r--library/std/src/sys/unix/net.rs39
-rw-r--r--library/std/src/sys/unix/os.rs6
-rw-r--r--library/std/src/sys/unix/pipe.rs6
-rw-r--r--library/std/src/sys/unix/process/process_fuchsia.rs2
-rw-r--r--library/std/src/sys/unix/rand.rs5
-rw-r--r--library/std/src/sys/unix/stdio.rs6
-rw-r--r--library/std/src/sys/unix/time.rs50
-rw-r--r--library/std/src/sys/unsupported/net.rs6
-rw-r--r--library/std/src/sys/unsupported/pipe.rs6
-rw-r--r--library/std/src/sys/wasi/fd.rs18
-rw-r--r--library/std/src/sys/wasi/fs.rs2
-rw-r--r--library/std/src/sys/wasi/net.rs6
-rw-r--r--library/std/src/sys/windows/args.rs23
-rw-r--r--library/std/src/sys/windows/c.rs87
-rw-r--r--library/std/src/sys/windows/c/errors.rs2
-rw-r--r--library/std/src/sys/windows/fs.rs46
-rw-r--r--library/std/src/sys/windows/handle.rs9
-rw-r--r--library/std/src/sys/windows/net.rs28
-rw-r--r--library/std/src/sys/windows/path.rs65
-rw-r--r--library/std/src/sys/windows/pipe.rs24
-rw-r--r--library/std/src/sys/windows/process.rs12
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)