Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

1
vendor/rustix/.cargo-checksum.json vendored Normal file

File diff suppressed because one or more lines are too long

397
vendor/rustix/CHANGES.md vendored Normal file
View File

@@ -0,0 +1,397 @@
# Changes from 0.38.x to 1.x
## Silent behavior changes
[`rustix::pipe::fcntl_setpipe_size`] now returns the new size, which may be
greater than the requested size.
[`rustix::pipe::fcntl_setpipe_size`]: https://docs.rs/rustix/1/rustix/pipe/fn.fcntl_setpipe_size.html
When a `&mut Vec<_>` is passed to [`rustix::event::epoll::wait`],
[`rustix::event::kqueue::kevent`], or [`rustix::event::port::getn`], these
functions previously adjusted the length of the `Vec` to the number of elements
written, and now do not. A common alternative is to wrap the `&mut Vec<_>`
using [`spare_capacity`], and then to clear the `Vec` by iterating using
`.drain(..)` after each call. For an example of using `spare_capacity` in this
way, see [here].
[`rustix::event::epoll::wait`]: https://docs.rs/rustix/1/rustix/event/epoll/fn.wait.html
[`rustix::event::kqueue::kevent`]: https://docs.rs/rustix/1/x86_64-unknown-freebsd/rustix/event/kqueue/fn.kevent.html
[`rustix::event::port::getn`]: https://docs.rs/rustix/1/x86_64-unknown-illumos/rustix/event/port/fn.getn.html
[`spare_capacity`]: https://docs.rs/rustix/1/rustix/buffer/fn.spare_capacity.html
[here]: https://docs.rs/rustix/1/rustix/event/epoll/index.html#examples
## API changes
`rustix::thread::FutexOperation` and `rustix::thread::futex` are removed. Use
the functions in the [`rustix::thread::futex`] module instead.
[`rustix::thread::futex`]: https://docs.rs/rustix/1/rustix/thread/futex/index.html
[`rustix::process::waitpid`]'s return type changed from `WaitStatus` to
`(Pid, WaitStatus)`, to additionally return the pid of the child.
[`rustix::process::waitpid`]: https://docs.rs/rustix/1/rustix/process/fn.waitpid.html
[`terminating_signal`] and other functions in [`rustix::process::WaitStatus`] changed
from returning `u32` to returning `i32`, for better compatibility with the new
[`Signal`] type and [`exit`].
[`terminating_signal`]: https://docs.rs/rustix/1/rustix/process/struct.WaitStatus.html#method.terminating_signal
[`rustix::process::WaitStatus`]: https://docs.rs/rustix/1/rustix/process/struct.WaitStatus.html
[`Signal`]: https://docs.rs/rustix/1/rustix/process/struct.Signal.html
[`exit`]: std::process::exit
The `SLAVE` flag in [`rustix::mount::MountPropagationFlags`] is renamed to
[`DOWNSTREAM`].
[`rustix::mount::MountPropagationFlags`]: https://docs.rs/rustix/1/rustix/mount/struct.MountPropagationFlags.html
[`DOWNSTREAM`]: https://docs.rs/rustix/1/rustix/mount/struct.MountPropagationFlags.html#associatedconstant.DOWNSTREAM
The "cc" and "libc-extra-traits" features are removed. The "cc" feature hasn't
had any effect for several major releases. If you need the traits provided by
"libc-extra-traits", you should instead depend on libc directly and enable its
"extra_traits" feature.
`rustix::net::Shutdown::ReadWrite` is renamed to
[`rustix::net::Shutdown::Both`] to [align with std].
[`rustix::net::Shutdown::Both`]: https://docs.rs/rustix/1/rustix/net/enum.Shutdown.html#variant.Both
[align with std]: https://doc.rust-lang.org/stable/std/net/enum.Shutdown.html#variant.Both
The `rustix::io_uring::io_uring_register_files_skip` function is replaced with
a [`IORING_REGISTER_FILES_SKIP`] constant, similar to the [`rustix::fs::CWD`]
constant.
[`IORING_REGISTER_FILES_SKIP`]: https://docs.rs/rustix/1/rustix/io_uring/constant.IORING_REGISTER_FILES_SKIP.html
[`rustix::fs::CWD`]: https://docs.rs/rustix/1/rustix/fs/constant.CWD.html
[`rustix::io_uring::io_uring_register`] now has a [`IoringRegisterFlags`]
argument, and `rustix::io_uring::io_uring_register_with` is removed.
[`rustix::io_uring::io_uring_register`]: https://docs.rs/rustix/1/rustix/io_uring/fn.io_uring_register.html
[`IoringRegisterFlags`]: https://docs.rs/rustix/1/rustix/io_uring/struct.IoringRegisterFlags.html
Several structs in [`rustix::io_uring`] are now marked `#[non_exhaustive]`
because they contain padding or reserved fields. Instead of constructing
them with field values and `..Default::default()`, construct them with
`Default::default()` and separately assign the fields.
[`rustix::io_uring`]: https://docs.rs/rustix/1/rustix/io_uring/index.html
[`rustix::process::Resource`], [`rustix::thread::MembarrierCommand`], and
[`rustix::thread::Capability`] are now marked `#[non_exhaustive]` to ease
migration in case new constants are defined in the future.
[`rustix::process::Resource`]: https://docs.rs/rustix/1/rustix/process/enum.Resource.html
[`rustix::thread::MembarrierCommand`]: https://docs.rs/rustix/1/rustix/thread/enum.MembarrierCommand.html
[`rustix::thread::Capability`]: https://docs.rs/rustix/1/rustix/thread/enum.Capability.html
`rustix::process::WaitidOptions` and `rustix::process::WaitidStatus` are
renamed to
[`rustix::process::WaitIdOptions`] and [`rustix::process::WaitIdStatus`] (note
the capitalization), for consistency with [`rustix::process::WaitId`].
[`rustix::process::WaitIdOptions`]: https://docs.rs/rustix/1/rustix/process/struct.WaitIdOptions.html
[`rustix::process::WaitIdStatus`]: https://docs.rs/rustix/1/rustix/process/struct.WaitIdStatus.html
[`rustix::process::WaitId`]: https://docs.rs/rustix/1/rustix/process/enum.WaitId.html
The offsets in [`rustix::fs::SeekFrom::Hole`] and
[`rustix::fs::SeekFrom::Data`] are changed from `i64` to `u64`, to
[align with std], since they represent absolute offsets.
[`rustix::fs::SeekFrom::Hole`]: https://docs.rs/rustix/1/rustix/fs/enum.SeekFrom.html#variant.Hole
[`rustix::fs::SeekFrom::Data`]: https://docs.rs/rustix/1/rustix/fs/enum.SeekFrom.html#variant.Data
[align with std]: https://doc.rust-lang.org/stable/std/io/enum.SeekFrom.html#variant.Start
Functions in [`rustix::net::sockopt`] are renamed to remove the `get_` prefix,
to [align with Rust conventions].
[`rustix::net::sockopt`]: https://docs.rs/rustix/1/rustix/net/sockopt/index.html
[align with Rust conventions]: https://rust-lang.github.io/api-guidelines/naming.html#getter-names-follow-rust-convention-c-getter
`rustix::process::sched_*` and `rustix::process::membarrier_*` are moved from
[`rustix::process`] to [`rustix::thread`], as they operate on the current
thread rather than the current process.
[`rustix::process`]: https://docs.rs/rustix/1/rustix/process/index.html
[`rustix::thread`]: https://docs.rs/rustix/1/rustix/thread/index.html
The `udata` in [`rustix::event::kqueue::Event`] is changed from `isize` to
`*mut c_void` to better propagate pointer provenance. To use arbitrary integer
values, convert using the [`without_provenance_mut`] and the [`.addr()`]
functions.
[`rustix::event::kqueue::Event`]: https://docs.rs/rustix/1/x86_64-unknown-freebsd/rustix/event/kqueue/struct.Event.html
[`without_provenance_mut`]: https://doc.rust-lang.org/stable/std/ptr/fn.without_provenance_mut.html
[`.addr()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.addr
`rustix::mount::mount_recursive_bind` is renamed to
[`rustix::mount::mount_bind_recursive`]. See [this comment] for details.
[`rustix::mount::mount_bind_recursive`]: https://docs.rs/rustix/1/rustix/mount/fn.mount_bind_recursive.html
[this comment]: https://github.com/bytecodealliance/rustix/pull/763#issuecomment-1662756184
The `rustix::procfs` is removed. This functionality is now available in the
[rustix-linux-procfs crate].
[rustix-linux-procfs crate]: https://crates.io/crates/rustix-linux-procfs
`rustix::net::RecvMsgReturn` is renamed to [`rustix::net::RecvMsg`].
[`rustix::net::RecvMsg`]: https://docs.rs/rustix/1/rustix/net/struct.RecvMsg.html
The `flags` field of [`rustix::net::RecvMsg`] changed type from [`RecvFlags`]
to a new [`ReturnFlags`], since it supports a different set of flags.
[`rustix::net::RecvMsg`]: https://docs.rs/rustix/1/rustix/net/struct.RecvMsg.html
[`RecvFlags`]: https://docs.rs/rustix/1/rustix/net/struct.RecvFlags.html
[`ReturnFlags`]: https://docs.rs/rustix/1/rustix/net/struct.ReturnFlags.html
[`rustix::event::poll`]'s and [`rustix::event::epoll`]'s `timeout` argument
changed from a `c_int` where `-1` means no timeout and non-negative numbers
mean a timeout in milliseconds to an `Option<&Timespec>`. The [`Timespec`]'s
fields are `tv_sec` which holds seconds and `tv_nsec` which holds nanoseconds.
[`rustix::event::poll`]: https://docs.rs/rustix/1/rustix/event/fn.poll.html
[`rustix::event::epoll`]: https://docs.rs/rustix/1/rustix/event/epoll/index.html
[`Timespec`]: https://docs.rs/rustix/1/rustix/time/struct.Timespec.html
The timeout argument in [`rustix::thread::futex::wait`],
[`rustix::thread::futex::lock_pi`], [`rustix::thread::futex::wait_bitset`],
[`rustix::thread::futex::wait_requeue_pi`], and
[`rustix::thread::futex::lock_pi2`] changed from `Option<Timespec>` to
`Option<&Timespec>`, for consistency with the rest of rustix's API, and for
low-level efficiency, as it means the implementation doesn't need to make a
copy of the `Timespec` to take its address. An easy way to convert an
`Option<Timespec>` to an `Option<&Timespec> is to use [`Option::as_ref`].
[`rustix::thread::futex::wait`]: https://docs.rs/rustix/1/rustix/thread/futex/fn.wait.html
[`rustix::thread::futex::lock_pi`]: https://docs.rs/rustix/1/rustix/thread/futex/fn.lock_pi.html
[`rustix::thread::futex::wait_bitset`]: https://docs.rs/rustix/1/rustix/thread/futex/fn.wait_bitset.html
[`rustix::thread::futex::wait_requeue_pi`]: https://docs.rs/rustix/1/rustix/thread/futex/fn.wait_requeue_pi.html
[`rustix::thread::futex::lock_pi2`]: https://docs.rs/rustix/1/rustix/thread/futex/fn.lock_pi2.html
[`Option::as_ref`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.as_ref
Functions in [`rustix::event::port`] are renamed to remove the redundant
`port_*` prefix.
[`rustix::event::port`]: https://docs.rs/rustix/1/x86_64-unknown-illumos/rustix/event/port/index.html
`rustix::fs::inotify::InotifyEvent` is renamed to
[`rustix::fs::inotify::Event`] to remove the redundant prefix.
[`rustix::fs::inotify::Event`]: https://docs.rs/rustix/1/rustix/fs/inotify/struct.Event.html
`rustix::fs::StatExt` is removed, and the timestamp fields `st_atime`,
`st_mtime`, and `st_ctime` of [`rustix::fs::Stat`] may now be accessed
directly. They are now signed instead of unsigned, so that they can represent
times before the epoch.
[`rustix::fs::Stat`]: https://docs.rs/rustix/1/rustix/fs/struct.Stat.html
`rustix::io::is_read_write` is removed, as it's higher-level functionality that
can be implemented in terms of lower-level rustix calls.
[`rustix::net::recv`] and [`rustix::net::recvfrom`] now include
the number of received bytes in their return types, as this number may differ
from the number of bytes written to the buffer when
[`rustix::net::RecvFlags::TRUNC`] is used.
[`rustix::net::recv`]: https://docs.rs/rustix/1/rustix/net/fn.recv.html
[`rustix::net::recvfrom`]: https://docs.rs/rustix/1/rustix/net/fn.recvfrom.html
[`rustix::net::RecvFlags::TRUNC`]: https://docs.rs/rustix/1/rustix/net/struct.RecvFlags.html#associatedconstant.TRUNC
[`rustix::process::Signal`] constants are now upper-cased; for example,
`Signal::Int` is now named [`Signal::INT`]. Also, `Signal` is no longer
directly convertible to `i32`; use [`Signal::as_raw`] instead.
[`rustix::process::Signal`]: https://docs.rs/rustix/1/rustix/process/struct.Signal.html
[`Signal::INT`]: https://docs.rs/rustix/1/rustix/process/struct.Signal.html#variant.Int
[`Signal::as_raw`]: https://docs.rs/rustix/1/rustix/process/struct.Signal.html#method.as_raw
`Signal::from_raw` is renamed to [`Signal::from_named_raw`].
[`Signal::from_named_raw`]: https://docs.rs/rustix/1/rustix/process/struct.Signal.html#method.from_named_raw
The associated constant `rustix::ioctl::Ioctl::OPCODE` is now replaced with an
associated method [`rustix::ioctl::Ioctl::opcode`], to support ioctls where the
opcode is computed rather than a constant.
[`rustix::ioctl::Ioctl::opcode`]: https://docs.rs/rustix/1/rustix/ioctl/trait.Ioctl.html#tymethod.opcode
The `ifindex` argument in
[`rustix::net::sockopt::set_ip_add_membership_with_ifindex`] and
[`rustix::net::sockopt::set_ip_drop_membership_with_ifindex`]
changed from `i32` to `u32`.
[`rustix::net::sockopt::set_ip_add_membership_with_ifindex`]: https://docs.rs/rustix/1/rustix/net/sockopt/fn.set_ip_add_membership_with_ifindex.html
[`rustix::net::sockopt::set_ip_drop_membership_with_ifindex`]: https://docs.rs/rustix/1/rustix/net/sockopt/fn.set_ip_drop_membership_with_ifindex.html
The `list` argument in [`rustix::fs::listxattr`], [`rustix::fs::flistxattr`],
and [`rustix::fs::llistxattr`] changed from `[c_char]`, which is `[i8]` on some
architectures, to `[u8]`.
[`rustix::fs::listxattr`]: https://docs.rs/rustix/1/rustix/fs/fn.listxattr.html
[`rustix::fs::flistxattr`]: https://docs.rs/rustix/1/rustix/fs/fn.flistxattr.html
[`rustix::fs::llistxattr`]: https://docs.rs/rustix/1/rustix/fs/fn.llistxattr.html
On NetBSD, the nanoseconds fields of [`Stat`] have been renamed, for consistency
with other platforms:
| Old name | New Name |
| -------------- | --------------- |
| `st_atimensec` | `st_atime_nsec` |
| `st_mtimensec` | `st_mtime_nsec` |
| `st_ctimensec` | `st_ctime_nsec` |
| `st_birthtimensec` | `st_birthtime_nsec` |
[`Stat`]: https://docs.rs/rustix/1/x86_64-unknown-netbsd/rustix/fs/struct.Stat.html
[`rustix::mount::mount`]'s `data` argument is now an `Option`, so it can now
be used in place of `mount2`, and `mount2` is now removed.
[`rustix::mount::mount`]: https://docs.rs/rustix/1/rustix/mount/fn.mount.html
The [`rustix::net`] functions ending with `_v4`, `_v6`, `_unix` and `_xdp` have
been merged into a single function that accepts any address type.
Specifically, the following functions are removed:
* `bind_any`, `bind_unix`, `bind_v4`, `bind_v6`, `bind_xdp` in favor of
[`bind`],
* `connect_any`, `connect_unix`, `connect_v4`, `connect_v6` in favor of
[`connect`] (leaving address-less [`connect_unspec`]),
* `sendmsg_v4`, `sendmsg_v6`, `sendmsg_unix`, `sendmsg_xdp`, `sendmsg_any` in
favor of [`sendmsg_addr`] (leaving address-less [`sendmsg`]),
* `sendto_any`, `sendto_v4`, `sendto_v6`, `sendto_unix`, `sendto_xdp` in
favor of [`sendto`].
[`rustix::net`]: https://docs.rs/rustix/1/rustix/net/index.html
[`bind`]: https://docs.rs/rustix/1/rustix/net/fn.bind.html
[`connect`]: https://docs.rs/rustix/1/rustix/net/fn.connect.html
[`connect_unspec`]: https://docs.rs/rustix/1/rustix/net/fn.connect_unspec.html
[`sendmsg_addr`]: https://docs.rs/rustix/1/rustix/net/fn.sendmsg_addr.html
[`sendmsg`]: https://docs.rs/rustix/1/rustix/net/fn.sendmsg.html
[`sendto`]: https://docs.rs/rustix/1/rustix/net/fn.sendto.html
The `SocketAddrAny` enum has changed to a [`SocketAddrAny`] struct which can
contain any kind of socket address. It can be converted to and from the more
specific socket types using `From`/`Into`/`TryFrom`/`TryInto` conversions.
[`SocketAddrAny`]: https://docs.rs/rustix/1/rustix/net/struct.SocketAddrAny.html
The `len` parameter to [`rustix::fs::fadvise`] has changed from `u64` to
`Option<NonZeroU64>`, to reflect that zero is a special case meaning the
advice applies to the end of the file. To convert an arbitrary `u64` value to
`Option<NonZeroU64>`, use `NonZeroU64::new`.
[`rustix::fs::fadvise`]: https://docs.rs/rustix/1/rustix/fs/fn.fadvise.html
[`rustix::io_uring::io_uring_enter`] no longer has `arg` and `size` arguments
providing a raw `*mut c_void` and `usize` describing the argument value. To
pass argumentts, there are now additional functions, `io_uring_enter_sigmask`,
and `io_uring_enter_arg`, which take a [`KernelSigSet`] or an
`io_uring_getevents_arg`, respectively. These are more ergonomic, and provide
a better path to adding `IORING_ENTER_EXT_ARG_REG` support in the future.
[`rustix::io_uring::io_uring_enter`]: https://docs.rs/rustix/1/rustix/io_uring/fn.io_uring_enter.html
[`KernelSigSet`]: https://docs.rs/rustix/1/rustix/io_uring/struct.KernelSigSet.html
The [`sigmask`] and [`ts`] fields of [`rustix::io_uring::getevents_arg`]
changed from `u64` to [`rustix::io_uring::io_uring_ptr`], to better preserve
pointer provenance.
[`sigmask`]: https://docs.rs/rustix/1/rustix/io_uring/struct.io_uring_getevents_arg.html#structfield.sigmask
[`ts`]: https://docs.rs/rustix/1/rustix/io_uring/struct.io_uring_getevents_arg.html#structfield.ts
[`rustix::io_uring::getevents_arg`]: https://docs.rs/rustix/1/rustix/io_uring/struct.io_uring_getevents_arg.html
[`rustix::io_uring::io_uring_ptr`]: https://docs.rs/rustix/1/rustix/io_uring/struct.io_uring_ptr.html
The aliases for [`fcntl_dupfd_cloexec`], [`fcntl_getfd`], and [`fcntl_setfd`]
in `rustix::fs` are removed; these functions are just available in
[`rustix::io`] now.
[`fcntl_dupfd_cloexec`]: https://docs.rs/rustix/1/rustix/io/fn.fcntl_dupfd_cloexec.html
[`fcntl_getfd`]: https://docs.rs/rustix/1/rustix/io/fn.fcntl_getfd.html
[`fcntl_setfd`]: https://docs.rs/rustix/1/rustix/io/fn.fcntl_setfd.html
[`rustix::io`]: https://docs.rs/rustix/1/rustix/io/index.html
[`SocketAddrXdp`] no longer has a shared UMEM field. A new
[`SocketAddrXdpWithSharedUmem`] is added for the purpose of calling `bind` and
passing it an XDP address with a shared UMEM fd. And `SockaddrXdpFlags` is
renamed to [`SocketAddrXdpFlags`].
[`SocketAddrXdp`]: https://docs.rs/rustix/1/rustix/net/xdp/struct.SocketAddrXdp.html
[`SocketAddrXdpWithSharedUmem`]: https://docs.rs/rustix/1/rustix/net/xdp/struct.SocketAddrXdpWithSharedUmem.html
[`SocketAddrXdpFlags`]: https://docs.rs/rustix/1/rustix/net/xdp/struct.SocketAddrXdpFlags.html
[`rustix::io_uring::io_uring_setup`] is now unsafe, due its `io_uring_params`
argument optionally containing a raw file descriptor.
[`rustix::io_uring::io_uring_setup`]: https://docs.rs/rustix/1/rustix/io_uring/fn.io_uring_setup.html
The buffer for [`SendAncillaryBuffer`] and [`RecvAncillaryBuffer`] is now
a `[MaybeUninit<u8>]` instead of a `[u8]`.
[`SendAncillaryBuffer`]: https://docs.rs/rustix/1/rustix/net/struct.SendAncillaryBuffer.html
[`RecvAncillaryBuffer`]: https://docs.rs/rustix/1/rustix/net/struct.RecvAncillaryBuffer.html
[`read`], [`pread`], [`recv`], [`recvfrom`], [`getrandom`], [`readlinkat_raw`],
[`epoll::wait`], [`kevent`], [`port::getn`], [`getxattr`], [`lgetxattr`],
[`fgetxattr`], [`listxattr`], [`llistxattr`], and [`flistxattr`] now use the
new [`Buffer` trait].
This replaces `read_uninit`, `pread_uninit`, `recv_uninit`, `recvfrom_uninit`,
and `getrandom_uninit`, as the `Buffer` trait supports reading into
uninitialized slices.
`epoll::wait`, `kevent`, and `port::getn` previously took a `Vec` which they
implicitly cleared before results were appended. When passing a `Vec` to
`epoll::wait`, `kevent`, or `port::getn` using [`spare_capacity`], the `Vec` is
not cleared first. Consider clearing the vector before calling `epoll::wait`,
`kevent`, or `port::getn`, or consuming it using `.drain(..)` before reusing it.
[`read`]: https://docs.rs/rustix/1/rustix/io/fn.read.html
[`pread`]: https://docs.rs/rustix/1/rustix/io/fn.pread.html
[`recv`]: https://docs.rs/rustix/1/rustix/net/fn.recv.html
[`recvfrom`]: https://docs.rs/rustix/1/rustix/net/fn.recvfrom.html
[`getrandom`]: https://docs.rs/rustix/1/rustix/rand/fn.getrandom.html
[`readlinkat_raw`]: https://docs.rs/rustix/1/rustix/fs/fn.readlinkat_raw.html
[`epoll::wait`]: https://docs.rs/rustix/1/rustix/event/epoll/fn.wait.html
[`getxattr`]: https://docs.rs/rustix/1/rustix/fs/fn.getxattr.html
[`lgetxattr`]: https://docs.rs/rustix/1/rustix/fs/fn.lgetxattr.html
[`fgetxattr`]: https://docs.rs/rustix/1/rustix/fs/fn.fgetxattr.html
[`listxattr`]: https://docs.rs/rustix/1/rustix/fs/fn.listxattr.html
[`llistxattr`]: https://docs.rs/rustix/1/rustix/fs/fn.llistxattr.html
[`flistxattr`]: https://docs.rs/rustix/1/rustix/fs/fn.flistxattr.html
[`kevent`]: https://docs.rs/rustix/1/x86_64-unknown-freebsd/rustix/event/kqueue/fn.kevent.html
[`port::getn`]: https://docs.rs/rustix/1/x86_64-unknown-illumos/rustix/event/port/fn.getn.html
[`Buffer` trait]: https://docs.rs/rustix/1/rustix/buffer/trait.Buffer.html
[`spare_capacity`]: https://docs.rs/rustix/1/rustix/buffer/fn.spare_capacity.html
The [`rustix::ioctl::Opcode`] type has changed from a struct to a raw integer
value, and the associated utilities are change to `const` functions. In place
of `ReadOpcode`, `WriteOpcode`, `ReadWriteOpcode`, and `NoneOpcode`, use the
`read`, `write`, `read_write`, and `none` const functions in the
[`ioctl::opcode`] module. For example, in place of this:
```rust
ioctl::Setter::<ioctl::ReadOpcode<b'U', 15, c_uint>, c_uint>::new(interface)
```
use this:
```rust
+ ioctl::Setter::<{ ioctl::opcode::read::<c_uint>(b'U', 15) }, c_uint>::new(interface)
```
.
In place of `BadOpcode`, use the opcode value directly.
[`rustix::ioctl::Opcode`]: https://docs.rs/rustix/1/rustix/ioctl/type.Opcode.html
[`ioctl::opcode`]: https://docs.rs/rustix/1/rustix/ioctl/opcode/index.html
[`rustix::event::port::getn`]'s `min_events` argument is now a `u32`, to
reflect the type in the underlying system API.
[`rustix::event::port::getn`]: https://docs.rs/rustix/1/x86_64-unknown-illumos/rustix/event/port/fn.getn.html
All explicitly deprecated functions and types have been removed. Their
deprecation messages will have identified alternatives.

49
vendor/rustix/CODE_OF_CONDUCT.md vendored Normal file
View File

@@ -0,0 +1,49 @@
# Contributor Covenant Code of Conduct
*Note*: this Code of Conduct pertains to individuals' behavior. Please also see the [Organizational Code of Conduct][OCoC].
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Bytecode Alliance CoC team at [report@bytecodealliance.org](mailto:report@bytecodealliance.org). The CoC team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The CoC team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the Bytecode Alliance's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[OCoC]: https://github.com/bytecodealliance/rustix/blob/main/ORG_CODE_OF_CONDUCT.md
[homepage]: https://www.contributor-covenant.org
[version]: https://www.contributor-covenant.org/version/1/4/

27
vendor/rustix/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,27 @@
# Contributing to rustix
Rustix is a [Bytecode Alliance] project. It follows the Bytecode Alliance's
[Code of Conduct] and [Organizational Code of Conduct].
## Testing
To keep compile times low, most features in rustix's API are behind cargo
features. A special feature, `all-apis` enables all APIs, which is useful
for testing.
```console
cargo test --features=all-apis
```
And, rustix has two backends, linux_raw and libc, and only one is used in
any given build. To test on Linux with the libc backend explicitly,
additionally enable the `use-libc` feature:
```console
cargo test --features=all-apis,use-libc
```
Beyond that, rustix's CI tests many targets and configurations. Asking for
help is always welcome, and it's especially encouraged when the issue is
getting all the `cfg`s lined up to get everything compiling on all the
configurations on CI.

29
vendor/rustix/COPYRIGHT vendored Normal file
View File

@@ -0,0 +1,29 @@
Short version for non-lawyers:
`rustix` is triple-licensed under Apache 2.0 with the LLVM Exception,
Apache 2.0, and MIT terms.
Longer version:
Copyrights in the `rustix` project are retained by their contributors.
No copyright assignment is required to contribute to the `rustix`
project.
Some files include code derived from Rust's `libstd`; see the comments in
the code for details.
Except as otherwise noted (below and/or in individual files), `rustix`
is licensed under:
- the Apache License, Version 2.0, with the LLVM Exception
<LICENSE-Apache-2.0_WITH_LLVM-exception> or
<http://llvm.org/foundation/relicensing/LICENSE.txt>
- the Apache License, Version 2.0
<LICENSE-APACHE> or
<http://www.apache.org/licenses/LICENSE-2.0>,
- or the MIT license
<LICENSE-MIT> or
<http://opensource.org/licenses/MIT>,
at your option.

1106
vendor/rustix/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

288
vendor/rustix/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,288 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.63"
name = "rustix"
version = "1.1.2"
authors = [
"Dan Gohman <dev@sunfishcode.online>",
"Jakub Konka <kubkon@jakubkonka.com>",
]
build = "build.rs"
include = [
"src",
"build.rs",
"Cargo.toml",
"COPYRIGHT",
"LICENSE*",
"/*.md",
"benches",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Safe Rust bindings to POSIX/Unix/Linux/Winsock-like syscalls"
documentation = "https://docs.rs/rustix"
readme = "README.md"
keywords = [
"api",
"file",
"network",
"safe",
"syscall",
]
categories = [
"os::unix-apis",
"date-and-time",
"filesystem",
"network-programming",
]
license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
repository = "https://github.com/bytecodealliance/rustix"
[package.metadata.docs.rs]
features = ["all-apis"]
targets = [
"x86_64-unknown-linux-gnu",
"i686-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-unknown-freebsd",
"x86_64-unknown-openbsd",
"x86_64-unknown-netbsd",
"x86_64-unknown-dragonfly",
"x86_64-unknown-illumos",
"x86_64-unknown-redox",
"x86_64-unknown-haiku",
"wasm32-unknown-emscripten",
"wasm32-wasip1",
]
[features]
all-apis = [
"event",
"fs",
"io_uring",
"mm",
"mount",
"net",
"param",
"pipe",
"process",
"pty",
"rand",
"runtime",
"shm",
"stdio",
"system",
"termios",
"thread",
"time",
]
alloc = []
default = ["std"]
event = []
fs = []
io_uring = [
"event",
"fs",
"net",
"thread",
"linux-raw-sys/io_uring",
]
linux_4_11 = []
linux_5_1 = ["linux_4_11"]
linux_5_11 = ["linux_5_1"]
linux_latest = ["linux_5_11"]
mm = []
mount = []
net = [
"linux-raw-sys/net",
"linux-raw-sys/netlink",
"linux-raw-sys/if_ether",
"linux-raw-sys/xdp",
]
param = []
pipe = []
process = ["linux-raw-sys/prctl"]
pty = ["fs"]
rand = []
runtime = ["linux-raw-sys/prctl"]
rustc-dep-of-std = [
"core",
"rustc-std-workspace-alloc",
"linux-raw-sys/rustc-dep-of-std",
"bitflags/rustc-dep-of-std",
]
shm = ["fs"]
std = [
"bitflags/std",
"alloc",
"libc?/std",
"libc_errno?/std",
]
stdio = []
system = ["linux-raw-sys/system"]
termios = []
thread = ["linux-raw-sys/prctl"]
time = []
try_close = []
use-explicitly-provided-auxv = []
use-libc = [
"libc_errno",
"libc",
]
use-libc-auxv = []
[lib]
name = "rustix"
path = "src/lib.rs"
[[bench]]
name = "mod"
path = "benches/mod.rs"
harness = false
[dependencies.bitflags]
version = "2.4.0"
default-features = false
[dependencies.core]
version = "1.0.0"
optional = true
package = "rustc-std-workspace-core"
[dependencies.rustc-std-workspace-alloc]
version = "1.0.0"
optional = true
[dev-dependencies.flate2]
version = "1.0"
[dev-dependencies.libc]
version = "0.2.171"
[dev-dependencies.libc_errno]
version = "0.3.10"
default-features = false
package = "errno"
[dev-dependencies.memoffset]
version = "0.9.0"
[dev-dependencies.serial_test]
version = "2.0.0"
[dev-dependencies.static_assertions]
version = "1.1.0"
[dev-dependencies.tempfile]
version = "3.5.0"
[target.'cfg(all(any(target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies.linux-raw-sys]
version = "0.11.0"
features = [
"general",
"ioctl",
"no_std",
]
default-features = false
[target.'cfg(all(criterion, not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies.criterion]
version = "0.4"
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies.libc]
version = "0.2.171"
optional = true
default-features = false
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies.libc_errno]
version = "0.3.10"
optional = true
default-features = false
package = "errno"
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies.linux-raw-sys]
version = "0.11.0"
features = [
"auxvec",
"general",
"errno",
"ioctl",
"no_std",
"elf",
]
default-features = false
[target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies.libc]
version = "0.2.171"
default-features = false
[target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies.libc_errno]
version = "0.3.10"
default-features = false
package = "errno"
[target."cfg(windows)".dependencies.libc_errno]
version = "0.3.10"
default-features = false
package = "errno"
[target."cfg(windows)".dependencies.windows-sys]
version = ">=0.52, <0.62"
features = [
"Win32_Foundation",
"Win32_Networking_WinSock",
]
[target."cfg(windows)".dev-dependencies.once_cell]
version = "1.20.3"
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = [
"cfg(alloc_c_string)",
"cfg(alloc_ffi)",
"cfg(apple)",
"cfg(asm_experimental_arch)",
"cfg(bsd)",
"cfg(core_c_str)",
"cfg(core_ffi_c)",
"cfg(core_intrinsics)",
"cfg(criterion)",
"cfg(document_experimental_runtime_api)",
"cfg(error_in_core)",
"cfg(fix_y2038)",
"cfg(freebsdlike)",
"cfg(libc)",
"cfg(linux_kernel)",
"cfg(linux_like)",
"cfg(linux_raw)",
"cfg(linux_raw_dep)",
"cfg(lower_upper_exp_for_non_zero)",
"cfg(netbsdlike)",
"cfg(rustc_attrs)",
"cfg(rustc_diagnostics)",
"cfg(solarish)",
"cfg(staged_api)",
"cfg(static_assertions)",
"cfg(thumb_mode)",
"cfg(wasi)",
"cfg(wasi_ext)",
"cfg(wasip2)",
'cfg(target_arch, values("xtensa"))',
'cfg(target_os, values("cygwin"))',
]

201
vendor/rustix/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,220 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.

23
vendor/rustix/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

143
vendor/rustix/ORG_CODE_OF_CONDUCT.md vendored Normal file
View File

@@ -0,0 +1,143 @@
# Bytecode Alliance Organizational Code of Conduct (OCoC)
*Note*: this Code of Conduct pertains to organizations' behavior. Please also see the [Individual Code of Conduct](CODE_OF_CONDUCT.md).
## Preamble
The Bytecode Alliance (BA) welcomes involvement from organizations,
including commercial organizations. This document is an
*organizational* code of conduct, intended particularly to provide
guidance to commercial organizations. It is distinct from the
[Individual Code of Conduct (ICoC)](CODE_OF_CONDUCT.md), and does not
replace the ICoC. This OCoC applies to any group of people acting in
concert as a BA member or as a participant in BA activities, whether
or not that group is formally incorporated in some jurisdiction.
The code of conduct described below is not a set of rigid rules, and
we did not write it to encompass every conceivable scenario that might
arise. For example, it is theoretically possible there would be times
when asserting patents is in the best interest of the BA community as
a whole. In such instances, consult with the BA, strive for
consensus, and interpret these rules with an intent that is generous
to the community the BA serves.
While we may revise these guidelines from time to time based on
real-world experience, overall they are based on a simple principle:
*Bytecode Alliance members should observe the distinction between
public community functions and private functions — especially
commercial ones — and should ensure that the latter support, or at
least do not harm, the former.*
## Guidelines
* **Do not cause confusion about Wasm standards or interoperability.**
Having an interoperable WebAssembly core is a high priority for
the BA, and members should strive to preserve that core. It is fine
to develop additional non-standard features or APIs, but they
should always be clearly distinguished from the core interoperable
Wasm.
Treat the WebAssembly name and any BA-associated names with
respect, and follow BA trademark and branding guidelines. If you
distribute a customized version of software originally produced by
the BA, or if you build a product or service using BA-derived
software, use names that clearly distinguish your work from the
original. (You should still provide proper attribution to the
original, of course, wherever such attribution would normally be
given.)
Further, do not use the WebAssembly name or BA-associated names in
other public namespaces in ways that could cause confusion, e.g.,
in company names, names of commercial service offerings, domain
names, publicly-visible social media accounts or online service
accounts, etc. It may sometimes be reasonable, however, to
register such a name in a new namespace and then immediately donate
control of that account to the BA, because that would help the project
maintain its identity.
For further guidance, see the BA Trademark and Branding Policy
[TODO: create policy, then insert link].
* **Do not restrict contributors.** If your company requires
employees or contractors to sign non-compete agreements, those
agreements must not prevent people from participating in the BA or
contributing to related projects.
This does not mean that all non-compete agreements are incompatible
with this code of conduct. For example, a company may restrict an
employee's ability to solicit the company's customers. However, an
agreement must not block any form of technical or social
participation in BA activities, including but not limited to the
implementation of particular features.
The accumulation of experience and expertise in individual persons,
who are ultimately free to direct their energy and attention as
they decide, is one of the most important drivers of progress in
open source projects. A company that limits this freedom may hinder
the success of the BA's efforts.
* **Do not use patents as offensive weapons.** If any BA participant
prevents the adoption or development of BA technologies by
asserting its patents, that undermines the purpose of the
coalition. The collaboration fostered by the BA cannot include
members who act to undermine its work.
* **Practice responsible disclosure** for security vulnerabilities.
Use designated, non-public reporting channels to disclose technical
vulnerabilities, and give the project a reasonable period to
respond, remediate, and patch. [TODO: optionally include the
security vulnerability reporting URL here.]
Vulnerability reporters may patch their company's own offerings, as
long as that patching does not significantly delay the reporting of
the vulnerability. Vulnerability information should never be used
for unilateral commercial advantage. Vendors may legitimately
compete on the speed and reliability with which they deploy
security fixes, but withholding vulnerability information damages
everyone in the long run by risking harm to the BA project's
reputation and to the security of all users.
* **Respect the letter and spirit of open source practice.** While
there is not space to list here all possible aspects of standard
open source practice, some examples will help show what we mean:
* Abide by all applicable open source license terms. Do not engage
in copyright violation or misattribution of any kind.
* Do not claim others' ideas or designs as your own.
* When others engage in publicly visible work (e.g., an upcoming
demo that is coordinated in a public issue tracker), do not
unilaterally announce early releases or early demonstrations of
that work ahead of their schedule in order to secure private
advantage (such as marketplace advantage) for yourself.
The BA reserves the right to determine what constitutes good open
source practices and to take action as it deems appropriate to
encourage, and if necessary enforce, such practices.
## Enforcement
Instances of organizational behavior in violation of the OCoC may
be reported by contacting the Bytecode Alliance CoC team at
[report@bytecodealliance.org](mailto:report@bytecodealliance.org). The
CoC team will review and investigate all complaints, and will respond
in a way that it deems appropriate to the circumstances. The CoC team
is obligated to maintain confidentiality with regard to the reporter of
an incident. Further details of specific enforcement policies may be
posted separately.
When the BA deems an organization in violation of this OCoC, the BA
will, at its sole discretion, determine what action to take. The BA
will decide what type, degree, and duration of corrective action is
needed, if any, before a violating organization can be considered for
membership (if it was not already a member) or can have its membership
reinstated (if it was a member and the BA canceled its membership due
to the violation).
In practice, the BA's first approach will be to start a conversation,
with punitive enforcement used only as a last resort. Violations
often turn out to be unintentional and swiftly correctable with all
parties acting in good faith.

208
vendor/rustix/README.md vendored Normal file
View File

@@ -0,0 +1,208 @@
<div align="center">
<h1><code>rustix</code></h1>
<p>
<strong>Safe Rust bindings to POSIX/Unix/Linux/Winsock syscalls</strong>
</p>
<strong>A <a href="https://bytecodealliance.org/">Bytecode Alliance</a> project</strong>
<p>
<a href="https://github.com/bytecodealliance/rustix/actions?query=workflow%3ACI"><img src="https://github.com/bytecodealliance/rustix/workflows/CI/badge.svg" alt="Github Actions CI Status" /></a>
<a href="https://bytecodealliance.zulipchat.com/#narrow/stream/206238-general"><img src="https://img.shields.io/badge/zulip-join_chat-brightgreen.svg" alt="zulip chat" /></a>
<a href="https://crates.io/crates/rustix"><img src="https://img.shields.io/crates/v/rustix.svg" alt="crates.io page" /></a>
<a href="https://docs.rs/rustix"><img src="https://docs.rs/rustix/badge.svg" alt="docs.rs docs" /></a>
</p>
</div>
`rustix` provides efficient memory-safe and [I/O-safe] wrappers to POSIX-like,
Unix-like, Linux, and Winsock syscall-like APIs, with configurable backends. It
uses Rust references, slices, and return values instead of raw pointers, and
[I/O safety types] instead of raw file descriptors, providing memory safety,
[I/O safety], and [provenance]. It uses `Result`s for reporting errors,
[`bitflags`] instead of bare integer flags, an [`Arg`] trait with optimizations
to efficiently accept any Rust string type, and several other efficient
conveniences.
`rustix` is low-level and, and while the `net` API supports [Windows Sockets 2]
(Winsock), the rest of the APIs do not support Windows; for higher-level and
more portable APIs built on this functionality, see the [`cap-std`], [`memfd`],
[`timerfd`], and [`io-streams`] crates, for example.
`rustix` currently has two backends available:
* linux_raw, which uses raw Linux system calls and vDSO calls, and is
supported on Linux on x86-64, x86, aarch64, riscv64gc, powerpc64le,
arm (v5 onwards), mipsel, and mips64el, with stable, nightly, and 1.63 Rust.
- By being implemented entirely in Rust, avoiding `libc`, `errno`, and pthread
cancellation, and employing some specialized optimizations, most functions
compile down to very efficient code, which can often be fully inlined into
user code.
- Most functions in `linux_raw` preserve memory, I/O safety, and pointer
provenance all the way down to the syscalls.
* libc, which uses the [`libc`] crate which provides bindings to native `libc`
libraries on Unix-family platforms, and [`windows-sys`] for Winsock on
Windows, and is portable to many OS's.
The linux_raw backend is enabled by default on platforms which support it. To
enable the libc backend instead, either enable the "use-libc" cargo feature, or
set the `RUSTFLAGS` environment variable to `--cfg=rustix_use_libc` when
building.
## Cargo features
The modules [`rustix::io`], [`rustix::buffer`], [`rustix::fd`],
[`rustix::ffi`], and [`rustix::ioctl`] are enabled by default. The rest of the
API modules are conditional with cargo feature flags.
| Name | Description |
| ---------- | -------------------------------------------------------------- |
| `event` | [`rustix::event`]—Polling and event operations. |
| `fs` | [`rustix::fs`]—Filesystem operations. |
| `io_uring` | [`rustix::io_uring`]—Linux io_uring. |
| `mm` | [`rustix::mm`]—Memory map operations. |
| `mount` | [`rustix::mount`]—Linux mount API. |
| `net` | [`rustix::net`]—Network-related operations. |
| `param` | [`rustix::param`]—Process parameters. |
| `pipe` | [`rustix::pipe`]—Pipe operations. |
| `process` | [`rustix::process`]—Process-associated operations. |
| `pty` | [`rustix::pty`]—Pseudoterminal operations. |
| `rand` | [`rustix::rand`]—Random-related operations. |
| `shm` | [`rustix::shm`]—POSIX shared memory. |
| `stdio` | [`rustix::stdio`]—Stdio-related operations. |
| `system` | [`rustix::system`]—System-related operations. |
| `termios` | [`rustix::termios`]—Terminal I/O stream operations. |
| `thread` | [`rustix::thread`]—Thread-associated operations. |
| `time` | [`rustix::time`]—Time-related operations. |
| | |
| `use-libc` | Enable the libc backend. |
| | |
| `linux_4_11` | Enable optimizations that assume Linux ≥ 4.11 |
| `linux_5_1` | Enable optimizations that assume Linux ≥ 5.1 |
| `linux_5_11` | Enable optimizations that assume Linux ≥ 5.11 |
| `linux_latest` | Enable optimizations that assume the latest Linux release |
| | |
| `use-libc-auxv` | Use `getauxval` instead of `PR_GET_AUXV` or "/proc/self/auxv". |
| | |
| `std` | On by default; disable to activate `#![no_std]`. |
| `alloc` | On by default; enables features that depend on [`alloc`]. |
[`rustix::buffer`]: https://docs.rs/rustix/*/rustix/buffer/index.html
[`rustix::event`]: https://docs.rs/rustix/*/rustix/event/index.html
[`rustix::fs`]: https://docs.rs/rustix/*/rustix/fs/index.html
[`rustix::io_uring`]: https://docs.rs/rustix/*/rustix/io_uring/index.html
[`rustix::mm`]: https://docs.rs/rustix/*/rustix/mm/index.html
[`rustix::mount`]: https://docs.rs/rustix/*/rustix/mount/index.html
[`rustix::net`]: https://docs.rs/rustix/*/rustix/net/index.html
[`rustix::param`]: https://docs.rs/rustix/*/rustix/param/index.html
[`rustix::pipe`]: https://docs.rs/rustix/*/rustix/pipe/index.html
[`rustix::process`]: https://docs.rs/rustix/*/rustix/process/index.html
[`rustix::pty`]: https://docs.rs/rustix/*/rustix/pty/index.html
[`rustix::rand`]: https://docs.rs/rustix/*/rustix/rand/index.html
[`rustix::shm`]: https://docs.rs/rustix/*/rustix/shm/index.html
[`rustix::stdio`]: https://docs.rs/rustix/*/rustix/stdio/index.html
[`rustix::system`]: https://docs.rs/rustix/*/rustix/system/index.html
[`rustix::termios`]: https://docs.rs/rustix/*/rustix/termios/index.html
[`rustix::thread`]: https://docs.rs/rustix/*/rustix/thread/index.html
[`rustix::time`]: https://docs.rs/rustix/*/rustix/time/index.html
[`rustix::io`]: https://docs.rs/rustix/*/rustix/io/index.html
[`rustix::fd`]: https://docs.rs/rustix/*/rustix/fd/index.html
[`rustix::ffi`]: https://docs.rs/rustix/*/rustix/ffi/index.html
[`rustix::ioctl`]: https://docs.rs/rustix/*/rustix/ffi/ioctl.html
## 64-bit Large File Support (LFS) and Year 2038 (y2038) support
`rustix` automatically uses 64-bit APIs when available, and avoids exposing
32-bit APIs that would have the year-2038 problem or fail to support large
files. For instance, `rustix::fstatvfs` calls `fstatvfs64`, and returns a
struct that's 64-bit even on 32-bit platforms.
## Similar crates
`rustix` is similar to [`nix`], [`simple_libc`], [`unix`], [`nc`], [`uapi`],
and [`rusl`]. `rustix` is architected for [I/O safety] with most APIs using
[`OwnedFd`] and [`AsFd`] to manipulate file descriptors rather than `File` or
even `c_int`, and supporting multiple backends so that it can use direct
syscalls while still being usable on all platforms `libc` supports. Like `nix`,
`rustix` has an optimized and flexible filename argument mechanism that allows
users to use a variety of string types, including non-UTF-8 string types.
[`relibc`] is a similar project which aims to be a full "libc", including
C-compatible interfaces and higher-level C/POSIX standard-library
functionality; `rustix` just aims to provide safe and idiomatic Rust interfaces
to low-level syscalls. `relibc` also doesn't tend to support features not
supported on Redox, such as `*at` functions like `openat`, which are important
features for `rustix`.
`rustix` has its own code for making direct syscalls, similar to the
[`syscall`], [`sc`], and [`scall`] crates, using the Rust `asm!` macro.
`rustix` can also use Linux's vDSO mechanism to optimize Linux `clock_gettime`
on all architectures, and all Linux system calls on x86. And `rustix`'s
syscalls report errors using an optimized `Errno` type.
`rustix`'s `*at` functions are similar to the [`openat`] crate, but `rustix`
provides them as free functions rather than associated functions of a `Dir`
type. `rustix`'s `CWD` constant exposes the special `AT_FDCWD` value in a safe
way, so users don't need to open `.` to get a current-directory handle.
`rustix`'s `openat2` function is similar to the [`openat2`] crate, but uses I/O
safety types rather than `RawFd`. `rustix` does not provide dynamic feature
detection, so users must handle the [`NOSYS`] error themselves.
`rustix`'s `termios` module is similar to the [`termios`] crate, but uses I/O
safety types rather than `RawFd`, and the flags parameters to functions such as
`tcsetattr` are `enum`s rather than bare integers. And, rustix calls its
`tcgetattr` function `tcgetattr`, rather than `Termios::from_fd`.
## Minimum Supported Rust Version (MSRV)
This crate currently works on the version of [Rust on Debian stable], which is
currently [Rust 1.63]. This policy may change in the future, in minor version
releases, so users using a fixed version of Rust should pin to a specific
version of this crate.
## Minimum Linux Version
On Linux platforms, rustix requires at least Linux 3.2. This is at most the
oldest Linux version supported by:
- [any current Rust target], or
- [kernel.org] at the time of rustix's [MSRV] release.
The specifics of this policy may change in the future, but we intend it to
always reflect “very old” Linux versions.
[MSRV]: #minimum-supported-rust-version-msrv
[Rust 1.63]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html
[any current Rust target]: https://doc.rust-lang.org/nightly/rustc/platform-support.html
[kernel.org]: https://www.kernel.org/releases.html
[Rust on Debian stable]: https://packages.debian.org/stable/rust/rustc
[Windows Sockets 2]: https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-start-page-2
[`nix`]: https://crates.io/crates/nix
[`unix`]: https://crates.io/crates/unix
[`nc`]: https://crates.io/crates/nc
[`simple_libc`]: https://crates.io/crates/simple_libc
[`uapi`]: https://crates.io/crates/uapi
[`rusl`]: https://lib.rs/crates/rusl
[`relibc`]: https://gitlab.redox-os.org/redox-os/relibc
[`syscall`]: https://crates.io/crates/syscall
[`sc`]: https://crates.io/crates/sc
[`scall`]: https://crates.io/crates/scall
[`openat`]: https://crates.io/crates/openat
[`openat2`]: https://crates.io/crates/openat2
[I/O safety types]: https://doc.rust-lang.org/stable/std/os/fd/index.html#structs
[`termios`]: https://crates.io/crates/termios
[`libc`]: https://crates.io/crates/libc
[`windows-sys`]: https://crates.io/crates/windows-sys
[`cap-std`]: https://crates.io/crates/cap-std
[`memfd`]: https://crates.io/crates/memfd
[`timerfd`]: https://crates.io/crates/timerfd
[`io-streams`]: https://crates.io/crates/io-streams
[`bitflags`]: https://crates.io/crates/bitflags
[`Arg`]: https://docs.rs/rustix/*/rustix/path/trait.Arg.html
[I/O-safe]: https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md
[I/O safety]: https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md
[provenance]: https://github.com/rust-lang/rust/issues/95228
[`OwnedFd`]: https://doc.rust-lang.org/stable/std/os/fd/struct.OwnedFd.html
[`AsFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.AsFd.html
[`NOSYS`]: https://docs.rs/rustix/*/rustix/io/struct.Errno.html#associatedconstant.NOSYS
[`alloc`]: https://doc.rust-lang.org/alloc/alloc/index.html

29
vendor/rustix/SECURITY.md vendored Normal file
View File

@@ -0,0 +1,29 @@
# Security Policy
Building secure foundations for software development is at the core of what we do in the Bytecode Alliance. Contributions of external security researchers are a vital part of that.
## Scope
If you believe you've found a security issue in any website, service, or software owned or operated by the Bytecode Alliance, we encourage you to notify us.
## How to Submit a Report
To submit a vulnerability report to the Bytecode Alliance, please contact us at [security@bytecodealliance.org](mailto:security@bytecodealliance.org). Your submission will be reviewed and validated by a member of our security team.
## Safe Harbor
The Bytecode Alliance supports safe harbor for security researchers who:
* Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our services.
* Only interact with accounts you own or with explicit permission of the account holder. If you do encounter Personally Identifiable Information (PII) contact us immediately, do not proceed with access, and immediately purge any local information.
* Provide us with a reasonable amount of time to resolve vulnerabilities prior to any disclosure to the public or a third-party.
We will consider activities conducted consistent with this policy to constitute "authorized" conduct and will not pursue civil action or initiate a complaint to law enforcement. We will help to the extent we can if legal action is initiated by a third party against you.
Please submit a report to us before engaging in conduct that may be inconsistent with or unaddressed by this policy.
## Preferences
* Please provide detailed reports with reproducible steps and a clearly defined impact.
* Submit one vulnerability per report.
* Social engineering (e.g. phishing, vishing, smishing) is prohibited.

217
vendor/rustix/benches/mod.rs vendored Normal file
View File

@@ -0,0 +1,217 @@
//! Benchmarks for rustix.
//!
//! To enable these benchmarks, add `--cfg=criterion` to RUSTFLAGS and enable
//! the "fs", "time", and "process" cargo features.
//!
//! ```sh
//! RUSTFLAGS=--cfg=criterion cargo bench --features=fs,time,process,stdio
//! ```
#[cfg(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
not(feature = "stdio"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
))]
fn main() {
unimplemented!(
"Add --cfg=criterion to RUSTFLAGS and enable the \"fs\", \"time\", \"process\", and \
\"stdio\" cargo features."
)
}
#[cfg(not(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
not(feature = "stdio"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
)))]
use criterion::{criterion_group, criterion_main};
#[cfg(not(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
not(feature = "stdio"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
)))]
mod suite {
use criterion::Criterion;
pub(super) fn simple_statat(c: &mut Criterion) {
use rustix::fs::{statat, AtFlags, CWD};
c.bench_function("simple statat", |b| {
b.iter(|| {
statat(CWD, "/", AtFlags::empty()).unwrap();
})
});
}
pub(super) fn simple_statat_libc(c: &mut Criterion) {
c.bench_function("simple statat libc", |b| {
b.iter(|| {
let mut s = std::mem::MaybeUninit::<libc::stat>::uninit();
unsafe {
assert_eq!(
libc::fstatat(
libc::AT_FDCWD,
std::ffi::CString::new("/").unwrap().as_c_str().as_ptr() as _,
s.as_mut_ptr(),
0
),
0
);
}
})
});
}
pub(super) fn simple_statat_libc_cstr(c: &mut Criterion) {
c.bench_function("simple statat libc cstr", |b| {
b.iter(|| {
let mut s = std::mem::MaybeUninit::<libc::stat>::uninit();
unsafe {
assert_eq!(
libc::fstatat(
libc::AT_FDCWD,
rustix::cstr!("/").as_ptr() as _,
s.as_mut_ptr(),
0
),
0
);
}
})
});
}
pub(super) fn simple_statat_cstr(c: &mut Criterion) {
use rustix::fs::{statat, AtFlags, CWD};
c.bench_function("simple statat cstr", |b| {
b.iter(|| {
statat(CWD, rustix::cstr!("/"), AtFlags::empty()).unwrap();
})
});
}
pub(super) fn simple_fstat(c: &mut Criterion) {
use rustix::fs::fstat;
c.bench_function("simple fstat", |b| {
b.iter(|| {
fstat(rustix::stdio::stdin()).unwrap();
})
});
}
pub(super) fn simple_fstat_libc(c: &mut Criterion) {
c.bench_function("simple fstat libc", |b| {
b.iter(|| {
let mut s = std::mem::MaybeUninit::<libc::stat>::uninit();
unsafe {
assert_eq!(libc::fstat(libc::STDIN_FILENO, s.as_mut_ptr()), 0);
}
})
});
}
#[cfg(not(target_os = "wasi"))]
pub(super) fn simple_clock_gettime(c: &mut Criterion) {
use rustix::time::{clock_gettime, ClockId};
c.bench_function("simple clock_gettime", |b| {
b.iter(|| {
let _ = clock_gettime(ClockId::Monotonic);
})
});
}
#[cfg(not(target_os = "wasi"))]
pub(super) fn simple_clock_gettime_libc(c: &mut Criterion) {
c.bench_function("simple clock_gettime libc", |b| {
b.iter(|| {
let mut s = std::mem::MaybeUninit::<libc::timespec>::uninit();
unsafe {
assert_eq!(
libc::clock_gettime(libc::CLOCK_MONOTONIC, s.as_mut_ptr()),
0
);
let _ = s.assume_init();
}
})
});
}
#[cfg(not(target_os = "wasi"))]
pub(super) fn simple_getpid(c: &mut Criterion) {
use rustix::process::getpid;
c.bench_function("simple getpid", |b| {
b.iter(|| {
let _ = getpid();
})
});
}
#[cfg(not(target_os = "wasi"))]
pub(super) fn simple_getpid_libc(c: &mut Criterion) {
c.bench_function("simple getpid libc", |b| {
b.iter(|| unsafe {
let _ = libc::getpid();
})
});
}
}
#[cfg(not(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
not(feature = "stdio"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
)))]
criterion_group!(
benches,
suite::simple_statat,
suite::simple_statat_libc,
suite::simple_statat_libc_cstr,
suite::simple_statat_cstr,
suite::simple_fstat,
suite::simple_fstat_libc,
suite::simple_clock_gettime,
suite::simple_clock_gettime_libc,
suite::simple_getpid,
suite::simple_getpid_libc
);
#[cfg(not(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
not(feature = "stdio"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
)))]
criterion_main!(benches);

283
vendor/rustix/build.rs vendored Normal file
View File

@@ -0,0 +1,283 @@
use std::env::var;
use std::io::Write as _;
/// The directory for inline asm.
const ASM_PATH: &str = "src/backend/linux_raw/arch";
fn main() {
// Don't rerun this on changes other than build.rs, as we only depend on
// the rustc version.
println!("cargo:rerun-if-changed=build.rs");
// Gather target information.
let arch = var("CARGO_CFG_TARGET_ARCH").unwrap();
let env = var("CARGO_CFG_TARGET_ENV").unwrap();
let abi = var("CARGO_CFG_TARGET_ABI");
let inline_asm_name = format!("{}/{}.rs", ASM_PATH, arch);
let inline_asm_name_present = std::fs::metadata(inline_asm_name).is_ok();
let os = var("CARGO_CFG_TARGET_OS").unwrap();
let pointer_width = var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap();
let endian = var("CARGO_CFG_TARGET_ENDIAN").unwrap();
// Check for special target variants.
let is_x32 = arch == "x86_64" && pointer_width == "32";
let is_arm64_ilp32 = arch == "aarch64" && pointer_width == "32";
let is_powerpc64be = arch == "powerpc64" && endian == "big";
let is_mipseb = (arch == "mips" || arch == "mips32r6") && endian == "big";
let is_mips64eb = arch.contains("mips64") && endian == "big";
let is_unsupported_abi = is_x32 || is_arm64_ilp32 || is_powerpc64be || is_mipseb || is_mips64eb;
// Check for `--features=use-libc`. This allows crate users to enable the
// libc backend.
let feature_use_libc = var("CARGO_FEATURE_USE_LIBC").is_ok();
// Check for `RUSTFLAGS=--cfg=rustix_use_libc`. This allows end users to
// enable the libc backend even if rustix is depended on transitively.
let cfg_use_libc = var("CARGO_CFG_RUSTIX_USE_LIBC").is_ok();
// Check for `RUSTFLAGS=--cfg=rustix_no_linux_raw`. This allows Linux users to
// enable the libc backend without the linux raw dependency.
let cfg_no_linux_raw = var("CARGO_CFG_RUSTIX_NO_LINUX_RAW").is_ok();
// Check for `--features=rustc-dep-of-std`.
let rustc_dep_of_std = var("CARGO_FEATURE_RUSTC_DEP_OF_STD").is_ok();
// Check for eg. `RUSTFLAGS=--cfg=rustix_use_experimental_features`. This
// is a rustc flag rather than a cargo feature flag because it's
// experimental and not something we want accidentally enabled via
// `--all-features`.
let rustix_use_experimental_features =
var("CARGO_CFG_RUSTIX_USE_EXPERIMENTAL_FEATURES").is_ok();
// Check for eg. `RUSTFLAGS=--cfg=rustix_use_experimental_asm`. This is a
// rustc flag rather than a cargo feature flag because it's experimental
// and not something we want accidentally enabled via `--all-features`.
let rustix_use_experimental_asm = var("CARGO_CFG_RUSTIX_USE_EXPERIMENTAL_ASM").is_ok();
// Miri doesn't support inline asm, and has builtin support for recognizing
// libc FFI calls, so if we're running under miri, use the libc backend.
let miri = var("CARGO_CFG_MIRI").is_ok();
// If experimental features are enabled, auto-detect and use available
// features.
if rustc_dep_of_std {
use_feature("rustc_attrs");
use_feature("core_intrinsics");
} else if rustix_use_experimental_features {
use_feature_or_nothing("rustc_attrs");
use_feature_or_nothing("core_intrinsics");
}
// Features needed only in no-std configurations.
#[cfg(not(feature = "std"))]
{
use_feature_or_nothing("core_c_str");
use_feature_or_nothing("core_ffi_c");
use_feature_or_nothing("alloc_c_string");
use_feature_or_nothing("alloc_ffi");
use_feature_or_nothing("error_in_core");
}
// Feature needed for testing.
if use_static_assertions() {
use_feature("static_assertions");
}
// `LowerExp`/`UpperExp` for `NonZeroI32` etc.
if has_lower_upper_exp_for_non_zero() {
use_feature("lower_upper_exp_for_non_zero");
}
if can_compile("#[diagnostic::on_unimplemented()] trait Foo {}") {
use_feature("rustc_diagnostics")
}
// WASI support can utilize wasi_ext if present.
if os == "wasi" {
use_feature_or_nothing("wasi_ext");
use_feature_or_nothing("wasip2");
}
// If the libc backend is requested, or if we're not on a platform for
// which we have linux_raw support, use the libc backend.
//
// For now Android uses the libc backend; in theory it could use the
// linux_raw backend, but to do that we'll need to figure out how to
// install the toolchain for it.
let libc = feature_use_libc
|| cfg_use_libc
|| os != "linux"
|| !inline_asm_name_present
|| is_unsupported_abi
|| miri
|| ((arch == "powerpc"
|| arch == "powerpc64"
|| arch == "s390x"
|| arch.starts_with("mips"))
&& !rustix_use_experimental_asm);
if libc {
if os != "android" && os == "linux" && !cfg_no_linux_raw {
use_feature("linux_raw_dep");
}
// Use the libc backend.
use_feature("libc");
} else {
// Use the linux_raw backend.
use_feature("linux_raw_dep");
use_feature("linux_raw");
if rustix_use_experimental_asm {
use_feature("asm_experimental_arch");
}
}
// Detect whether the compiler requires us to use thumb mode on ARM.
if arch == "arm" && use_thumb_mode() {
use_feature("thumb_mode");
}
// Rust's libc crate groups some OS's together which have similar APIs;
// create similarly-named features to make `cfg` tests more concise.
let freebsdlike = os == "freebsd" || os == "dragonfly";
if freebsdlike {
use_feature("freebsdlike");
}
let netbsdlike = os == "openbsd" || os == "netbsd";
if netbsdlike {
use_feature("netbsdlike");
}
let apple = os == "macos" || os == "ios" || os == "tvos" || os == "visionos" || os == "watchos";
if apple {
use_feature("apple");
}
if os == "linux" || os == "l4re" || os == "android" || os == "emscripten" {
use_feature("linux_like");
}
if os == "solaris" || os == "illumos" {
use_feature("solarish");
}
if apple || freebsdlike || netbsdlike {
use_feature("bsd");
}
// Add some additional common target combinations.
// Android and "regular" Linux both use the Linux kernel.
if os == "android" || os == "linux" {
use_feature("linux_kernel");
}
// These platforms have a 32-bit `time_t`.
if libc
&& (arch == "arm"
|| arch == "powerpc"
|| arch == "mips"
|| arch == "sparc"
|| arch == "x86"
|| (arch == "aarch64" && os == "linux" && abi == Ok("ilp32".to_string())))
&& (apple
|| os == "android"
|| (os == "freebsd" && arch == "x86")
|| os == "haiku"
|| env == "gnu"
|| (env == "musl" && arch == "x86")
|| (arch == "aarch64" && os == "linux" && abi == Ok("ilp32".to_string())))
{
use_feature("fix_y2038");
}
println!("cargo:rerun-if-env-changed=CARGO_CFG_RUSTIX_USE_EXPERIMENTAL_ASM");
println!("cargo:rerun-if-env-changed=CARGO_CFG_RUSTIX_USE_LIBC");
// Rerun this script if any of our features or configuration flags change,
// or if the toolchain we used for feature detection changes.
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_USE_LIBC");
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_RUSTC_DEP_OF_STD");
println!("cargo:rerun-if-env-changed=CARGO_CFG_MIRI");
}
fn use_static_assertions() -> bool {
// `offset_from` was made const in Rust 1.65.
can_compile("const unsafe fn foo(p: *const u8) -> isize { p.offset_from(p) }")
}
fn use_thumb_mode() -> bool {
// In thumb mode, r7 is reserved.
!can_compile("pub unsafe fn f() { core::arch::asm!(\"udf #16\", in(\"r7\") 0); }")
}
fn has_lower_upper_exp_for_non_zero() -> bool {
// LowerExp/UpperExp for NonZero* were added in Rust 1.84.
// <https://doc.rust-lang.org/stable/std/fmt/trait.LowerExp.html#impl-LowerExp-for-NonZero%3CT%3E>
can_compile("fn a(x: &core::num::NonZeroI32, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { core::fmt::LowerExp::fmt(x, f) }")
}
fn use_feature_or_nothing(feature: &str) {
if has_feature(feature) {
use_feature(feature);
}
}
fn use_feature(feature: &str) {
println!("cargo:rustc-cfg={}", feature);
}
/// Test whether the rustc at `var("RUSTC")` supports the given feature.
fn has_feature(feature: &str) -> bool {
can_compile(format!(
"#![allow(stable_features)]\n#![feature({})]",
feature
))
}
/// Test whether the rustc at `var("RUSTC")` can compile the given code.
fn can_compile<T: AsRef<str>>(test: T) -> bool {
use std::process::Stdio;
let rustc = var("RUSTC").unwrap();
let target = var("TARGET").unwrap();
// Use `RUSTC_WRAPPER` if it's set, unless it's set to an empty string, as
// documented [here].
// [here]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads
let wrapper = var("RUSTC_WRAPPER")
.ok()
.and_then(|w| if w.is_empty() { None } else { Some(w) });
let mut cmd = if let Some(wrapper) = wrapper {
let mut cmd = std::process::Command::new(wrapper);
// The wrapper's first argument is supposed to be the path to rustc.
cmd.arg(rustc);
cmd
} else {
std::process::Command::new(rustc)
};
cmd.arg("--crate-type=rlib") // Don't require `main`.
.arg("--emit=metadata") // Do as little as possible but still parse.
.arg("--target")
.arg(target)
.arg("-o")
.arg(std::env::temp_dir().join("rustix_test_can_compile"))
.stdout(Stdio::null()); // We don't care about the output (only whether it builds or not)
// If Cargo wants to set RUSTFLAGS, use that.
if let Ok(rustflags) = var("CARGO_ENCODED_RUSTFLAGS") {
if !rustflags.is_empty() {
for arg in rustflags.split('\x1f') {
cmd.arg(arg);
}
}
}
let mut child = cmd
.arg("-") // Read from stdin.
.stdin(Stdio::piped()) // Stdin is a pipe.
.stderr(Stdio::null()) // Errors from feature detection aren't interesting and can be confusing.
.spawn()
.unwrap();
writeln!(child.stdin.take().unwrap(), "{}", test.as_ref()).unwrap();
child.wait().unwrap().success()
}

595
vendor/rustix/src/backend/libc/c.rs vendored Normal file
View File

@@ -0,0 +1,595 @@
//! Libc and supplemental types and constants.
#![allow(unused_imports)]
// Import everything from libc, but we'll add some stuff and override some
// things below.
pub(crate) use libc::*;
/// `PROC_SUPER_MAGIC`—The magic number for the procfs filesystem.
#[cfg(all(linux_kernel, target_env = "musl"))]
pub(crate) const PROC_SUPER_MAGIC: u32 = 0x0000_9fa0;
/// `NFS_SUPER_MAGIC`—The magic number for the NFS filesystem.
#[cfg(all(linux_kernel, target_env = "musl"))]
pub(crate) const NFS_SUPER_MAGIC: u32 = 0x0000_6969;
#[cfg(feature = "process")]
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
pub(crate) const EXIT_SIGNALED_SIGABRT: c_int = 128 + SIGABRT as c_int;
// TODO: Upstream these.
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_TSN: c_int = linux_raw_sys::if_ether::ETH_P_TSN as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_ERSPAN2: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN2 as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_ERSPAN: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_PROFINET: c_int = linux_raw_sys::if_ether::ETH_P_PROFINET as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_REALTEK: c_int = linux_raw_sys::if_ether::ETH_P_REALTEK as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_ETHERCAT: c_int = linux_raw_sys::if_ether::ETH_P_ETHERCAT as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_PREAUTH: c_int = linux_raw_sys::if_ether::ETH_P_PREAUTH as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_LLDP: c_int = linux_raw_sys::if_ether::ETH_P_LLDP as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_MRP: c_int = linux_raw_sys::if_ether::ETH_P_MRP as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_NCSI: c_int = linux_raw_sys::if_ether::ETH_P_NCSI as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_CFM: c_int = linux_raw_sys::if_ether::ETH_P_CFM as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_IBOE: c_int = linux_raw_sys::if_ether::ETH_P_IBOE as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_HSR: c_int = linux_raw_sys::if_ether::ETH_P_HSR as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_NSH: c_int = linux_raw_sys::if_ether::ETH_P_NSH as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_DSA_8021Q: c_int = linux_raw_sys::if_ether::ETH_P_DSA_8021Q as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_DSA_A5PSW: c_int = linux_raw_sys::if_ether::ETH_P_DSA_A5PSW as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_IFE: c_int = linux_raw_sys::if_ether::ETH_P_IFE as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_CAN: c_int = linux_raw_sys::if_ether::ETH_P_CAN as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_CANXL: c_int = linux_raw_sys::if_ether::ETH_P_CANXL as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_XDSA: c_int = linux_raw_sys::if_ether::ETH_P_XDSA as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_MAP: c_int = linux_raw_sys::if_ether::ETH_P_MAP as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_MCTP: c_int = linux_raw_sys::if_ether::ETH_P_MCTP as _;
#[cfg(all(linux_raw_dep, feature = "mount"))]
pub(crate) const MS_NOSYMFOLLOW: c_ulong = linux_raw_sys::general::MS_NOSYMFOLLOW as _;
#[cfg(all(
linux_kernel,
any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
)
))]
pub(crate) const SIGEMT: c_int = linux_raw_sys::general::SIGEMT as _;
// TODO: Upstream these.
#[cfg(all(linux_raw_dep, feature = "termios"))]
pub(crate) const IUCLC: tcflag_t = linux_raw_sys::general::IUCLC as _;
#[cfg(all(linux_raw_dep, feature = "termios"))]
pub(crate) const XCASE: tcflag_t = linux_raw_sys::general::XCASE as _;
#[cfg(target_os = "aix")]
pub(crate) const MSG_DONTWAIT: c_int = MSG_NONBLOCK;
// `O_LARGEFILE` can be automatically set by the kernel on Linux:
// <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/open.c?h=v6.13#n1423>
// so libc implementations may leave it undefined or defined to zero.
#[cfg(linux_raw_dep)]
pub(crate) const O_LARGEFILE: c_int = linux_raw_sys::general::O_LARGEFILE as _;
// Gated under `_LARGEFILE_SOURCE` but automatically set by the kernel.
// <https://github.com/illumos/illumos-gate/blob/fb2cb638e5604b214d8ea8d4f01ad2e77b437c17/usr/src/ucbhead/sys/fcntl.h#L64>
#[cfg(solarish)]
pub(crate) const O_LARGEFILE: c_int = 0x2000;
// On PowerPC, the regular `termios` has the `termios2` fields and there is no
// `termios2`, so we define aliases.
#[cfg(all(
linux_kernel,
feature = "termios",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
pub(crate) use {
termios as termios2, TCGETS as TCGETS2, TCSETS as TCSETS2, TCSETSF as TCSETSF2,
TCSETSW as TCSETSW2,
};
// And PowerPC doesn't define `CIBAUD`, but it does define `IBSHIFT`, so we can
// compute `CIBAUD` ourselves.
#[cfg(all(
linux_kernel,
feature = "termios",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
pub(crate) const CIBAUD: u32 = CBAUD << IBSHIFT;
// Automatically enable “large file” support (LFS) features.
#[cfg(target_os = "vxworks")]
pub(super) use _Vx_ticks64_t as _Vx_ticks_t;
#[cfg(linux_kernel)]
pub(super) use fallocate64 as fallocate;
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
#[cfg(any(linux_like, target_os = "aix"))]
pub(super) use open64 as open;
#[cfg(any(
linux_kernel,
target_os = "aix",
target_os = "hurd",
target_os = "l4re"
))]
pub(super) use posix_fallocate64 as posix_fallocate;
#[cfg(any(all(linux_like, not(target_os = "android")), target_os = "aix"))]
pub(super) use {blkcnt64_t as blkcnt_t, rlim64_t as rlim_t};
// TODO: AIX has `stat64x`, `fstat64x`, `lstat64x`, and `stat64xat`; add them
// to the upstream libc crate and implement rustix's `statat` etc. with them.
#[cfg(target_os = "aix")]
pub(super) use {
blksize64_t as blksize_t, fstat64 as fstat, fstatfs64 as fstatfs, fstatvfs64 as fstatvfs,
ftruncate64 as ftruncate, getrlimit64 as getrlimit, ino_t, lseek64 as lseek, mmap,
off64_t as off_t, openat, posix_fadvise64 as posix_fadvise, preadv, pwritev,
rlimit64 as rlimit, setrlimit64 as setrlimit, stat64at as fstatat, statfs64 as statfs,
statvfs64 as statvfs, RLIM_INFINITY,
};
#[cfg(any(linux_like, target_os = "hurd"))]
pub(super) use {
fstat64 as fstat, fstatat64 as fstatat, fstatfs64 as fstatfs, fstatvfs64 as fstatvfs,
ftruncate64 as ftruncate, getrlimit64 as getrlimit, ino64_t as ino_t, lseek64 as lseek,
mmap64 as mmap, off64_t as off_t, openat64 as openat, posix_fadvise64 as posix_fadvise,
rlimit64 as rlimit, setrlimit64 as setrlimit, statfs64 as statfs, statvfs64 as statvfs,
RLIM64_INFINITY as RLIM_INFINITY,
};
#[cfg(apple)]
pub(super) use {
host_info64_t as host_info_t, host_statistics64 as host_statistics,
vm_statistics64_t as vm_statistics_t,
};
#[cfg(not(all(
linux_kernel,
any(
target_pointer_width = "32",
target_arch = "mips64",
target_arch = "mips64r6"
)
)))]
#[cfg(any(linux_like, target_os = "aix", target_os = "hurd"))]
pub(super) use {lstat64 as lstat, stat64 as stat};
#[cfg(any(
linux_kernel,
target_os = "aix",
target_os = "hurd",
target_os = "emscripten"
))]
pub(super) use {pread64 as pread, pwrite64 as pwrite};
#[cfg(any(target_os = "linux", target_os = "hurd", target_os = "emscripten"))]
pub(super) use {preadv64 as preadv, pwritev64 as pwritev};
#[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]
pub(super) unsafe fn prlimit(
pid: pid_t,
resource: __rlimit_resource_t,
new_limit: *const rlimit64,
old_limit: *mut rlimit64,
) -> c_int {
// `prlimit64` wasn't supported in glibc until 2.13.
weak_or_syscall! {
fn prlimit64(
pid: pid_t,
resource: __rlimit_resource_t,
new_limit: *const rlimit64,
old_limit: *mut rlimit64
) via SYS_prlimit64 -> c_int
}
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub(super) unsafe fn prlimit(
pid: pid_t,
resource: c_int,
new_limit: *const rlimit64,
old_limit: *mut rlimit64,
) -> c_int {
weak_or_syscall! {
fn prlimit64(
pid: pid_t,
resource: c_int,
new_limit: *const rlimit64,
old_limit: *mut rlimit64
) via SYS_prlimit64 -> c_int
}
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(target_os = "android")]
pub(super) unsafe fn prlimit(
pid: pid_t,
resource: c_int,
new_limit: *const rlimit64,
old_limit: *mut rlimit64,
) -> c_int {
weak_or_syscall! {
fn prlimit64(
pid: pid_t,
resource: c_int,
new_limit: *const rlimit64,
old_limit: *mut rlimit64
) via SYS_prlimit64 -> c_int
}
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(target_os = "android")]
mod readwrite_pv64 {
use super::*;
pub(in super::super) unsafe fn preadv64(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
) -> ssize_t {
// Older Android libc lacks `preadv64`, so use the `weak!` mechanism to
// test for it, and call back to `syscall`. We don't use
// `weak_or_syscall` here because we need to pass the 64-bit offset
// specially.
weak! {
fn preadv64(c_int, *const iovec, c_int, off64_t) -> ssize_t
}
if let Some(fun) = preadv64.get() {
fun(fd, iov, iovcnt, offset)
} else {
// Unlike the plain "p" functions, the "pv" functions pass their
// offset in an endian-independent way, and always in two
// registers.
syscall! {
fn preadv(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize
) via SYS_preadv -> ssize_t
}
preadv(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize)
}
}
pub(in super::super) unsafe fn pwritev64(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
) -> ssize_t {
// See the comments in `preadv64`.
weak! {
fn pwritev64(c_int, *const iovec, c_int, off64_t) -> ssize_t
}
if let Some(fun) = pwritev64.get() {
fun(fd, iov, iovcnt, offset)
} else {
// Unlike the plain "p" functions, the "pv" functions pass their
// offset in an endian-independent way, and always in two
// registers.
syscall! {
fn pwritev(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize
) via SYS_pwritev -> ssize_t
}
pwritev(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize)
}
}
}
#[cfg(target_os = "android")]
pub(super) use readwrite_pv64::{preadv64 as preadv, pwritev64 as pwritev};
// macOS added `preadv` and `pwritev` in version 11.0.
#[cfg(apple)]
mod readwrite_pv {
use super::*;
weakcall! {
pub(in super::super) fn preadv(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off_t
) -> ssize_t
}
weakcall! {
pub(in super::super) fn pwritev(
fd: c_int,
iov: *const iovec,
iovcnt: c_int, offset: off_t
) -> ssize_t
}
}
#[cfg(apple)]
pub(super) use readwrite_pv::{preadv, pwritev};
// glibc added `preadv64v2` and `pwritev64v2` in version 2.26.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod readwrite_pv64v2 {
use super::*;
pub(in super::super) unsafe fn preadv64v2(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
flags: c_int,
) -> ssize_t {
// Older glibc lacks `preadv64v2`, so use the `weak!` mechanism to
// test for it, and call back to `syscall`. We don't use
// `weak_or_syscall` here because we need to pass the 64-bit offset
// specially.
weak! {
fn preadv64v2(c_int, *const iovec, c_int, off64_t, c_int) -> ssize_t
}
if let Some(fun) = preadv64v2.get() {
fun(fd, iov, iovcnt, offset, flags)
} else {
// Unlike the plain "p" functions, the "pv" functions pass their
// offset in an endian-independent way, and always in two
// registers.
syscall! {
fn preadv2(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize,
flags: c_int
) via SYS_preadv2 -> ssize_t
}
preadv2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
}
pub(in super::super) unsafe fn pwritev64v2(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
flags: c_int,
) -> ssize_t {
// See the comments in `preadv64v2`.
weak! {
fn pwritev64v2(c_int, *const iovec, c_int, off64_t, c_int) -> ssize_t
}
if let Some(fun) = pwritev64v2.get() {
fun(fd, iov, iovcnt, offset, flags)
} else {
// Unlike the plain "p" functions, the "pv" functions pass their
// offset in an endian-independent way, and always in two
// registers.
syscall! {
fn pwritev2(
fd: c_int,
iov: *const iovec,
iovec: c_int,
offset_lo: usize,
offset_hi: usize,
flags: c_int
) via SYS_pwritev2 -> ssize_t
}
pwritev2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(super) use readwrite_pv64v2::{preadv64v2 as preadv2, pwritev64v2 as pwritev2};
// On non-glibc, assume we don't have `pwritev2`/`preadv2` in libc and use
// `c::syscall` instead.
#[cfg(any(
target_os = "android",
all(target_os = "linux", not(target_env = "gnu")),
))]
mod readwrite_pv64v2 {
use super::*;
pub(in super::super) unsafe fn preadv64v2(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
flags: c_int,
) -> ssize_t {
// Unlike the plain "p" functions, the "pv" functions pass their offset
// in an endian-independent way, and always in two registers.
syscall! {
fn preadv2(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize,
flags: c_int
) via SYS_preadv2 -> ssize_t
}
preadv2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
pub(in super::super) unsafe fn pwritev64v2(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
flags: c_int,
) -> ssize_t {
// Unlike the plain "p" functions, the "pv" functions pass their offset
// in an endian-independent way, and always in two registers.
syscall! {
fn pwritev2(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize,
flags: c_int
) via SYS_pwritev2 -> ssize_t
}
pwritev2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
}
#[cfg(any(
target_os = "android",
all(target_os = "linux", not(target_env = "gnu")),
))]
pub(super) use readwrite_pv64v2::{preadv64v2 as preadv2, pwritev64v2 as pwritev2};
// Rust's libc crate lacks statx for Non-glibc targets.
#[cfg(feature = "fs")]
#[cfg(all(
linux_like,
linux_raw_dep,
not(any(
target_os = "emscripten",
target_env = "gnu",
all(target_arch = "loongarch64", target_env = "musl")
))
))]
mod statx_flags {
pub(crate) use linux_raw_sys::general::{
STATX_ALL, STATX_ATIME, STATX_BASIC_STATS, STATX_BLOCKS, STATX_BTIME, STATX_CTIME,
STATX_DIOALIGN, STATX_GID, STATX_INO, STATX_MNT_ID, STATX_MODE, STATX_MTIME, STATX_NLINK,
STATX_SIZE, STATX_TYPE, STATX_UID,
};
pub(crate) use linux_raw_sys::general::{
STATX_ATTR_APPEND, STATX_ATTR_AUTOMOUNT, STATX_ATTR_COMPRESSED, STATX_ATTR_DAX,
STATX_ATTR_ENCRYPTED, STATX_ATTR_IMMUTABLE, STATX_ATTR_MOUNT_ROOT, STATX_ATTR_NODUMP,
STATX_ATTR_VERITY,
};
}
#[cfg(feature = "fs")]
#[cfg(all(
linux_like,
linux_raw_dep,
not(any(
target_os = "emscripten",
target_env = "gnu",
all(target_arch = "loongarch64", target_env = "musl")
))
))]
pub(crate) use statx_flags::*;
#[cfg(feature = "fs")]
#[cfg(target_os = "android")]
pub(crate) use __fsid_t as fsid_t;
// FreeBSD added `timerfd_*` in FreeBSD 14. NetBSD added then in NetBSD 10.
#[cfg(all(feature = "time", any(target_os = "freebsd", target_os = "netbsd")))]
syscall!(pub(crate) fn timerfd_create(
clockid: c_int,
flags: c_int
) via SYS_timerfd_create -> c_int);
#[cfg(all(feature = "time", any(target_os = "freebsd", target_os = "netbsd")))]
syscall!(pub(crate) fn timerfd_gettime(
fd: c_int,
curr_value: *mut itimerspec
) via SYS_timerfd_gettime -> c_int);
#[cfg(all(feature = "time", any(target_os = "freebsd", target_os = "netbsd")))]
syscall!(pub(crate) fn timerfd_settime(
fd: c_int,
flags: c_int,
new_value: *const itimerspec,
old_value: *mut itimerspec
) via SYS_timerfd_settime -> c_int);
#[cfg(all(feature = "time", target_os = "illumos"))]
extern "C" {
pub(crate) fn timerfd_create(clockid: c_int, flags: c_int) -> c_int;
pub(crate) fn timerfd_gettime(fd: c_int, curr_value: *mut itimerspec) -> c_int;
pub(crate) fn timerfd_settime(
fd: c_int,
flags: c_int,
new_value: *const itimerspec,
old_value: *mut itimerspec,
) -> c_int;
}
// illumos and NetBSD timerfd support.
// Submitted upstream in <https://github.com/rust-lang/libc/pull/4333>.
// <https://code.illumos.org/plugins/gitiles/illumos-gate/+/refs/heads/master/usr/src/uts/common/sys/timerfd.h#34>
#[cfg(all(feature = "time", target_os = "illumos"))]
pub(crate) const TFD_CLOEXEC: i32 = 0o2000000;
#[cfg(all(feature = "time", target_os = "illumos"))]
pub(crate) const TFD_NONBLOCK: i32 = 0o4000;
#[cfg(all(feature = "time", target_os = "illumos"))]
pub(crate) const TFD_TIMER_ABSTIME: i32 = 1 << 0;
#[cfg(all(feature = "time", target_os = "illumos"))]
pub(crate) const TFD_TIMER_CANCEL_ON_SET: i32 = 1 << 1;
// <https://nxr.netbsd.org/xref/src/sys/sys/timerfd.h#44>
#[cfg(all(feature = "time", target_os = "netbsd"))]
pub(crate) const TFD_CLOEXEC: i32 = O_CLOEXEC;
#[cfg(all(feature = "time", target_os = "netbsd"))]
pub(crate) const TFD_NONBLOCK: i32 = O_NONBLOCK;
#[cfg(all(feature = "time", target_os = "netbsd"))]
pub(crate) const TFD_TIMER_ABSTIME: i32 = O_WRONLY;
#[cfg(all(feature = "time", target_os = "netbsd"))]
pub(crate) const TFD_TIMER_CANCEL_ON_SET: i32 = O_RDWR;
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(linux_kernel)]
fn test_flags() {
// libc may publicly define `O_LARGEFILE` to 0, but we want the real
// non-zero value.
assert_ne!(O_LARGEFILE, 0);
}
}

188
vendor/rustix/src/backend/libc/conv.rs vendored Normal file
View File

@@ -0,0 +1,188 @@
//! Libc call arguments and return values are often things like `c_int`,
//! `c_uint`, or libc-specific pointer types. This module provides functions
//! for converting between rustix's types and libc types.
use super::c;
#[cfg(all(feature = "alloc", not(any(windows, target_os = "espidf"))))]
use super::fd::IntoRawFd as _;
use super::fd::{AsRawFd as _, BorrowedFd, FromRawFd as _, LibcFd, OwnedFd, RawFd};
#[cfg(not(windows))]
use crate::ffi::CStr;
use crate::io;
#[cfg(not(windows))]
#[inline]
pub(super) fn c_str(c: &CStr) -> *const c::c_char {
c.as_ptr().cast()
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(super) fn no_fd() -> LibcFd {
-1
}
#[inline]
pub(super) fn borrowed_fd(fd: BorrowedFd<'_>) -> LibcFd {
fd.as_raw_fd() as LibcFd
}
#[cfg(all(
feature = "alloc",
not(any(windows, target_os = "espidf", target_os = "redox"))
))]
#[inline]
pub(super) fn owned_fd(fd: OwnedFd) -> LibcFd {
fd.into_raw_fd() as LibcFd
}
#[inline]
pub(super) fn ret(raw: c::c_int) -> io::Result<()> {
if raw == 0 {
Ok(())
} else {
Err(io::Errno::last_os_error())
}
}
#[cfg(apple)]
#[inline]
pub(super) fn nonnegative_ret(raw: c::c_int) -> io::Result<()> {
if raw >= 0 {
Ok(())
} else {
Err(io::Errno::last_os_error())
}
}
#[cfg(not(any(windows, target_os = "wasi")))]
#[inline]
pub(super) unsafe fn ret_infallible(raw: c::c_int) {
debug_assert_eq!(raw, 0, "unexpected error: {:?}", io::Errno::last_os_error());
}
#[inline]
pub(super) fn ret_c_int(raw: c::c_int) -> io::Result<c::c_int> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw)
}
}
#[cfg(any(
linux_kernel,
all(target_os = "illumos", feature = "event"),
all(target_os = "redox", feature = "event")
))]
#[inline]
pub(super) fn ret_u32(raw: c::c_int) -> io::Result<u32> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw as u32)
}
}
#[inline]
pub(super) fn ret_usize(raw: c::ssize_t) -> io::Result<usize> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
debug_assert!(raw >= 0);
Ok(raw as usize)
}
}
#[cfg(not(windows))]
#[cfg(feature = "fs")]
#[inline]
pub(super) fn ret_off_t(raw: c::off_t) -> io::Result<c::off_t> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw)
}
}
#[cfg(not(any(windows, target_os = "wasi")))]
#[inline]
pub(super) fn ret_pid_t(raw: c::pid_t) -> io::Result<c::pid_t> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw)
}
}
/// Convert a `c_int` returned from a libc function to an `OwnedFd`, if valid.
///
/// # Safety
///
/// The caller must ensure that this is the return value of a libc function
/// which returns an owned file descriptor.
#[inline]
pub(super) unsafe fn ret_owned_fd(raw: LibcFd) -> io::Result<OwnedFd> {
if raw == !0 {
Err(io::Errno::last_os_error())
} else {
Ok(OwnedFd::from_raw_fd(raw as RawFd))
}
}
#[cfg(not(any(windows, target_os = "wasi")))]
#[inline]
pub(super) fn ret_discarded_fd(raw: LibcFd) -> io::Result<()> {
if raw == !0 {
Err(io::Errno::last_os_error())
} else {
Ok(())
}
}
#[cfg(all(feature = "alloc", not(any(windows, target_os = "wasi"))))]
#[inline]
pub(super) fn ret_discarded_char_ptr(raw: *mut c::c_char) -> io::Result<()> {
if raw.is_null() {
Err(io::Errno::last_os_error())
} else {
Ok(())
}
}
/// Convert the buffer-length argument value of a `send` or `recv` call.
#[cfg(not(any(windows, target_os = "wasi")))]
#[inline]
pub(super) fn send_recv_len(len: usize) -> usize {
len
}
/// Convert the buffer-length argument value of a `send` or `recv` call.
#[cfg(windows)]
#[inline]
pub(super) fn send_recv_len(len: usize) -> i32 {
// On Windows, the length argument has type `i32`; saturate the length,
// since `send` and `recv` are allowed to send and recv less data than
// requested.
len.try_into().unwrap_or(i32::MAX)
}
/// Convert the return value of a `send` or `recv` call.
#[cfg(not(any(windows, target_os = "wasi")))]
#[inline]
pub(super) fn ret_send_recv(len: isize) -> io::Result<usize> {
ret_usize(len)
}
/// Convert the return value of a `send` or `recv` call.
#[cfg(windows)]
#[inline]
pub(super) fn ret_send_recv(len: i32) -> io::Result<usize> {
ret_usize(len as isize)
}

View File

@@ -0,0 +1,74 @@
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `EPOLL_*` for use with [`epoll::create`].
///
/// [`epoll::create`]: crate::event::epoll::create
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct CreateFlags: u32 {
/// `EPOLL_CLOEXEC`
const CLOEXEC = bitcast!(c::EPOLL_CLOEXEC);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `EPOLL*` for use with [`epoll::add`].
///
/// [`epoll::add`]: crate::event::epoll::add
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct EventFlags: u32 {
/// `EPOLLIN`
const IN = bitcast!(c::EPOLLIN);
/// `EPOLLOUT`
const OUT = bitcast!(c::EPOLLOUT);
/// `EPOLLPRI`
const PRI = bitcast!(c::EPOLLPRI);
/// `EPOLLERR`
const ERR = bitcast!(c::EPOLLERR);
/// `EPOLLHUP`
const HUP = bitcast!(c::EPOLLHUP);
/// `EPOLLRDNORM`
const RDNORM = bitcast!(c::EPOLLRDNORM);
/// `EPOLLRDBAND`
const RDBAND = bitcast!(c::EPOLLRDBAND);
/// `EPOLLWRNORM`
const WRNORM = bitcast!(c::EPOLLWRNORM);
/// `EPOLLWRBAND`
const WRBAND = bitcast!(c::EPOLLWRBAND);
/// `EPOLLMSG`
const MSG = bitcast!(c::EPOLLMSG);
/// `EPOLLRDHUP`
const RDHUP = bitcast!(c::EPOLLRDHUP);
/// `EPOLLET`
const ET = bitcast!(c::EPOLLET);
/// `EPOLLONESHOT`
const ONESHOT = bitcast!(c::EPOLLONESHOT);
/// `EPOLLWAKEUP`
const WAKEUP = bitcast!(c::EPOLLWAKEUP);
/// `EPOLLEXCLUSIVE`
const EXCLUSIVE = bitcast!(c::EPOLLEXCLUSIVE);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,9 @@
pub(crate) mod poll_fd;
#[cfg(not(windows))]
pub(crate) mod types;
#[cfg_attr(windows, path = "windows_syscalls.rs")]
pub(crate) mod syscalls;
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub mod epoll;

View File

@@ -0,0 +1,143 @@
use crate::backend::c;
use crate::backend::conv::borrowed_fd;
use crate::backend::fd::{AsFd, AsRawFd as _, BorrowedFd, LibcFd};
#[cfg(windows)]
use crate::backend::fd::{AsSocket, RawFd};
use crate::ffi;
use bitflags::bitflags;
use core::fmt;
use core::marker::PhantomData;
bitflags! {
/// `POLL*` flags for use with [`poll`].
///
/// [`poll`]: crate::event::poll
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct PollFlags: ffi::c_short {
/// `POLLIN`
const IN = c::POLLIN;
/// `POLLPRI`
#[cfg(not(target_os = "wasi"))]
const PRI = c::POLLPRI;
/// `POLLOUT`
const OUT = c::POLLOUT;
/// `POLLRDNORM`
const RDNORM = c::POLLRDNORM;
/// `POLLWRNORM`
#[cfg(not(target_os = "l4re"))]
const WRNORM = c::POLLWRNORM;
/// `POLLRDBAND`
#[cfg(not(any(target_os = "l4re", target_os = "wasi")))]
const RDBAND = c::POLLRDBAND;
/// `POLLWRBAND`
#[cfg(not(any(target_os = "l4re", target_os = "wasi")))]
const WRBAND = c::POLLWRBAND;
/// `POLLERR`
const ERR = c::POLLERR;
/// `POLLHUP`
const HUP = c::POLLHUP;
/// `POLLNVAL`
#[cfg(not(target_os = "espidf"))]
const NVAL = c::POLLNVAL;
/// `POLLRDHUP`
#[cfg(any(
target_os = "freebsd",
target_os = "illumos",
all(
linux_kernel,
not(any(target_arch = "sparc", target_arch = "sparc64"))
),
))]
const RDHUP = c::POLLRDHUP;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
/// `struct pollfd`—File descriptor and flags for use with [`poll`].
///
/// [`poll`]: crate::event::poll
#[doc(alias = "pollfd")]
#[derive(Clone)]
#[repr(transparent)]
pub struct PollFd<'fd> {
pollfd: c::pollfd,
_phantom: PhantomData<BorrowedFd<'fd>>,
}
impl<'fd> fmt::Debug for PollFd<'fd> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PollFd")
.field("fd", &self.pollfd.fd)
.field("events", &self.pollfd.events)
.field("revents", &self.pollfd.revents)
.finish()
}
}
impl<'fd> PollFd<'fd> {
/// Constructs a new `PollFd` holding `fd` and `events`.
#[inline]
pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> Self {
Self::from_borrowed_fd(fd.as_fd(), events)
}
/// Sets the contained file descriptor to `fd`.
#[inline]
pub fn set_fd<Fd: AsFd>(&mut self, fd: &'fd Fd) {
self.pollfd.fd = fd.as_fd().as_raw_fd() as LibcFd;
}
/// Clears the ready events.
#[inline]
pub fn clear_revents(&mut self) {
self.pollfd.revents = 0;
}
/// Constructs a new `PollFd` holding `fd` and `events`.
///
/// This is the same as `new`, but can be used to avoid borrowing the
/// `BorrowedFd`, which can be tricky in situations where the `BorrowedFd`
/// is a temporary.
#[inline]
pub fn from_borrowed_fd(fd: BorrowedFd<'fd>, events: PollFlags) -> Self {
Self {
pollfd: c::pollfd {
fd: borrowed_fd(fd),
events: events.bits(),
revents: 0,
},
_phantom: PhantomData,
}
}
/// Returns the ready events.
#[inline]
pub fn revents(&self) -> PollFlags {
// Use `.unwrap()` here because in theory we know we know all the bits
// the OS might set here, but OS's have added extensions in the past.
PollFlags::from_bits(self.pollfd.revents).unwrap()
}
}
#[cfg(not(windows))]
impl<'fd> AsFd for PollFd<'fd> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
// SAFETY: Our constructors and `set_fd` require `pollfd.fd` to be
// valid for the `'fd` lifetime.
unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) }
}
}
#[cfg(windows)]
impl<'fd> AsSocket for PollFd<'fd> {
#[inline]
fn as_socket(&self) -> BorrowedFd<'_> {
// SAFETY: Our constructors and `set_fd` require `pollfd.fd` to be
// valid for the `'fd` lifetime.
unsafe { BorrowedFd::borrow_raw(self.pollfd.fd as RawFd) }
}
}

View File

@@ -0,0 +1,631 @@
//! libc syscalls supporting `rustix::event`.
use crate::backend::c;
#[cfg(any(linux_kernel, solarish, target_os = "redox"))]
use crate::backend::conv::ret;
use crate::backend::conv::ret_c_int;
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
use crate::backend::conv::ret_u32;
#[cfg(bsd)]
use crate::event::kqueue::Event;
#[cfg(solarish)]
use crate::event::port::Event;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "illumos",
target_os = "espidf"
))]
use crate::event::EventfdFlags;
#[cfg(any(bsd, linux_kernel, target_os = "wasi"))]
use crate::event::FdSetElement;
use crate::event::{PollFd, Timespec};
use crate::io;
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
use crate::utils::as_ptr;
#[cfg(solarish)]
use core::mem::MaybeUninit;
#[cfg(any(
bsd,
linux_kernel,
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "netbsd",
target_os = "wasi"
))]
use core::ptr::null;
#[cfg(any(bsd, linux_kernel, solarish, target_os = "redox", target_os = "wasi"))]
use core::ptr::null_mut;
#[cfg(any(bsd, linux_kernel, solarish, target_os = "redox"))]
use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd};
#[cfg(any(
bsd,
linux_kernel,
solarish,
target_os = "freebsd",
target_os = "illumos",
target_os = "espidf",
target_os = "redox"
))]
use {crate::backend::conv::ret_owned_fd, crate::fd::OwnedFd};
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "illumos",
target_os = "espidf"
))]
pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> {
#[cfg(linux_kernel)]
unsafe {
syscall! {
fn eventfd2(
initval: c::c_uint,
flags: c::c_int
) via SYS_eventfd2 -> c::c_int
}
ret_owned_fd(eventfd2(initval, bitflags_bits!(flags)))
}
// `eventfd` was added in FreeBSD 13, so it isn't available on FreeBSD 12.
#[cfg(target_os = "freebsd")]
unsafe {
weakcall! {
fn eventfd(
initval: c::c_uint,
flags: c::c_int
) -> c::c_int
}
ret_owned_fd(eventfd(initval, bitflags_bits!(flags)))
}
#[cfg(any(target_os = "illumos", target_os = "espidf"))]
unsafe {
ret_owned_fd(c::eventfd(initval, bitflags_bits!(flags)))
}
}
#[cfg(bsd)]
pub(crate) fn kqueue() -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::kqueue()) }
}
#[cfg(bsd)]
pub(crate) unsafe fn kevent(
kq: BorrowedFd<'_>,
changelist: &[Event],
eventlist: (*mut Event, usize),
timeout: Option<&Timespec>,
) -> io::Result<c::c_int> {
// If we don't have to fix y2038 on this platform, `Timespec` is the same
// as `c::timespec` and it's easy.
#[cfg(not(fix_y2038))]
let timeout = crate::timespec::option_as_libc_timespec_ptr(timeout);
// If we do have to fix y2038 on this platform, convert to `c::timespec`.
#[cfg(fix_y2038)]
let converted_timeout;
#[cfg(fix_y2038)]
let timeout = match timeout {
None => null(),
Some(timeout) => {
converted_timeout = c::timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timeout.tv_nsec as _,
};
&converted_timeout
}
};
ret_c_int(c::kevent(
borrowed_fd(kq),
changelist.as_ptr().cast(),
changelist
.len()
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
eventlist.0.cast(),
eventlist.1.try_into().map_err(|_| io::Errno::OVERFLOW)?,
timeout,
))
}
#[inline]
pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: Option<&Timespec>) -> io::Result<usize> {
let nfds = fds
.len()
.try_into()
.map_err(|_convert_err| io::Errno::INVAL)?;
// If we have `ppoll`, it supports a `timespec` timeout, so use it.
#[cfg(any(
linux_kernel,
freebsdlike,
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "netbsd"
))]
{
// If we don't have to fix y2038 on this platform, `Timespec` is
// the same as `c::timespec` and it's easy.
#[cfg(not(fix_y2038))]
let timeout = crate::timespec::option_as_libc_timespec_ptr(timeout);
// If we do have to fix y2038 on this platform, convert to
// `c::timespec`.
#[cfg(fix_y2038)]
let converted_timeout;
#[cfg(fix_y2038)]
let timeout = match timeout {
None => null(),
Some(timeout) => {
converted_timeout = c::timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timeout.tv_nsec as _,
};
&converted_timeout
}
};
#[cfg(not(target_os = "netbsd"))]
{
ret_c_int(unsafe { c::ppoll(fds.as_mut_ptr().cast(), nfds, timeout, null()) })
.map(|nready| nready as usize)
}
// NetBSD 9.x lacks `ppoll`, so use a weak symbol and fall back to
// plain `poll` if needed.
#[cfg(target_os = "netbsd")]
{
weak! {
fn ppoll(
*mut c::pollfd,
c::nfds_t,
*const c::timespec,
*const c::sigset_t
) -> c::c_int
}
if let Some(func) = ppoll.get() {
return ret_c_int(unsafe { func(fds.as_mut_ptr().cast(), nfds, timeout, null()) })
.map(|nready| nready as usize);
}
}
}
// If we don't have `ppoll`, convert the timeout to `c_int` and use `poll`.
#[cfg(not(any(
linux_kernel,
freebsdlike,
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd"
)))]
{
let timeout = match timeout {
None => -1,
Some(timeout) => timeout.as_c_int_millis().ok_or(io::Errno::INVAL)?,
};
ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) })
.map(|nready| nready as usize)
}
}
#[cfg(any(bsd, linux_kernel))]
pub(crate) unsafe fn select(
nfds: i32,
readfds: Option<&mut [FdSetElement]>,
writefds: Option<&mut [FdSetElement]>,
exceptfds: Option<&mut [FdSetElement]>,
timeout: Option<&Timespec>,
) -> io::Result<i32> {
let len = crate::event::fd_set_num_elements_for_bitvector(nfds);
let readfds = match readfds {
Some(readfds) => {
assert!(readfds.len() >= len);
readfds.as_mut_ptr()
}
None => null_mut(),
};
let writefds = match writefds {
Some(writefds) => {
assert!(writefds.len() >= len);
writefds.as_mut_ptr()
}
None => null_mut(),
};
let exceptfds = match exceptfds {
Some(exceptfds) => {
assert!(exceptfds.len() >= len);
exceptfds.as_mut_ptr()
}
None => null_mut(),
};
let timeout_data;
let timeout_ptr = match timeout {
Some(timeout) => {
// Convert from `Timespec` to `c::timeval`.
timeout_data = c::timeval {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_usec: ((timeout.tv_nsec + 999) / 1000) as _,
};
&timeout_data
}
None => null(),
};
// On Apple platforms, use the specially mangled `select` which doesn't
// have an `FD_SETSIZE` limitation.
#[cfg(apple)]
{
extern "C" {
#[link_name = "select$DARWIN_EXTSN$NOCANCEL"]
fn select(
nfds: c::c_int,
readfds: *mut FdSetElement,
writefds: *mut FdSetElement,
errorfds: *mut FdSetElement,
timeout: *const c::timeval,
) -> c::c_int;
}
ret_c_int(select(nfds, readfds, writefds, exceptfds, timeout_ptr))
}
// Otherwise just use the normal `select`.
#[cfg(not(apple))]
{
ret_c_int(c::select(
nfds,
readfds.cast(),
writefds.cast(),
exceptfds.cast(),
timeout_ptr as *mut c::timeval,
))
}
}
// WASI uses a count + array instead of a bitvector.
#[cfg(target_os = "wasi")]
pub(crate) unsafe fn select(
nfds: i32,
readfds: Option<&mut [FdSetElement]>,
writefds: Option<&mut [FdSetElement]>,
exceptfds: Option<&mut [FdSetElement]>,
timeout: Option<&Timespec>,
) -> io::Result<i32> {
let len = crate::event::fd_set_num_elements_for_fd_array(nfds as usize);
let readfds = match readfds {
Some(readfds) => {
assert!(readfds.len() >= len);
readfds.as_mut_ptr()
}
None => null_mut(),
};
let writefds = match writefds {
Some(writefds) => {
assert!(writefds.len() >= len);
writefds.as_mut_ptr()
}
None => null_mut(),
};
let exceptfds = match exceptfds {
Some(exceptfds) => {
assert!(exceptfds.len() >= len);
exceptfds.as_mut_ptr()
}
None => null_mut(),
};
let timeout_data;
let timeout_ptr = match timeout {
Some(timeout) => {
// Convert from `Timespec` to `c::timeval`.
timeout_data = c::timeval {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_usec: ((timeout.tv_nsec + 999) / 1000) as _,
};
&timeout_data
}
None => null(),
};
ret_c_int(c::select(
nfds,
readfds.cast(),
writefds.cast(),
exceptfds.cast(),
timeout_ptr as *mut c::timeval,
))
}
#[cfg(solarish)]
pub(crate) fn port_create() -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::port_create()) }
}
#[cfg(solarish)]
pub(crate) unsafe fn port_associate(
port: BorrowedFd<'_>,
source: c::c_int,
object: c::uintptr_t,
events: c::c_int,
user: *mut c::c_void,
) -> io::Result<()> {
ret(c::port_associate(
borrowed_fd(port),
source,
object,
events,
user,
))
}
#[cfg(solarish)]
pub(crate) unsafe fn port_dissociate(
port: BorrowedFd<'_>,
source: c::c_int,
object: c::uintptr_t,
) -> io::Result<()> {
ret(c::port_dissociate(borrowed_fd(port), source, object))
}
#[cfg(solarish)]
pub(crate) fn port_get(port: BorrowedFd<'_>, timeout: Option<&Timespec>) -> io::Result<Event> {
// If we don't have to fix y2038 on this platform, `Timespec` is
// the same as `c::timespec` and it's easy.
#[cfg(not(fix_y2038))]
let timeout = crate::timespec::option_as_libc_timespec_ptr(timeout);
// If we do have to fix y2038 on this platform, convert to
// `c::timespec`.
#[cfg(fix_y2038)]
let converted_timeout;
#[cfg(fix_y2038)]
let timeout = match timeout {
None => null(),
Some(timeout) => {
converted_timeout = c::timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timeout.tv_nsec as _,
};
&converted_timeout
}
};
let mut event = MaybeUninit::<c::port_event>::uninit();
// In Rust ≥ 1.65, the `as _` can be `.cast_mut()`.
unsafe {
ret(c::port_get(
borrowed_fd(port),
event.as_mut_ptr(),
timeout as _,
))?;
}
// If we're done, initialize the event and return it.
Ok(Event(unsafe { event.assume_init() }))
}
#[cfg(solarish)]
pub(crate) unsafe fn port_getn(
port: BorrowedFd<'_>,
events: (*mut Event, usize),
mut nget: u32,
timeout: Option<&Timespec>,
) -> io::Result<usize> {
// If we don't have to fix y2038 on this platform, `Timespec` is
// the same as `c::timespec` and it's easy.
#[cfg(not(fix_y2038))]
let timeout = crate::timespec::option_as_libc_timespec_ptr(timeout);
// If we do have to fix y2038 on this platform, convert to
// `c::timespec`.
#[cfg(fix_y2038)]
let converted_timeout;
#[cfg(fix_y2038)]
let timeout = match timeout {
None => null(),
Some(timeout) => {
converted_timeout = c::timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timeout.tv_nsec as _,
};
&converted_timeout
}
};
// `port_getn` special-cases a max value of 0 to be a query that returns
// the number of events, so bail out early if needed.
if events.1 == 0 {
return Ok(0);
}
// In Rust ≥ 1.65, the `as _` can be `.cast_mut()`.
ret(c::port_getn(
borrowed_fd(port),
events.0.cast(),
events.1.try_into().unwrap_or(u32::MAX),
&mut nget,
timeout as _,
))?;
Ok(nget as usize)
}
#[cfg(solarish)]
pub(crate) fn port_getn_query(port: BorrowedFd<'_>) -> io::Result<u32> {
let mut nget: u32 = 0;
// Pass a `max` of 0 to query the number of available events.
unsafe {
ret(c::port_getn(
borrowed_fd(port),
null_mut(),
0,
&mut nget,
null_mut(),
))?;
}
Ok(nget)
}
#[cfg(solarish)]
pub(crate) fn port_send(
port: BorrowedFd<'_>,
events: c::c_int,
userdata: *mut c::c_void,
) -> io::Result<()> {
unsafe { ret(c::port_send(borrowed_fd(port), events, userdata)) }
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn pause() {
let r = unsafe { c::pause() };
let errno = libc_errno::errno().0;
debug_assert_eq!(r, -1);
debug_assert_eq!(errno, c::EINTR);
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) fn epoll_create(flags: super::epoll::CreateFlags) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) }
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) fn epoll_add(
epoll: BorrowedFd<'_>,
source: BorrowedFd<'_>,
event: &crate::event::epoll::Event,
) -> io::Result<()> {
// We use our own `Event` struct instead of libc's because
// ours preserves pointer provenance instead of just using a `u64`,
// and we have tests elsewhere for layout equivalence.
unsafe {
ret(c::epoll_ctl(
borrowed_fd(epoll),
c::EPOLL_CTL_ADD,
borrowed_fd(source),
// The event is read-only even though libc has a non-const pointer.
as_ptr(event) as *mut c::epoll_event,
))
}
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) fn epoll_mod(
epoll: BorrowedFd<'_>,
source: BorrowedFd<'_>,
event: &crate::event::epoll::Event,
) -> io::Result<()> {
unsafe {
ret(c::epoll_ctl(
borrowed_fd(epoll),
c::EPOLL_CTL_MOD,
borrowed_fd(source),
// The event is read-only even though libc has a non-const pointer.
as_ptr(event) as *mut c::epoll_event,
))
}
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) fn epoll_del(epoll: BorrowedFd<'_>, source: BorrowedFd<'_>) -> io::Result<()> {
unsafe {
ret(c::epoll_ctl(
borrowed_fd(epoll),
c::EPOLL_CTL_DEL,
borrowed_fd(source),
null_mut(),
))
}
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) unsafe fn epoll_wait(
epoll: BorrowedFd<'_>,
events: (*mut crate::event::epoll::Event, usize),
timeout: Option<&Timespec>,
) -> io::Result<usize> {
// If we're on Linux ≥ 5.11 and a libc that has an `epoll_pwait2`
// function, and it's y2038-safe, use it.
#[cfg(all(
linux_kernel,
feature = "linux_5_11",
target_env = "gnu",
not(fix_y2038)
))]
{
weak! {
fn epoll_pwait2(
c::c_int,
*mut c::epoll_event,
c::c_int,
*const c::timespec,
*const c::sigset_t
) -> c::c_int
}
if let Some(epoll_pwait2_func) = epoll_pwait2.get() {
return ret_u32(epoll_pwait2_func(
borrowed_fd(epoll),
events.0.cast::<c::epoll_event>(),
events.1.try_into().unwrap_or(i32::MAX),
crate::utils::option_as_ptr(timeout).cast(),
null(),
))
.map(|i| i as usize);
}
}
// If we're on Linux ≥ 5.11, use `epoll_pwait2` via `libc::syscall`.
#[cfg(all(linux_kernel, feature = "linux_5_11"))]
{
syscall! {
fn epoll_pwait2(
epfd: c::c_int,
events: *mut c::epoll_event,
maxevents: c::c_int,
timeout: *const Timespec,
sigmask: *const c::sigset_t
) via SYS_epoll_pwait2 -> c::c_int
}
ret_u32(epoll_pwait2(
borrowed_fd(epoll),
events.0.cast::<c::epoll_event>(),
events.1.try_into().unwrap_or(i32::MAX),
crate::utils::option_as_ptr(timeout).cast(),
null(),
))
.map(|i| i as usize)
}
// Otherwise just use `epoll_wait`.
#[cfg(not(all(linux_kernel, feature = "linux_5_11")))]
{
let timeout = match timeout {
None => -1,
Some(timeout) => timeout.as_c_int_millis().ok_or(io::Errno::INVAL)?,
};
ret_u32(c::epoll_wait(
borrowed_fd(epoll),
events.0.cast::<c::epoll_event>(),
events.1.try_into().unwrap_or(i32::MAX),
timeout,
))
.map(|i| i as usize)
}
}

View File

@@ -0,0 +1,37 @@
#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "illumos"))]
use crate::backend::c;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "illumos",
target_os = "espidf"
))]
use bitflags::bitflags;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "illumos",
target_os = "espidf"
))]
bitflags! {
/// `EFD_*` flags for use with [`eventfd`].
///
/// [`eventfd`]: crate::event::eventfd
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct EventfdFlags: u32 {
/// `EFD_CLOEXEC`
#[cfg(not(target_os = "espidf"))]
const CLOEXEC = bitcast!(c::EFD_CLOEXEC);
/// `EFD_NONBLOCK`
#[cfg(not(target_os = "espidf"))]
const NONBLOCK = bitcast!(c::EFD_NONBLOCK);
/// `EFD_SEMAPHORE`
#[cfg(not(target_os = "espidf"))]
const SEMAPHORE = bitcast!(c::EFD_SEMAPHORE);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,79 @@
//! Windows system calls in the `event` module.
use crate::backend::c;
use crate::backend::conv::ret_c_int;
use crate::event::{FdSetElement, PollFd, Timespec};
use crate::io;
pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: Option<&Timespec>) -> io::Result<usize> {
let nfds = fds
.len()
.try_into()
.map_err(|_convert_err| io::Errno::INVAL)?;
let timeout = match timeout {
None => -1,
Some(timeout) => timeout.as_c_int_millis().ok_or(io::Errno::INVAL)?,
};
ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) })
.map(|nready| nready as usize)
}
pub(crate) fn select(
nfds: i32,
readfds: Option<&mut [FdSetElement]>,
writefds: Option<&mut [FdSetElement]>,
exceptfds: Option<&mut [FdSetElement]>,
timeout: Option<&crate::timespec::Timespec>,
) -> io::Result<i32> {
use core::ptr::{null, null_mut};
let readfds = match readfds {
Some(readfds) => {
assert!(readfds.len() >= readfds[0].0 as usize);
readfds.as_mut_ptr()
}
None => null_mut(),
};
let writefds = match writefds {
Some(writefds) => {
assert!(writefds.len() >= writefds[0].0 as usize);
writefds.as_mut_ptr()
}
None => null_mut(),
};
let exceptfds = match exceptfds {
Some(exceptfds) => {
assert!(exceptfds.len() >= exceptfds[0].0 as usize);
exceptfds.as_mut_ptr()
}
None => null_mut(),
};
let timeout_data;
let timeout_ptr = match timeout {
Some(timeout) => {
// Convert from `Timespec` to `TIMEVAL`.
timeout_data = c::TIMEVAL {
tv_sec: timeout
.tv_sec
.try_into()
.map_err(|_| io::Errno::OPNOTSUPP)?,
tv_usec: ((timeout.tv_nsec + 999) / 1000) as _,
};
&timeout_data
}
None => null(),
};
unsafe {
ret_c_int(c::select(
nfds,
readfds.cast(),
writefds.cast(),
exceptfds.cast(),
timeout_ptr,
))
}
}

489
vendor/rustix/src/backend/libc/fs/dir.rs vendored Normal file
View File

@@ -0,0 +1,489 @@
#[cfg(not(any(
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
)))]
use super::types::FileType;
use crate::backend::c;
use crate::backend::conv::owned_fd;
use crate::fd::{AsFd, BorrowedFd, OwnedFd};
use crate::ffi::{CStr, CString};
use crate::fs::{fcntl_getfl, openat, Mode, OFlags};
#[cfg(not(target_os = "vita"))]
use crate::fs::{fstat, Stat};
#[cfg(not(any(
solarish,
target_os = "haiku",
target_os = "horizon",
target_os = "netbsd",
target_os = "nto",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
use crate::fs::{fstatfs, StatFs};
#[cfg(not(any(solarish, target_os = "vita", target_os = "wasi")))]
use crate::fs::{fstatvfs, StatVfs};
use crate::io;
#[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))]
#[cfg(feature = "process")]
use crate::process::fchdir;
use alloc::borrow::ToOwned as _;
#[cfg(not(any(linux_like, target_os = "hurd")))]
use c::readdir as libc_readdir;
#[cfg(any(linux_like, target_os = "hurd"))]
use c::readdir64 as libc_readdir;
use core::fmt;
use core::ptr::NonNull;
use libc_errno::{errno, set_errno, Errno};
/// `DIR*`
pub struct Dir {
/// The `libc` `DIR` pointer.
libc_dir: NonNull<c::DIR>,
/// Have we seen any errors in this iteration?
any_errors: bool,
}
impl Dir {
/// Take ownership of `fd` and construct a `Dir` that reads entries from
/// the given directory file descriptor.
#[inline]
pub fn new<Fd: Into<OwnedFd>>(fd: Fd) -> io::Result<Self> {
Self::_new(fd.into())
}
#[inline]
fn _new(fd: OwnedFd) -> io::Result<Self> {
let raw = owned_fd(fd);
unsafe {
let libc_dir = c::fdopendir(raw);
if let Some(libc_dir) = NonNull::new(libc_dir) {
Ok(Self {
libc_dir,
any_errors: false,
})
} else {
let err = io::Errno::last_os_error();
let _ = c::close(raw);
Err(err)
}
}
}
/// Borrow `fd` and construct a `Dir` that reads entries from the given
/// directory file descriptor.
#[inline]
pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> {
Self::_read_from(fd.as_fd())
}
#[inline]
#[allow(unused_mut)]
fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> {
let mut any_errors = false;
// Given an arbitrary `OwnedFd`, it's impossible to know whether the
// user holds a `dup`'d copy which could continue to modify the
// file description state, which would cause Undefined Behavior after
// our call to `fdopendir`. To prevent this, we obtain an independent
// `OwnedFd`.
let flags = fcntl_getfl(fd)?;
let fd_for_dir = match openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty()) {
Ok(fd) => fd,
#[cfg(not(target_os = "wasi"))]
Err(io::Errno::NOENT) => {
// If "." doesn't exist, it means the directory was removed.
// We treat that as iterating through a directory with no
// entries.
any_errors = true;
crate::io::dup(fd)?
}
Err(err) => return Err(err),
};
let raw = owned_fd(fd_for_dir);
unsafe {
let libc_dir = c::fdopendir(raw);
if let Some(libc_dir) = NonNull::new(libc_dir) {
Ok(Self {
libc_dir,
any_errors,
})
} else {
let err = io::Errno::last_os_error();
let _ = c::close(raw);
Err(err)
}
}
}
/// `rewinddir(self)`
#[inline]
pub fn rewind(&mut self) {
self.any_errors = false;
unsafe { c::rewinddir(self.libc_dir.as_ptr()) }
}
/// `seekdir(self, offset)`
///
/// This function is only available on 64-bit platforms because it's
/// implemented using [`libc::seekdir`] which only supports offsets that
/// fit in a `c_long`.
///
/// [`libc::seekdir`]: https://docs.rs/libc/*/arm-unknown-linux-gnueabihf/libc/fn.seekdir.html
#[cfg(target_pointer_width = "64")]
#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))]
#[doc(alias = "seekdir")]
#[inline]
pub fn seek(&mut self, offset: i64) -> io::Result<()> {
self.any_errors = false;
unsafe { c::seekdir(self.libc_dir.as_ptr(), offset) }
Ok(())
}
/// `readdir(self)`, where `None` means the end of the directory.
pub fn read(&mut self) -> Option<io::Result<DirEntry>> {
// If we've seen errors, don't continue to try to read anything
// further.
if self.any_errors {
return None;
}
set_errno(Errno(0));
let dirent_ptr = unsafe { libc_readdir(self.libc_dir.as_ptr()) };
if dirent_ptr.is_null() {
let curr_errno = errno().0;
if curr_errno == 0 {
// We successfully reached the end of the stream.
None
} else {
// `errno` is unknown or non-zero, so an error occurred.
self.any_errors = true;
Some(Err(io::Errno(curr_errno)))
}
} else {
// We successfully read an entry.
unsafe {
let dirent = &*dirent_ptr;
// We have our own copy of OpenBSD's dirent; check that the
// layout minimally matches libc's.
#[cfg(target_os = "openbsd")]
check_dirent_layout(dirent);
let result = DirEntry {
#[cfg(not(any(
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
)))]
d_type: dirent.d_type,
#[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))]
d_ino: dirent.d_ino,
#[cfg(any(
linux_like,
solarish,
target_os = "fuchsia",
target_os = "hermit",
target_os = "openbsd",
target_os = "redox"
))]
d_off: dirent.d_off,
#[cfg(any(freebsdlike, netbsdlike))]
d_fileno: dirent.d_fileno,
name: CStr::from_ptr(dirent.d_name.as_ptr().cast()).to_owned(),
};
Some(Ok(result))
}
}
}
/// `fstat(self)`
#[cfg(not(any(target_os = "horizon", target_os = "vita")))]
#[inline]
pub fn stat(&self) -> io::Result<Stat> {
fstat(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
/// `fstatfs(self)`
#[cfg(not(any(
solarish,
target_os = "haiku",
target_os = "horizon",
target_os = "netbsd",
target_os = "nto",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
pub fn statfs(&self) -> io::Result<StatFs> {
fstatfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
/// `fstatvfs(self)`
#[cfg(not(any(
solarish,
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub fn statvfs(&self) -> io::Result<StatVfs> {
fstatvfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
/// `fchdir(self)`
#[cfg(feature = "process")]
#[cfg(not(any(
target_os = "fuchsia",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
#[inline]
pub fn chdir(&self) -> io::Result<()> {
fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
}
/// `Dir` is `Send` and `Sync`, because even though it contains internal
/// state, all methods that modify the state require a `mut &self` and
/// can therefore not be called concurrently. Calling them from different
/// threads sequentially is fine.
unsafe impl Send for Dir {}
unsafe impl Sync for Dir {}
impl Drop for Dir {
#[inline]
fn drop(&mut self) {
unsafe { c::closedir(self.libc_dir.as_ptr()) };
}
}
impl Iterator for Dir {
type Item = io::Result<DirEntry>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
Self::read(self)
}
}
impl fmt::Debug for Dir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("Dir");
#[cfg(not(any(target_os = "horizon", target_os = "vita")))]
s.field("fd", unsafe { &c::dirfd(self.libc_dir.as_ptr()) });
s.finish()
}
}
/// `struct dirent`
#[derive(Debug)]
pub struct DirEntry {
#[cfg(not(any(
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
)))]
d_type: u8,
#[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))]
d_ino: c::ino_t,
#[cfg(any(freebsdlike, netbsdlike))]
d_fileno: c::ino_t,
name: CString,
#[cfg(any(
linux_like,
solarish,
target_os = "fuchsia",
target_os = "hermit",
target_os = "openbsd",
target_os = "redox"
))]
d_off: c::off_t,
}
impl DirEntry {
/// Returns the file name of this directory entry.
#[inline]
pub fn file_name(&self) -> &CStr {
&self.name
}
/// Returns the “offset” of this directory entry. This is not a true
/// numerical offset but an opaque cookie that identifies a position in the
/// given stream.
#[cfg(any(
linux_like,
solarish,
target_os = "fuchsia",
target_os = "hermit",
target_os = "openbsd",
target_os = "redox"
))]
#[inline]
pub fn offset(&self) -> i64 {
self.d_off as i64
}
/// Returns the type of this directory entry.
#[cfg(not(any(
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
)))]
#[inline]
pub fn file_type(&self) -> FileType {
FileType::from_dirent_d_type(self.d_type)
}
/// Return the inode number of this directory entry.
#[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))]
#[inline]
pub fn ino(&self) -> u64 {
self.d_ino as u64
}
/// Return the inode number of this directory entry.
#[cfg(any(freebsdlike, netbsdlike))]
#[inline]
pub fn ino(&self) -> u64 {
#[allow(clippy::useless_conversion)]
self.d_fileno.into()
}
}
/// libc's OpenBSD `dirent` has a private field so we can't construct it
/// directly, so we declare it ourselves to make all fields accessible.
#[cfg(target_os = "openbsd")]
#[repr(C)]
#[derive(Debug)]
struct libc_dirent {
d_fileno: c::ino_t,
d_off: c::off_t,
d_reclen: u16,
d_type: u8,
d_namlen: u8,
__d_padding: [u8; 4],
d_name: [c::c_char; 256],
}
/// We have our own copy of OpenBSD's dirent; check that the layout
/// minimally matches libc's.
#[cfg(target_os = "openbsd")]
fn check_dirent_layout(dirent: &c::dirent) {
use crate::utils::as_ptr;
// Check that the basic layouts match.
#[cfg(test)]
{
assert_eq_size!(libc_dirent, c::dirent);
assert_eq_size!(libc_dirent, c::dirent);
}
// Check that the field offsets match.
assert_eq!(
{
let z = libc_dirent {
d_fileno: 0_u64,
d_off: 0_i64,
d_reclen: 0_u16,
d_type: 0_u8,
d_namlen: 0_u8,
__d_padding: [0_u8; 4],
d_name: [0 as c::c_char; 256],
};
let base = as_ptr(&z) as usize;
(
(as_ptr(&z.d_fileno) as usize) - base,
(as_ptr(&z.d_off) as usize) - base,
(as_ptr(&z.d_reclen) as usize) - base,
(as_ptr(&z.d_type) as usize) - base,
(as_ptr(&z.d_namlen) as usize) - base,
(as_ptr(&z.d_name) as usize) - base,
)
},
{
let z = dirent;
let base = as_ptr(z) as usize;
(
(as_ptr(&z.d_fileno) as usize) - base,
(as_ptr(&z.d_off) as usize) - base,
(as_ptr(&z.d_reclen) as usize) - base,
(as_ptr(&z.d_type) as usize) - base,
(as_ptr(&z.d_namlen) as usize) - base,
(as_ptr(&z.d_name) as usize) - base,
)
}
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dir_iterator_handles_io_errors() {
// create a dir, keep the FD, then delete the dir
let tmp = tempfile::tempdir().unwrap();
let fd = crate::fs::openat(
crate::fs::CWD,
tmp.path(),
crate::fs::OFlags::RDONLY | crate::fs::OFlags::CLOEXEC,
crate::fs::Mode::empty(),
)
.unwrap();
let file_fd = crate::fs::openat(
&fd,
tmp.path().join("test.txt"),
crate::fs::OFlags::WRONLY | crate::fs::OFlags::CREATE,
crate::fs::Mode::RWXU,
)
.unwrap();
let mut dir = Dir::read_from(&fd).unwrap();
// Reach inside the `Dir` and replace its directory with a file, which
// will cause the subsequent `readdir` to fail.
unsafe {
let raw_fd = c::dirfd(dir.libc_dir.as_ptr());
let mut owned_fd: crate::fd::OwnedFd = crate::fd::FromRawFd::from_raw_fd(raw_fd);
crate::io::dup2(&file_fd, &mut owned_fd).unwrap();
core::mem::forget(owned_fd);
}
// FreeBSD and macOS seem to read some directory entries before we call
// `.next()`.
#[cfg(any(apple, freebsdlike))]
{
dir.rewind();
}
assert!(matches!(dir.next(), Some(Err(_))));
assert!(dir.next().is_none());
}
}

View File

@@ -0,0 +1,124 @@
//! inotify support for working with inotify objects.
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `IN_*` for use with [`inotify::init`].
///
/// [`inotify::init`]: crate::fs::inotify::init
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct CreateFlags: u32 {
/// `IN_CLOEXEC`
const CLOEXEC = bitcast!(c::IN_CLOEXEC);
/// `IN_NONBLOCK`
const NONBLOCK = bitcast!(c::IN_NONBLOCK);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `IN*` for use with [`inotify::add_watch`].
///
/// [`inotify::add_watch`]: crate::fs::inotify::add_watch
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct WatchFlags: u32 {
/// `IN_ACCESS`
const ACCESS = c::IN_ACCESS;
/// `IN_ATTRIB`
const ATTRIB = c::IN_ATTRIB;
/// `IN_CLOSE_NOWRITE`
const CLOSE_NOWRITE = c::IN_CLOSE_NOWRITE;
/// `IN_CLOSE_WRITE`
const CLOSE_WRITE = c::IN_CLOSE_WRITE;
/// `IN_CREATE`
const CREATE = c::IN_CREATE;
/// `IN_DELETE`
const DELETE = c::IN_DELETE;
/// `IN_DELETE_SELF`
const DELETE_SELF = c::IN_DELETE_SELF;
/// `IN_MODIFY`
const MODIFY = c::IN_MODIFY;
/// `IN_MOVE_SELF`
const MOVE_SELF = c::IN_MOVE_SELF;
/// `IN_MOVED_FROM`
const MOVED_FROM = c::IN_MOVED_FROM;
/// `IN_MOVED_TO`
const MOVED_TO = c::IN_MOVED_TO;
/// `IN_OPEN`
const OPEN = c::IN_OPEN;
/// `IN_CLOSE`
const CLOSE = c::IN_CLOSE;
/// `IN_MOVE`
const MOVE = c::IN_MOVE;
/// `IN_ALL_EVENTS`
const ALL_EVENTS = c::IN_ALL_EVENTS;
/// `IN_DONT_FOLLOW`
const DONT_FOLLOW = c::IN_DONT_FOLLOW;
/// `IN_EXCL_UNLINK`
const EXCL_UNLINK = 1;
/// `IN_MASK_ADD`
const MASK_ADD = 1;
/// `IN_MASK_CREATE`
const MASK_CREATE = 1;
/// `IN_ONESHOT`
const ONESHOT = c::IN_ONESHOT;
/// `IN_ONLYDIR`
const ONLYDIR = c::IN_ONLYDIR;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `IN*` for use with [`inotify::Reader`].
///
/// [`inotify::Reader`]: crate::fs::inotify::Reader
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ReadFlags: u32 {
/// `IN_ACCESS`
const ACCESS = c::IN_ACCESS;
/// `IN_ATTRIB`
const ATTRIB = c::IN_ATTRIB;
/// `IN_CLOSE_NOWRITE`
const CLOSE_NOWRITE = c::IN_CLOSE_NOWRITE;
/// `IN_CLOSE_WRITE`
const CLOSE_WRITE = c::IN_CLOSE_WRITE;
/// `IN_CREATE`
const CREATE = c::IN_CREATE;
/// `IN_DELETE`
const DELETE = c::IN_DELETE;
/// `IN_DELETE_SELF`
const DELETE_SELF = c::IN_DELETE_SELF;
/// `IN_MODIFY`
const MODIFY = c::IN_MODIFY;
/// `IN_MOVE_SELF`
const MOVE_SELF = c::IN_MOVE_SELF;
/// `IN_MOVED_FROM`
const MOVED_FROM = c::IN_MOVED_FROM;
/// `IN_MOVED_TO`
const MOVED_TO = c::IN_MOVED_TO;
/// `IN_OPEN`
const OPEN = c::IN_OPEN;
/// `IN_IGNORED`
const IGNORED = c::IN_IGNORED;
/// `IN_ISDIR`
const ISDIR = c::IN_ISDIR;
/// `IN_Q_OVERFLOW`
const QUEUE_OVERFLOW = c::IN_Q_OVERFLOW;
/// `IN_UNMOUNT`
const UNMOUNT = c::IN_UNMOUNT;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,144 @@
// TODO: Remove the unsafe blocks. libc 0.2.171 removed `unsafe` from several
// of these functions. Eventually we should depend on that version and remove
// the `unsafe` blocks in the code, but for now, disable that warning so that
// we're compatible with older libc versions.
#![allow(unused_unsafe)]
#[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
use crate::backend::c;
use crate::fs::Dev;
#[cfg(not(any(
apple,
solarish,
target_os = "aix",
target_os = "android",
target_os = "emscripten",
)))]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
c::makedev(maj, min)
}
#[cfg(solarish)]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// SAFETY: Solarish's `makedev` is marked unsafe but it isn't doing
// anything unsafe.
unsafe { c::makedev(maj, min) }
}
#[cfg(all(target_os = "android", not(target_pointer_width = "32")))]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
c::makedev(maj, min)
}
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do
// it ourselves.
((u64::from(maj) & 0xffff_f000_u64) << 32)
| ((u64::from(maj) & 0x0000_0fff_u64) << 8)
| ((u64::from(min) & 0xffff_ff00_u64) << 12)
| (u64::from(min) & 0x0000_00ff_u64)
}
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// Emscripten's `makedev` has a 32-bit return value.
Dev::from(c::makedev(maj, min))
}
#[cfg(apple)]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// Apple's `makedev` oddly has signed argument types and is `unsafe`.
unsafe { c::makedev(maj as i32, min as i32) }
}
#[cfg(target_os = "aix")]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// AIX's `makedev` oddly is `unsafe`.
unsafe { c::makedev(maj, min) }
}
#[cfg(not(any(
apple,
freebsdlike,
target_os = "android",
target_os = "emscripten",
target_os = "netbsd"
)))]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
unsafe { c::major(dev) }
}
#[cfg(any(
apple,
freebsdlike,
target_os = "netbsd",
all(target_os = "android", not(target_pointer_width = "32")),
))]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
// On some platforms `major` oddly has signed return types.
(unsafe { c::major(dev) }) as u32
}
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
// 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do
// it ourselves.
(((dev >> 31 >> 1) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)) as u32
}
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
// Emscripten's `major` has a 32-bit argument value.
unsafe { c::major(dev as u32) }
}
#[cfg(not(any(
apple,
freebsdlike,
target_os = "android",
target_os = "emscripten",
target_os = "netbsd"
)))]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
unsafe { c::minor(dev) }
}
#[cfg(any(
apple,
freebsdlike,
target_os = "netbsd",
all(target_os = "android", not(target_pointer_width = "32"))
))]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
// On some platforms, `minor` oddly has signed return types.
(unsafe { c::minor(dev) }) as u32
}
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
// 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do
// it ourselves.
(((dev >> 12) & 0xffff_ff00) | (dev & 0x0000_00ff)) as u32
}
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
// Emscripten's `minor` has a 32-bit argument value.
unsafe { c::minor(dev as u32) }
}

View File

@@ -0,0 +1,27 @@
#[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))]
pub(crate) mod dir;
#[cfg(linux_kernel)]
pub mod inotify;
#[cfg(not(any(
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
pub(crate) mod makedev;
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;
// TODO: Fix linux-raw-sys to define ioctl codes for sparc.
#[cfg(all(linux_raw_dep, any(target_arch = "sparc", target_arch = "sparc64")))]
pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::Opcode = 0x8008_6610;
#[cfg(all(
linux_raw_dep,
not(any(target_arch = "sparc", target_arch = "sparc64"))
))]
pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::Opcode =
linux_raw_sys::ioctl::EXT4_IOC_RESIZE_FS as crate::ioctl::Opcode;

File diff suppressed because it is too large Load Diff

1153
vendor/rustix/src/backend/libc/fs/types.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1114
vendor/rustix/src/backend/libc/io/errno.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
pub(crate) mod errno;
#[cfg(not(windows))]
pub(crate) mod types;
#[cfg_attr(windows, path = "windows_syscalls.rs")]
pub(crate) mod syscalls;

View File

@@ -0,0 +1,303 @@
//! libc syscalls supporting `rustix::io`.
use crate::backend::c;
#[cfg(not(target_os = "wasi"))]
use crate::backend::conv::ret_discarded_fd;
use crate::backend::conv::{borrowed_fd, ret, ret_c_int, ret_owned_fd, ret_usize};
use crate::fd::{AsFd as _, BorrowedFd, OwnedFd, RawFd};
#[cfg(not(any(
target_os = "aix",
target_os = "espidf",
target_os = "nto",
target_os = "vita",
target_os = "wasi"
)))]
use crate::io::DupFlags;
#[cfg(all(linux_kernel, not(target_os = "android")))]
use crate::io::ReadWriteFlags;
use crate::io::{self, FdFlags};
use crate::ioctl::{IoctlOutput, Opcode};
use core::cmp::min;
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
use {
crate::backend::MAX_IOV,
crate::io::{IoSlice, IoSliceMut},
};
pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: (*mut u8, usize)) -> io::Result<usize> {
ret_usize(c::read(
borrowed_fd(fd),
buf.0.cast(),
min(buf.1, READ_LIMIT),
))
}
pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
unsafe {
ret_usize(c::write(
borrowed_fd(fd),
buf.as_ptr().cast(),
min(buf.len(), READ_LIMIT),
))
}
}
pub(crate) unsafe fn pread(
fd: BorrowedFd<'_>,
buf: (*mut u8, usize),
offset: u64,
) -> io::Result<usize> {
let len = min(buf.1, READ_LIMIT);
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
// ESP-IDF and Vita don't support 64-bit offsets, for example.
let offset = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
ret_usize(c::pread(borrowed_fd(fd), buf.0.cast(), len, offset))
}
pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: u64) -> io::Result<usize> {
let len = min(buf.len(), READ_LIMIT);
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
// ESP-IDF and Vita don't support 64-bit offsets, for example.
let offset = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
unsafe { ret_usize(c::pwrite(borrowed_fd(fd), buf.as_ptr().cast(), len, offset)) }
}
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
unsafe {
ret_usize(c::readv(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), MAX_IOV) as c::c_int,
))
}
}
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
unsafe {
ret_usize(c::writev(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), MAX_IOV) as c::c_int,
))
}
}
#[cfg(not(any(
target_os = "cygwin",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
)))]
pub(crate) fn preadv(
fd: BorrowedFd<'_>,
bufs: &mut [IoSliceMut<'_>],
offset: u64,
) -> io::Result<usize> {
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
// ESP-IDF and Vita don't support 64-bit offsets, for example.
let offset = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
unsafe {
ret_usize(c::preadv(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), MAX_IOV) as c::c_int,
offset,
))
}
}
#[cfg(not(any(
target_os = "cygwin",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "horizon",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
)))]
pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
// ESP-IDF and Vita don't support 64-bit offsets, for example.
let offset = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
unsafe {
ret_usize(c::pwritev(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), MAX_IOV) as c::c_int,
offset,
))
}
}
#[cfg(all(linux_kernel, not(target_os = "android")))]
pub(crate) fn preadv2(
fd: BorrowedFd<'_>,
bufs: &mut [IoSliceMut<'_>],
offset: u64,
flags: ReadWriteFlags,
) -> io::Result<usize> {
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
unsafe {
ret_usize(c::preadv2(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), MAX_IOV) as c::c_int,
offset,
bitflags_bits!(flags),
))
}
}
#[cfg(all(linux_kernel, not(target_os = "android")))]
pub(crate) fn pwritev2(
fd: BorrowedFd<'_>,
bufs: &[IoSlice<'_>],
offset: u64,
flags: ReadWriteFlags,
) -> io::Result<usize> {
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
unsafe {
ret_usize(c::pwritev2(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), MAX_IOV) as c::c_int,
offset,
bitflags_bits!(flags),
))
}
}
// These functions are derived from Rust's library/std/src/sys/unix/fd.rs at
// revision 326ef470a8b379a180d6dc4bbef08990698a737a.
// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, with the
// manual page quoting that if the count of bytes to read is greater than
// `SSIZE_MAX` the result is “unspecified”.
//
// On macOS, however, apparently the 64-bit libc is either buggy or
// intentionally showing odd behavior by rejecting any read with a size larger
// than or equal to `INT_MAX`. To handle both of these the read size is capped
// on both platforms.
#[cfg(target_os = "macos")]
const READ_LIMIT: usize = c::c_int::MAX as usize - 1;
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = c::ssize_t::MAX as usize;
pub(crate) unsafe fn close(raw_fd: RawFd) {
let _ = c::close(raw_fd as c::c_int);
}
#[cfg(feature = "try_close")]
pub(crate) unsafe fn try_close(raw_fd: RawFd) -> io::Result<()> {
ret(c::close(raw_fd as c::c_int))
}
#[inline]
pub(crate) unsafe fn ioctl(
fd: BorrowedFd<'_>,
request: Opcode,
arg: *mut c::c_void,
) -> io::Result<IoctlOutput> {
ret_c_int(c::ioctl(borrowed_fd(fd), request, arg))
}
#[inline]
pub(crate) unsafe fn ioctl_readonly(
fd: BorrowedFd<'_>,
request: Opcode,
arg: *mut c::c_void,
) -> io::Result<IoctlOutput> {
ioctl(fd, request, arg)
}
pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> {
let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFD))? };
Ok(FdFlags::from_bits_retain(bitcast!(flags)))
}
pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> {
unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFD, flags.bits())) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD_CLOEXEC, min)) }
}
#[cfg(target_os = "espidf")]
pub(crate) fn fcntl_dupfd(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD, min)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::dup(borrowed_fd(fd))) }
}
#[allow(clippy::needless_pass_by_ref_mut)]
#[cfg(not(target_os = "wasi"))]
pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> {
unsafe { ret_discarded_fd(c::dup2(borrowed_fd(fd), borrowed_fd(new.as_fd()))) }
}
#[allow(clippy::needless_pass_by_ref_mut)]
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "android",
target_os = "dragonfly",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> {
unsafe {
ret_discarded_fd(c::dup3(
borrowed_fd(fd),
borrowed_fd(new.as_fd()),
bitflags_bits!(flags),
))
}
}
#[cfg(any(
apple,
target_os = "android",
target_os = "dragonfly",
target_os = "haiku",
target_os = "redox",
))]
pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, _flags: DupFlags) -> io::Result<()> {
// Android 5.0 has `dup3`, but libc doesn't have bindings. Emulate it
// using `dup2`. We don't need to worry about the difference between
// `dup2` and `dup3` when the file descriptors are equal because we
// have an `&mut OwnedFd` which means `fd` doesn't alias it.
dup2(fd, new)
}

View File

@@ -0,0 +1,65 @@
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `FD_*` constants for use with [`fcntl_getfd`] and [`fcntl_setfd`].
///
/// [`fcntl_getfd`]: crate::io::fcntl_getfd
/// [`fcntl_setfd`]: crate::io::fcntl_setfd
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FdFlags: u32 {
/// `FD_CLOEXEC`
const CLOEXEC = bitcast!(c::FD_CLOEXEC);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(all(linux_kernel, not(target_os = "android")))]
bitflags! {
/// `RWF_*` constants for use with [`preadv2`] and [`pwritev2`].
///
/// [`preadv2`]: crate::io::preadv2
/// [`pwritev2`]: crate::io::pwritev
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ReadWriteFlags: u32 {
/// `RWF_DSYNC` (since Linux 4.7)
const DSYNC = libc::RWF_DSYNC as u32;
/// `RWF_HIPRI` (since Linux 4.6)
const HIPRI = libc::RWF_HIPRI as u32;
/// `RWF_SYNC` (since Linux 4.7)
const SYNC = libc::RWF_SYNC as u32;
/// `RWF_NOWAIT` (since Linux 4.14)
const NOWAIT = libc::RWF_NOWAIT as u32;
/// `RWF_APPEND` (since Linux 4.16)
const APPEND = libc::RWF_APPEND as u32;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(not(target_os = "wasi"))]
bitflags! {
/// `O_*` constants for use with [`dup2`].
///
/// [`dup2`]: crate::io::dup2
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct DupFlags: u32 {
/// `O_CLOEXEC`
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "android",
target_os = "redox",
)))] // Android 5.0 has dup3, but libc doesn't have bindings
const CLOEXEC = bitcast!(c::O_CLOEXEC);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,58 @@
//! Windows system calls in the `io` module.
use crate::backend::c;
#[cfg(feature = "try_close")]
use crate::backend::conv::ret;
use crate::backend::conv::{borrowed_fd, ret_c_int, ret_send_recv, send_recv_len};
use crate::fd::{BorrowedFd, RawFd};
use crate::io;
use crate::ioctl::{IoctlOutput, Opcode};
pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: (*mut u8, usize)) -> io::Result<usize> {
// `read` on a socket is equivalent to `recv` with no flags.
ret_send_recv(c::recv(
borrowed_fd(fd),
buf.0.cast(),
send_recv_len(buf.1),
0,
))
}
pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
// `write` on a socket is equivalent to `send` with no flags.
unsafe {
ret_send_recv(c::send(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
0,
))
}
}
pub(crate) unsafe fn close(raw_fd: RawFd) {
let _ = c::closesocket(raw_fd as c::SOCKET);
}
#[cfg(feature = "try_close")]
pub(crate) unsafe fn try_close(raw_fd: RawFd) -> io::Result<()> {
ret(c::closesocket(raw_fd as c::SOCKET))
}
#[inline]
pub(crate) unsafe fn ioctl(
fd: BorrowedFd<'_>,
request: Opcode,
arg: *mut c::c_void,
) -> io::Result<IoctlOutput> {
ret_c_int(c::ioctl(borrowed_fd(fd), request, arg.cast()))
}
#[inline]
pub(crate) unsafe fn ioctl_readonly(
fd: BorrowedFd<'_>,
request: Opcode,
arg: *mut c::c_void,
) -> io::Result<IoctlOutput> {
ioctl(fd, request, arg)
}

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -0,0 +1,94 @@
//! libc syscalls supporting `rustix::io_uring`.
use crate::backend::c;
use crate::backend::conv::{borrowed_fd, ret_owned_fd, ret_u32};
use crate::fd::{BorrowedFd, OwnedFd};
use crate::io;
use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterFlags, IoringRegisterOp};
#[inline]
pub(crate) fn io_uring_setup(entries: u32, params: &mut io_uring_params) -> io::Result<OwnedFd> {
syscall! {
fn io_uring_setup(
entries: u32,
params: *mut io_uring_params
) via SYS_io_uring_setup -> c::c_int
}
unsafe { ret_owned_fd(io_uring_setup(entries, params)) }
}
#[inline]
pub(crate) unsafe fn io_uring_register(
fd: BorrowedFd<'_>,
opcode: IoringRegisterOp,
arg: *const c::c_void,
nr_args: u32,
) -> io::Result<u32> {
syscall! {
fn io_uring_register(
fd: c::c_uint,
opcode: c::c_uint,
arg: *const c::c_void,
nr_args: c::c_uint
) via SYS_io_uring_register -> c::c_int
}
ret_u32(io_uring_register(
borrowed_fd(fd) as _,
opcode as u32,
arg,
nr_args,
))
}
#[inline]
pub(crate) unsafe fn io_uring_register_with(
fd: BorrowedFd<'_>,
opcode: IoringRegisterOp,
flags: IoringRegisterFlags,
arg: *const c::c_void,
nr_args: u32,
) -> io::Result<u32> {
syscall! {
fn io_uring_register(
fd: c::c_uint,
opcode: c::c_uint,
arg: *const c::c_void,
nr_args: c::c_uint
) via SYS_io_uring_register -> c::c_int
}
ret_u32(io_uring_register(
borrowed_fd(fd) as _,
(opcode as u32) | bitflags_bits!(flags),
arg,
nr_args,
))
}
#[inline]
pub(crate) unsafe fn io_uring_enter(
fd: BorrowedFd<'_>,
to_submit: u32,
min_complete: u32,
flags: IoringEnterFlags,
arg: *const c::c_void,
size: usize,
) -> io::Result<u32> {
syscall! {
fn io_uring_enter2(
fd: c::c_uint,
to_submit: c::c_uint,
min_complete: c::c_uint,
flags: c::c_uint,
arg: *const c::c_void,
size: usize
) via SYS_io_uring_enter -> c::c_int
}
ret_u32(io_uring_enter2(
borrowed_fd(fd) as _,
to_submit,
min_complete,
bitflags_bits!(flags),
arg,
size,
))
}

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,244 @@
//! libc syscalls supporting `rustix::mm`.
#[cfg(not(target_os = "redox"))]
use super::types::Advice;
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
use super::types::MlockAllFlags;
#[cfg(any(target_os = "emscripten", target_os = "linux"))]
use super::types::MremapFlags;
use super::types::{MapFlags, MprotectFlags, MsyncFlags, ProtFlags};
#[cfg(linux_kernel)]
use super::types::{MlockFlags, UserfaultfdFlags};
use crate::backend::c;
#[cfg(linux_kernel)]
use crate::backend::conv::ret_owned_fd;
use crate::backend::conv::{borrowed_fd, no_fd, ret};
use crate::fd::BorrowedFd;
#[cfg(linux_kernel)]
use crate::fd::OwnedFd;
use crate::io;
#[cfg(not(target_os = "redox"))]
pub(crate) fn madvise(addr: *mut c::c_void, len: usize, advice: Advice) -> io::Result<()> {
// On Linux platforms, `MADV_DONTNEED` has the same value as
// `POSIX_MADV_DONTNEED` but different behavior. We remap it to a different
// value, and check for it here.
#[cfg(target_os = "linux")]
if let Advice::LinuxDontNeed = advice {
return unsafe { ret(c::madvise(addr, len, c::MADV_DONTNEED)) };
}
#[cfg(not(target_os = "android"))]
{
let err = unsafe { c::posix_madvise(addr, len, advice as c::c_int) };
// `posix_madvise` returns its error status rather than using `errno`.
if err == 0 {
Ok(())
} else {
Err(io::Errno(err))
}
}
#[cfg(target_os = "android")]
{
if let Advice::DontNeed = advice {
// Do nothing. Linux's `MADV_DONTNEED` isn't the same as
// `POSIX_MADV_DONTNEED`, so just discard `MADV_DONTNEED`.
Ok(())
} else {
unsafe { ret(c::madvise(addr, len, advice as c::c_int)) }
}
}
}
pub(crate) unsafe fn msync(addr: *mut c::c_void, len: usize, flags: MsyncFlags) -> io::Result<()> {
let err = c::msync(addr, len, bitflags_bits!(flags));
// `msync` returns its error status rather than using `errno`.
if err == 0 {
Ok(())
} else {
Err(io::Errno(err))
}
}
/// # Safety
///
/// `mmap` is primarily unsafe due to the `addr` parameter, as anything working
/// with memory pointed to by raw pointers is unsafe.
pub(crate) unsafe fn mmap(
ptr: *mut c::c_void,
len: usize,
prot: ProtFlags,
flags: MapFlags,
fd: BorrowedFd<'_>,
offset: u64,
) -> io::Result<*mut c::c_void> {
let res = c::mmap(
ptr,
len,
bitflags_bits!(prot),
bitflags_bits!(flags),
borrowed_fd(fd),
offset as i64,
);
if res == c::MAP_FAILED {
Err(io::Errno::last_os_error())
} else {
Ok(res)
}
}
/// # Safety
///
/// `mmap` is primarily unsafe due to the `addr` parameter, as anything working
/// with memory pointed to by raw pointers is unsafe.
pub(crate) unsafe fn mmap_anonymous(
ptr: *mut c::c_void,
len: usize,
prot: ProtFlags,
flags: MapFlags,
) -> io::Result<*mut c::c_void> {
let res = c::mmap(
ptr,
len,
bitflags_bits!(prot),
bitflags_bits!(flags | MapFlags::from_bits_retain(bitcast!(c::MAP_ANONYMOUS))),
no_fd(),
0,
);
if res == c::MAP_FAILED {
Err(io::Errno::last_os_error())
} else {
Ok(res)
}
}
pub(crate) unsafe fn mprotect(
ptr: *mut c::c_void,
len: usize,
flags: MprotectFlags,
) -> io::Result<()> {
ret(c::mprotect(ptr, len, bitflags_bits!(flags)))
}
pub(crate) unsafe fn munmap(ptr: *mut c::c_void, len: usize) -> io::Result<()> {
ret(c::munmap(ptr, len))
}
/// # Safety
///
/// `mremap` is primarily unsafe due to the `old_address` parameter, as
/// anything working with memory pointed to by raw pointers is unsafe.
#[cfg(any(target_os = "emscripten", target_os = "linux"))]
pub(crate) unsafe fn mremap(
old_address: *mut c::c_void,
old_size: usize,
new_size: usize,
flags: MremapFlags,
) -> io::Result<*mut c::c_void> {
let res = c::mremap(old_address, old_size, new_size, bitflags_bits!(flags));
if res == c::MAP_FAILED {
Err(io::Errno::last_os_error())
} else {
Ok(res)
}
}
/// # Safety
///
/// `mremap_fixed` is primarily unsafe due to the `old_address` and
/// `new_address` parameters, as anything working with memory pointed to by raw
/// pointers is unsafe.
#[cfg(any(target_os = "emscripten", target_os = "linux"))]
pub(crate) unsafe fn mremap_fixed(
old_address: *mut c::c_void,
old_size: usize,
new_size: usize,
flags: MremapFlags,
new_address: *mut c::c_void,
) -> io::Result<*mut c::c_void> {
let res = c::mremap(
old_address,
old_size,
new_size,
bitflags_bits!(flags | MremapFlags::from_bits_retain(bitcast!(c::MAP_FIXED))),
new_address,
);
if res == c::MAP_FAILED {
Err(io::Errno::last_os_error())
} else {
Ok(res)
}
}
/// # Safety
///
/// `mlock` operates on raw pointers and may round out to the nearest page
/// boundaries.
#[inline]
pub(crate) unsafe fn mlock(addr: *mut c::c_void, length: usize) -> io::Result<()> {
ret(c::mlock(addr, length))
}
/// # Safety
///
/// `mlock_with` operates on raw pointers and may round out to the nearest page
/// boundaries.
#[cfg(linux_kernel)]
#[inline]
pub(crate) unsafe fn mlock_with(
addr: *mut c::c_void,
length: usize,
flags: MlockFlags,
) -> io::Result<()> {
weak_or_syscall! {
fn mlock2(
addr: *const c::c_void,
len: c::size_t,
flags: c::c_int
) via SYS_mlock2 -> c::c_int
}
ret(mlock2(addr, length, bitflags_bits!(flags)))
}
/// # Safety
///
/// `munlock` operates on raw pointers and may round out to the nearest page
/// boundaries.
#[inline]
pub(crate) unsafe fn munlock(addr: *mut c::c_void, length: usize) -> io::Result<()> {
ret(c::munlock(addr, length))
}
#[cfg(linux_kernel)]
pub(crate) unsafe fn userfaultfd(flags: UserfaultfdFlags) -> io::Result<OwnedFd> {
syscall! {
fn userfaultfd(
flags: c::c_int
) via SYS_userfaultfd -> c::c_int
}
ret_owned_fd(userfaultfd(bitflags_bits!(flags)))
}
/// Locks all pages mapped into the address space of the calling process.
///
/// This includes the pages of the code, data, and stack segment, as well as
/// shared libraries, user space kernel data, shared memory, and memory-mapped
/// files. All mapped pages are guaranteed to be resident in RAM when the call
/// returns successfully; the pages are guaranteed to stay in RAM until later
/// unlocked.
#[inline]
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
pub(crate) fn mlockall(flags: MlockAllFlags) -> io::Result<()> {
unsafe { ret(c::mlockall(bitflags_bits!(flags))) }
}
/// Unlocks all pages mapped into the address space of the calling process.
#[inline]
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
pub(crate) fn munlockall() -> io::Result<()> {
unsafe { ret(c::munlockall()) }
}

View File

@@ -0,0 +1,506 @@
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `PROT_*` flags for use with [`mmap`].
///
/// For `PROT_NONE`, use `ProtFlags::empty()`.
///
/// [`mmap`]: crate::mm::mmap
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ProtFlags: u32 {
/// `PROT_READ`
const READ = bitcast!(c::PROT_READ);
/// `PROT_WRITE`
const WRITE = bitcast!(c::PROT_WRITE);
/// `PROT_EXEC`
const EXEC = bitcast!(c::PROT_EXEC);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `PROT_*` flags for use with [`mprotect`].
///
/// For `PROT_NONE`, use `MprotectFlags::empty()`.
///
/// [`mprotect`]: crate::mm::mprotect
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MprotectFlags: u32 {
/// `PROT_READ`
const READ = bitcast!(c::PROT_READ);
/// `PROT_WRITE`
const WRITE = bitcast!(c::PROT_WRITE);
/// `PROT_EXEC`
const EXEC = bitcast!(c::PROT_EXEC);
/// `PROT_GROWSUP`
#[cfg(linux_kernel)]
const GROWSUP = bitcast!(c::PROT_GROWSUP);
/// `PROT_GROWSDOWN`
#[cfg(linux_kernel)]
const GROWSDOWN = bitcast!(c::PROT_GROWSDOWN);
/// `PROT_SEM`
#[cfg(linux_raw_dep)]
const SEM = linux_raw_sys::general::PROT_SEM;
/// `PROT_BTI`
#[cfg(all(linux_raw_dep, target_arch = "aarch64"))]
const BTI = linux_raw_sys::general::PROT_BTI;
/// `PROT_MTE`
#[cfg(all(linux_raw_dep, target_arch = "aarch64"))]
const MTE = linux_raw_sys::general::PROT_MTE;
/// `PROT_SAO`
#[cfg(all(linux_raw_dep, any(target_arch = "powerpc", target_arch = "powerpc64")))]
const SAO = linux_raw_sys::general::PROT_SAO;
/// `PROT_ADI`
#[cfg(all(linux_raw_dep, any(target_arch = "sparc", target_arch = "sparc64")))]
const ADI = linux_raw_sys::general::PROT_ADI;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `MAP_*` flags for use with [`mmap`].
///
/// For `MAP_ANONYMOUS` (aka `MAP_ANON`), see [`mmap_anonymous`].
///
/// [`mmap`]: crate::mm::mmap
/// [`mmap_anonymous`]: crates::mm::mmap_anonymous
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MapFlags: u32 {
/// `MAP_SHARED`
const SHARED = bitcast!(c::MAP_SHARED);
/// `MAP_SHARED_VALIDATE`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "android",
target_os = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const SHARED_VALIDATE = bitcast!(c::MAP_SHARED_VALIDATE);
/// `MAP_PRIVATE`
const PRIVATE = bitcast!(c::MAP_PRIVATE);
/// `MAP_DENYWRITE`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const DENYWRITE = bitcast!(c::MAP_DENYWRITE);
/// `MAP_FIXED`
const FIXED = bitcast!(c::MAP_FIXED);
/// `MAP_FIXED_NOREPLACE`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "android",
target_os = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const FIXED_NOREPLACE = bitcast!(c::MAP_FIXED_NOREPLACE);
/// `MAP_GROWSDOWN`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const GROWSDOWN = bitcast!(c::MAP_GROWSDOWN);
/// `MAP_HUGETLB`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const HUGETLB = bitcast!(c::MAP_HUGETLB);
/// `MAP_HUGE_2MB`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "android",
target_os = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const HUGE_2MB = bitcast!(c::MAP_HUGE_2MB);
/// `MAP_HUGE_1GB`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "android",
target_os = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const HUGE_1GB = bitcast!(c::MAP_HUGE_1GB);
/// `MAP_LOCKED`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const LOCKED = bitcast!(c::MAP_LOCKED);
/// `MAP_NOCORE`
#[cfg(freebsdlike)]
const NOCORE = bitcast!(c::MAP_NOCORE);
/// `MAP_NORESERVE`
#[cfg(not(any(
freebsdlike,
target_os = "aix",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const NORESERVE = bitcast!(c::MAP_NORESERVE);
/// `MAP_NOSYNC`
#[cfg(freebsdlike)]
const NOSYNC = bitcast!(c::MAP_NOSYNC);
/// `MAP_POPULATE`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const POPULATE = bitcast!(c::MAP_POPULATE);
/// `MAP_STACK`
#[cfg(not(any(
apple,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "redox",
)))]
const STACK = bitcast!(c::MAP_STACK);
/// `MAP_PREFAULT_READ`
#[cfg(target_os = "freebsd")]
const PREFAULT_READ = bitcast!(c::MAP_PREFAULT_READ);
/// `MAP_SYNC`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "android",
target_os = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
all(
linux_kernel,
any(target_arch = "mips", target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6"),
),
)))]
const SYNC = bitcast!(c::MAP_SYNC);
/// `MAP_UNINITIALIZED`
#[cfg(any())]
const UNINITIALIZED = bitcast!(c::MAP_UNINITIALIZED);
/// `MAP_DROPPABLE`
#[cfg(all(linux_kernel, not(target_os = "android")))]
const DROPPABLE = bitcast!(c::MAP_DROPPABLE);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(any(target_os = "emscripten", target_os = "linux"))]
bitflags! {
/// `MREMAP_*` flags for use with [`mremap`].
///
/// For `MREMAP_FIXED`, see [`mremap_fixed`].
///
/// [`mremap`]: crate::mm::mremap
/// [`mremap_fixed`]: crate::mm::mremap_fixed
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MremapFlags: u32 {
/// `MREMAP_MAYMOVE`
const MAYMOVE = bitcast!(c::MREMAP_MAYMOVE);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `MS_*` flags for use with [`msync`].
///
/// [`msync`]: crate::mm::msync
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MsyncFlags: u32 {
/// `MS_SYNC`—Requests an update and waits for it to complete.
const SYNC = bitcast!(c::MS_SYNC);
/// `MS_ASYNC`—Specifies that an update be scheduled, but the call
/// returns immediately.
const ASYNC = bitcast!(c::MS_ASYNC);
/// `MS_INVALIDATE`—Asks to invalidate other mappings of the same
/// file (so that they can be updated with the fresh values just
/// written).
const INVALIDATE = bitcast!(c::MS_INVALIDATE);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
/// `MLOCK_*` flags for use with [`mlock_with`].
///
/// [`mlock_with`]: crate::mm::mlock_with
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MlockFlags: u32 {
/// `MLOCK_ONFAULT`
const ONFAULT = bitcast!(c::MLOCK_ONFAULT);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
/// `POSIX_MADV_*` constants for use with [`madvise`].
///
/// [`madvise`]: crate::mm::madvise
#[cfg(not(target_os = "redox"))]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
#[non_exhaustive]
pub enum Advice {
/// `POSIX_MADV_NORMAL`
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
Normal = bitcast!(c::POSIX_MADV_NORMAL),
/// `POSIX_MADV_NORMAL`
#[cfg(any(target_os = "android", target_os = "haiku"))]
Normal = bitcast!(c::MADV_NORMAL),
/// `POSIX_MADV_SEQUENTIAL`
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
Sequential = bitcast!(c::POSIX_MADV_SEQUENTIAL),
/// `POSIX_MADV_SEQUENTIAL`
#[cfg(any(target_os = "android", target_os = "haiku"))]
Sequential = bitcast!(c::MADV_SEQUENTIAL),
/// `POSIX_MADV_RANDOM`
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
Random = bitcast!(c::POSIX_MADV_RANDOM),
/// `POSIX_MADV_RANDOM`
#[cfg(any(target_os = "android", target_os = "haiku"))]
Random = bitcast!(c::MADV_RANDOM),
/// `POSIX_MADV_WILLNEED`
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
WillNeed = bitcast!(c::POSIX_MADV_WILLNEED),
/// `POSIX_MADV_WILLNEED`
#[cfg(any(target_os = "android", target_os = "haiku"))]
WillNeed = bitcast!(c::MADV_WILLNEED),
/// `POSIX_MADV_DONTNEED`
#[cfg(not(any(
target_os = "android",
target_os = "emscripten",
target_os = "haiku",
target_os = "hurd",
)))]
DontNeed = bitcast!(c::POSIX_MADV_DONTNEED),
/// `POSIX_MADV_DONTNEED`
#[cfg(any(target_os = "android", target_os = "haiku"))]
DontNeed = bitcast!(i32::MAX - 1),
/// `MADV_DONTNEED`
// `MADV_DONTNEED` has the same value as `POSIX_MADV_DONTNEED`. We don't
// have a separate `posix_madvise` from `madvise`, so we expose a special
// value which we special-case.
#[cfg(target_os = "linux")]
LinuxDontNeed = bitcast!(i32::MAX),
/// `MADV_DONTNEED`
#[cfg(target_os = "android")]
LinuxDontNeed = bitcast!(c::MADV_DONTNEED),
/// `MADV_FREE`
#[cfg(linux_kernel)]
LinuxFree = bitcast!(c::MADV_FREE),
/// `MADV_REMOVE`
#[cfg(linux_kernel)]
LinuxRemove = bitcast!(c::MADV_REMOVE),
/// `MADV_DONTFORK`
#[cfg(linux_kernel)]
LinuxDontFork = bitcast!(c::MADV_DONTFORK),
/// `MADV_DOFORK`
#[cfg(linux_kernel)]
LinuxDoFork = bitcast!(c::MADV_DOFORK),
/// `MADV_HWPOISON`
#[cfg(linux_kernel)]
LinuxHwPoison = bitcast!(c::MADV_HWPOISON),
/// `MADV_SOFT_OFFLINE`
#[cfg(all(
linux_kernel,
not(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))
))]
LinuxSoftOffline = bitcast!(c::MADV_SOFT_OFFLINE),
/// `MADV_MERGEABLE`
#[cfg(linux_kernel)]
LinuxMergeable = bitcast!(c::MADV_MERGEABLE),
/// `MADV_UNMERGEABLE`
#[cfg(linux_kernel)]
LinuxUnmergeable = bitcast!(c::MADV_UNMERGEABLE),
/// `MADV_HUGEPAGE`
#[cfg(linux_kernel)]
LinuxHugepage = bitcast!(c::MADV_HUGEPAGE),
/// `MADV_NOHUGEPAGE`
#[cfg(linux_kernel)]
LinuxNoHugepage = bitcast!(c::MADV_NOHUGEPAGE),
/// `MADV_DONTDUMP` (since Linux 3.4)
#[cfg(linux_kernel)]
LinuxDontDump = bitcast!(c::MADV_DONTDUMP),
/// `MADV_DODUMP` (since Linux 3.4)
#[cfg(linux_kernel)]
LinuxDoDump = bitcast!(c::MADV_DODUMP),
/// `MADV_WIPEONFORK` (since Linux 4.14)
#[cfg(linux_kernel)]
LinuxWipeOnFork = bitcast!(c::MADV_WIPEONFORK),
/// `MADV_KEEPONFORK` (since Linux 4.14)
#[cfg(linux_kernel)]
LinuxKeepOnFork = bitcast!(c::MADV_KEEPONFORK),
/// `MADV_COLD` (since Linux 5.4)
#[cfg(linux_kernel)]
LinuxCold = bitcast!(c::MADV_COLD),
/// `MADV_PAGEOUT` (since Linux 5.4)
#[cfg(linux_kernel)]
LinuxPageOut = bitcast!(c::MADV_PAGEOUT),
/// `MADV_POPULATE_READ` (since Linux 5.14)
#[cfg(linux_kernel)]
LinuxPopulateRead = bitcast!(c::MADV_POPULATE_READ),
/// `MADV_POPULATE_WRITE` (since Linux 5.14)
#[cfg(linux_kernel)]
LinuxPopulateWrite = bitcast!(c::MADV_POPULATE_WRITE),
/// `MADV_DONTNEED_LOCKED` (since Linux 5.18)
#[cfg(linux_kernel)]
LinuxDontneedLocked = bitcast!(c::MADV_DONTNEED_LOCKED),
}
#[cfg(target_os = "emscripten")]
#[allow(non_upper_case_globals)]
impl Advice {
/// `POSIX_MADV_DONTNEED`
pub const DontNeed: Self = Self::Normal;
}
#[cfg(linux_kernel)]
bitflags! {
/// `O_*` flags for use with [`userfaultfd`].
///
/// [`userfaultfd`]: crate::mm::userfaultfd
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct UserfaultfdFlags: u32 {
/// `O_CLOEXEC`
const CLOEXEC = bitcast!(c::O_CLOEXEC);
/// `O_NONBLOCK`
const NONBLOCK = bitcast!(c::O_NONBLOCK);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(any(linux_kernel, freebsdlike, netbsdlike))]
bitflags! {
/// `MCL_*` flags for use with [`mlockall`].
///
/// [`mlockall`]: crate::mm::mlockall
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MlockAllFlags: u32 {
/// Used together with `MCL_CURRENT`, `MCL_FUTURE`, or both. Mark all
/// current (with `MCL_CURRENT`) or future (with `MCL_FUTURE`) mappings
/// to lock pages when they are faulted in. When used with
/// `MCL_CURRENT`, all present pages are locked, but `mlockall` will
/// not fault in non-present pages. When used with `MCL_FUTURE`, all
/// future mappings will be marked to lock pages when they are faulted
/// in, but they will not be populated by the lock when the mapping is
/// created. `MCL_ONFAULT` must be used with either `MCL_CURRENT` or
/// `MCL_FUTURE` or both.
#[cfg(linux_kernel)]
const ONFAULT = bitcast!(c::MCL_ONFAULT);
/// Lock all pages which will become mapped into the address space of
/// the process in the future. These could be, for instance, new pages
/// required by a growing heap and stack as well as new memory-mapped
/// files or shared memory regions.
const FUTURE = bitcast!(c::MCL_FUTURE);
/// Lock all pages which are currently mapped into the address space of
/// the process.
const CURRENT = bitcast!(c::MCL_CURRENT);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

220
vendor/rustix/src/backend/libc/mod.rs vendored Normal file
View File

@@ -0,0 +1,220 @@
//! The libc backend.
//!
//! On most platforms, this uses the `libc` crate to make system calls. On
//! Windows, this uses the Winsock API in `windows-sys`, which can be adapted
//! to have a very `libc`-like interface.
// Every FFI call requires an unsafe block, and there are a lot of FFI
// calls. For now, set this to allow for the libc backend.
#![allow(clippy::undocumented_unsafe_blocks)]
// Lots of libc types vary between platforms, so we often need a `.into()` on
// one platform where it's redundant on another.
#![allow(clippy::useless_conversion)]
mod conv;
#[cfg(windows)]
pub(crate) mod fd {
// Re-export `AsSocket` etc. too, as users can't implement `AsFd` etc. on
// Windows due to them having blanket impls on Windows, so users must
// implement `AsSocket` etc.
pub use crate::maybe_polyfill::os::windows::io::{
AsRawSocket, AsSocket, BorrowedSocket as BorrowedFd, FromRawSocket, IntoRawSocket,
OwnedSocket as OwnedFd, RawSocket as RawFd,
};
pub(crate) use windows_sys::Win32::Networking::WinSock::SOCKET as LibcFd;
/// A version of [`AsRawFd`] for use with Winsock API.
///
/// [`AsRawFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.AsRawFd.html
pub trait AsRawFd {
/// A version of [`as_raw_fd`] for use with Winsock API.
///
/// [`as_raw_fd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.AsRawFd.html#tymethod.as_raw_fd
fn as_raw_fd(&self) -> RawFd;
}
impl<T: AsRawSocket> AsRawFd for T {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_raw_socket()
}
}
/// A version of [`IntoRawFd`] for use with Winsock API.
///
/// [`IntoRawFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.IntoRawFd.html
pub trait IntoRawFd {
/// A version of [`into_raw_fd`] for use with Winsock API.
///
/// [`into_raw_fd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.IntoRawFd.html#tymethod.into_raw_fd
fn into_raw_fd(self) -> RawFd;
}
impl<T: IntoRawSocket> IntoRawFd for T {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.into_raw_socket()
}
}
/// A version of [`FromRawFd`] for use with Winsock API.
///
/// [`FromRawFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html
pub trait FromRawFd {
/// A version of [`from_raw_fd`] for use with Winsock API.
///
/// # Safety
///
/// See the [safety requirements] for [`from_raw_fd`].
///
/// [`from_raw_fd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html#tymethod.from_raw_fd
/// [safety requirements]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html#safety
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self;
}
impl<T: FromRawSocket> FromRawFd for T {
#[inline]
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
Self::from_raw_socket(raw_fd)
}
}
/// A version of [`AsFd`] for use with Winsock API.
///
/// [`AsFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.AsFd.html
pub trait AsFd {
/// An `as_fd` function for Winsock, where an `Fd` is a `Socket`.
fn as_fd(&self) -> BorrowedFd<'_>;
}
impl<T: AsSocket> AsFd for T {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_socket()
}
}
}
#[cfg(not(windows))]
pub(crate) mod fd {
pub use crate::maybe_polyfill::os::fd::*;
#[allow(unused_imports)]
pub(crate) use RawFd as LibcFd;
}
// On Windows we emulate selected libc-compatible interfaces. On non-Windows,
// we just use libc here, since this is the libc backend.
#[cfg_attr(windows, path = "winsock_c.rs")]
pub(crate) mod c;
#[cfg(feature = "event")]
pub(crate) mod event;
#[cfg(not(windows))]
#[cfg(feature = "fs")]
pub(crate) mod fs;
pub(crate) mod io;
#[cfg(linux_kernel)]
#[cfg(feature = "io_uring")]
pub(crate) mod io_uring;
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[cfg(feature = "mm")]
pub(crate) mod mm;
#[cfg(linux_kernel)]
#[cfg(feature = "mount")]
pub(crate) mod mount;
#[cfg(not(target_os = "wasi"))]
#[cfg(feature = "net")]
pub(crate) mod net;
#[cfg(not(any(windows, target_os = "espidf")))]
#[cfg(any(
feature = "param",
feature = "runtime",
feature = "time",
target_arch = "x86",
))]
pub(crate) mod param;
#[cfg(not(windows))]
#[cfg(feature = "pipe")]
pub(crate) mod pipe;
#[cfg(not(windows))]
#[cfg(feature = "process")]
pub(crate) mod process;
#[cfg(not(windows))]
#[cfg(not(target_os = "wasi"))]
#[cfg(feature = "pty")]
pub(crate) mod pty;
#[cfg(not(windows))]
#[cfg(feature = "rand")]
pub(crate) mod rand;
#[cfg(not(windows))]
#[cfg(not(target_os = "wasi"))]
#[cfg(feature = "system")]
pub(crate) mod system;
#[cfg(not(any(windows, target_os = "horizon", target_os = "vita")))]
#[cfg(feature = "termios")]
pub(crate) mod termios;
#[cfg(not(windows))]
#[cfg(feature = "thread")]
pub(crate) mod thread;
#[cfg(not(any(windows, target_os = "espidf")))]
#[cfg(feature = "time")]
pub(crate) mod time;
/// If the host libc is glibc, return `true` if it is less than version 2.25.
///
/// To restate and clarify, this function returning true does not mean the libc
/// is glibc just that if it is glibc, it is less than version 2.25.
///
/// For now, this function is only available on Linux, but if it ends up being
/// used beyond that, this could be changed to e.g. `#[cfg(unix)]`.
#[cfg(all(unix, target_env = "gnu"))]
pub(crate) fn if_glibc_is_less_than_2_25() -> bool {
// This is also defined inside `weak_or_syscall!` in
// backend/libc/rand/syscalls.rs, but it's not convenient to re-export the
// weak symbol from that macro, so we duplicate it at a small cost here.
weak! { fn getrandom(*mut c::c_void, c::size_t, c::c_uint) -> c::ssize_t }
// glibc 2.25 has `getrandom`, which is how we satisfy the API contract of
// this function. But, there are likely other libc versions which have it.
getrandom.get().is_none()
}
// Private modules used by multiple public modules.
#[cfg(any(feature = "process", feature = "runtime"))]
#[cfg(not(any(windows, target_os = "wasi")))]
pub(crate) mod pid;
#[cfg(any(feature = "process", feature = "thread"))]
#[cfg(linux_kernel)]
pub(crate) mod prctl;
#[cfg(not(any(
windows,
target_os = "android",
target_os = "espidf",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[cfg(feature = "shm")]
pub(crate) mod shm;
#[cfg(any(feature = "fs", feature = "thread", feature = "process"))]
#[cfg(not(any(windows, target_os = "wasi")))]
pub(crate) mod ugid;
#[cfg(bsd)]
const MAX_IOV: usize = c::IOV_MAX as usize;
#[cfg(any(linux_kernel, target_os = "emscripten", target_os = "nto"))]
const MAX_IOV: usize = c::UIO_MAXIOV as usize;
#[cfg(not(any(
bsd,
linux_kernel,
windows,
target_os = "emscripten",
target_os = "espidf",
target_os = "nto",
target_os = "horizon",
)))]
const MAX_IOV: usize = 16; // The minimum value required by POSIX.

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,268 @@
use crate::backend::c;
use crate::backend::conv::{borrowed_fd, c_str, ret, ret_owned_fd};
use crate::fd::{BorrowedFd, OwnedFd};
use crate::ffi::CStr;
use crate::io;
use core::ptr::null;
#[cfg(linux_kernel)]
pub(crate) fn mount(
source: Option<&CStr>,
target: &CStr,
file_system_type: Option<&CStr>,
flags: super::types::MountFlagsArg,
data: Option<&CStr>,
) -> io::Result<()> {
unsafe {
ret(c::mount(
source.map_or_else(null, CStr::as_ptr),
target.as_ptr(),
file_system_type.map_or_else(null, CStr::as_ptr),
flags.0,
data.map_or_else(null, CStr::as_ptr).cast(),
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn unmount(target: &CStr, flags: super::types::UnmountFlags) -> io::Result<()> {
unsafe { ret(c::umount2(target.as_ptr(), bitflags_bits!(flags))) }
}
#[cfg(linux_kernel)]
pub(crate) fn fsopen(fs_name: &CStr, flags: super::types::FsOpenFlags) -> io::Result<OwnedFd> {
syscall! {
fn fsopen(
fs_name: *const c::c_char,
flags: c::c_uint
) via SYS_fsopen -> c::c_int
}
unsafe { ret_owned_fd(fsopen(c_str(fs_name), flags.bits())) }
}
#[cfg(linux_kernel)]
pub(crate) fn fsmount(
fs_fd: BorrowedFd<'_>,
flags: super::types::FsMountFlags,
attr_flags: super::types::MountAttrFlags,
) -> io::Result<OwnedFd> {
syscall! {
fn fsmount(
fs_fd: c::c_int,
flags: c::c_uint,
attr_flags: c::c_uint
) via SYS_fsmount -> c::c_int
}
unsafe { ret_owned_fd(fsmount(borrowed_fd(fs_fd), flags.bits(), attr_flags.bits())) }
}
#[cfg(linux_kernel)]
pub(crate) fn move_mount(
from_dfd: BorrowedFd<'_>,
from_pathname: &CStr,
to_dfd: BorrowedFd<'_>,
to_pathname: &CStr,
flags: super::types::MoveMountFlags,
) -> io::Result<()> {
syscall! {
fn move_mount(
from_dfd: c::c_int,
from_pathname: *const c::c_char,
to_dfd: c::c_int,
to_pathname: *const c::c_char,
flags: c::c_uint
) via SYS_move_mount -> c::c_int
}
unsafe {
ret(move_mount(
borrowed_fd(from_dfd),
c_str(from_pathname),
borrowed_fd(to_dfd),
c_str(to_pathname),
flags.bits(),
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn open_tree(
dfd: BorrowedFd<'_>,
filename: &CStr,
flags: super::types::OpenTreeFlags,
) -> io::Result<OwnedFd> {
syscall! {
fn open_tree(
dfd: c::c_int,
filename: *const c::c_char,
flags: c::c_uint
) via SYS_open_tree -> c::c_int
}
unsafe { ret_owned_fd(open_tree(borrowed_fd(dfd), c_str(filename), flags.bits())) }
}
#[cfg(linux_kernel)]
pub(crate) fn fspick(
dfd: BorrowedFd<'_>,
path: &CStr,
flags: super::types::FsPickFlags,
) -> io::Result<OwnedFd> {
syscall! {
fn fspick(
dfd: c::c_int,
path: *const c::c_char,
flags: c::c_uint
) via SYS_fspick -> c::c_int
}
unsafe { ret_owned_fd(fspick(borrowed_fd(dfd), c_str(path), flags.bits())) }
}
#[cfg(linux_kernel)]
syscall! {
fn fsconfig(
fs_fd: c::c_int,
cmd: c::c_uint,
key: *const c::c_char,
val: *const c::c_char,
aux: c::c_int
) via SYS_fsconfig -> c::c_int
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: &CStr) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::SetFlag as _,
c_str(key),
null(),
0,
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_set_string(
fs_fd: BorrowedFd<'_>,
key: &CStr,
value: &CStr,
) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::SetString as _,
c_str(key),
c_str(value),
0,
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_set_binary(
fs_fd: BorrowedFd<'_>,
key: &CStr,
value: &[u8],
) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::SetBinary as _,
c_str(key),
value.as_ptr().cast(),
value.len().try_into().map_err(|_| io::Errno::OVERFLOW)?,
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_set_fd(
fs_fd: BorrowedFd<'_>,
key: &CStr,
fd: BorrowedFd<'_>,
) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::SetFd as _,
c_str(key),
null(),
borrowed_fd(fd),
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_set_path(
fs_fd: BorrowedFd<'_>,
key: &CStr,
path: &CStr,
fd: BorrowedFd<'_>,
) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::SetPath as _,
c_str(key),
c_str(path),
borrowed_fd(fd),
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_set_path_empty(
fs_fd: BorrowedFd<'_>,
key: &CStr,
fd: BorrowedFd<'_>,
) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::SetPathEmpty as _,
c_str(key),
c_str(cstr!("")),
borrowed_fd(fd),
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::Create as _,
null(),
null(),
0,
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::Reconfigure as _,
null(),
null(),
0,
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_create_excl(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::CreateExclusive as _,
null(),
null(),
0,
))
}
}

View File

@@ -0,0 +1,348 @@
use crate::backend::c;
use crate::ffi;
use bitflags::bitflags;
#[cfg(linux_kernel)]
bitflags! {
/// `MS_*` constants for use with [`mount`].
///
/// [`mount`]: crate::mount::mount
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MountFlags: ffi::c_ulong {
/// `MS_BIND`
const BIND = c::MS_BIND;
/// `MS_DIRSYNC`
const DIRSYNC = c::MS_DIRSYNC;
/// `MS_LAZYTIME`
const LAZYTIME = c::MS_LAZYTIME;
/// `MS_MANDLOCK`
#[doc(alias = "MANDLOCK")]
const PERMIT_MANDATORY_FILE_LOCKING = c::MS_MANDLOCK;
/// `MS_NOATIME`
const NOATIME = c::MS_NOATIME;
/// `MS_NODEV`
const NODEV = c::MS_NODEV;
/// `MS_NODIRATIME`
const NODIRATIME = c::MS_NODIRATIME;
/// `MS_NOEXEC`
const NOEXEC = c::MS_NOEXEC;
/// `MS_NOSUID`
const NOSUID = c::MS_NOSUID;
/// `MS_RDONLY`
const RDONLY = c::MS_RDONLY;
/// `MS_REC`
const REC = c::MS_REC;
/// `MS_RELATIME`
const RELATIME = c::MS_RELATIME;
/// `MS_SILENT`
const SILENT = c::MS_SILENT;
/// `MS_STRICTATIME`
const STRICTATIME = c::MS_STRICTATIME;
/// `MS_SYNCHRONOUS`
const SYNCHRONOUS = c::MS_SYNCHRONOUS;
/// `MS_NOSYMFOLLOW`
#[cfg(linux_raw_dep)]
const NOSYMFOLLOW = c::MS_NOSYMFOLLOW;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
/// `MNT_*` constants for use with [`unmount`].
///
/// [`unmount`]: crate::mount::unmount
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct UnmountFlags: u32 {
/// `MNT_FORCE`
const FORCE = bitcast!(c::MNT_FORCE);
/// `MNT_DETACH`
const DETACH = bitcast!(c::MNT_DETACH);
/// `MNT_EXPIRE`
const EXPIRE = bitcast!(c::MNT_EXPIRE);
/// `UMOUNT_NOFOLLOW`
const NOFOLLOW = bitcast!(c::UMOUNT_NOFOLLOW);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
/// `FSOPEN_*` constants for use with [`fsopen`].
///
/// [`fsopen`]: crate::mount::fsopen
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FsOpenFlags: ffi::c_uint {
/// `FSOPEN_CLOEXEC`
const FSOPEN_CLOEXEC = 0x0000_0001;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
/// `FSMOUNT_*` constants for use with [`fsmount`].
///
/// [`fsmount`]: crate::mount::fsmount
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FsMountFlags: ffi::c_uint {
/// `FSMOUNT_CLOEXEC`
const FSMOUNT_CLOEXEC = 0x0000_0001;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
/// `FSCONFIG_*` constants for use with the `fsconfig` syscall.
#[cfg(linux_kernel)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
pub(crate) enum FsConfigCmd {
/// `FSCONFIG_SET_FLAG`
SetFlag = 0,
/// `FSCONFIG_SET_STRING`
SetString = 1,
/// `FSCONFIG_SET_BINARY`
SetBinary = 2,
/// `FSCONFIG_SET_PATH`
SetPath = 3,
/// `FSCONFIG_SET_PATH_EMPTY`
SetPathEmpty = 4,
/// `FSCONFIG_SET_FD`
SetFd = 5,
/// `FSCONFIG_CMD_CREATE`
Create = 6,
/// `FSCONFIG_CMD_RECONFIGURE`
Reconfigure = 7,
/// `FSCONFIG_CMD_CREATE_EXCL` (since Linux 6.6)
CreateExclusive = 8,
}
#[cfg(linux_kernel)]
bitflags! {
/// `MOUNT_ATTR_*` constants for use with [`fsmount`].
///
/// [`fsmount`]: crate::mount::fsmount
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MountAttrFlags: ffi::c_uint {
/// `MOUNT_ATTR_RDONLY`
const MOUNT_ATTR_RDONLY = 0x0000_0001;
/// `MOUNT_ATTR_NOSUID`
const MOUNT_ATTR_NOSUID = 0x0000_0002;
/// `MOUNT_ATTR_NODEV`
const MOUNT_ATTR_NODEV = 0x0000_0004;
/// `MOUNT_ATTR_NOEXEC`
const MOUNT_ATTR_NOEXEC = 0x0000_0008;
/// `MOUNT_ATTR__ATIME`
const MOUNT_ATTR__ATIME = 0x0000_0070;
/// `MOUNT_ATTR_RELATIME`
const MOUNT_ATTR_RELATIME = 0x0000_0000;
/// `MOUNT_ATTR_NOATIME`
const MOUNT_ATTR_NOATIME = 0x0000_0010;
/// `MOUNT_ATTR_STRICTATIME`
const MOUNT_ATTR_STRICTATIME = 0x0000_0020;
/// `MOUNT_ATTR_NODIRATIME`
const MOUNT_ATTR_NODIRATIME = 0x0000_0080;
/// `MOUNT_ATTR_NOUSER`
const MOUNT_ATTR_IDMAP = 0x0010_0000;
/// `MOUNT_ATTR__ATIME_FLAGS`
const MOUNT_ATTR_NOSYMFOLLOW = 0x0020_0000;
/// `MOUNT_ATTR__ATIME_FLAGS`
const MOUNT_ATTR_SIZE_VER0 = 32;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
/// `MOVE_MOUNT_*` constants for use with [`move_mount`].
///
/// [`move_mount`]: crate::mount::move_mount
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MoveMountFlags: ffi::c_uint {
/// `MOVE_MOUNT_F_EMPTY_PATH`
const MOVE_MOUNT_F_SYMLINKS = 0x0000_0001;
/// `MOVE_MOUNT_F_AUTOMOUNTS`
const MOVE_MOUNT_F_AUTOMOUNTS = 0x0000_0002;
/// `MOVE_MOUNT_F_EMPTY_PATH`
const MOVE_MOUNT_F_EMPTY_PATH = 0x0000_0004;
/// `MOVE_MOUNT_T_SYMLINKS`
const MOVE_MOUNT_T_SYMLINKS = 0x0000_0010;
/// `MOVE_MOUNT_T_AUTOMOUNTS`
const MOVE_MOUNT_T_AUTOMOUNTS = 0x0000_0020;
/// `MOVE_MOUNT_T_EMPTY_PATH`
const MOVE_MOUNT_T_EMPTY_PATH = 0x0000_0040;
/// `MOVE_MOUNT__MASK`
const MOVE_MOUNT_SET_GROUP = 0x0000_0100;
/// `MOVE_MOUNT_BENEATH` (since Linux 6.5)
const MOVE_MOUNT_BENEATH = 0x0000_0200;
/// `MOVE_MOUNT__MASK`
const MOVE_MOUNT__MASK = 0x0000_0377;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
/// `OPENTREE_*` constants for use with [`open_tree`].
///
/// [`open_tree`]: crate::mount::open_tree
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct OpenTreeFlags: ffi::c_uint {
/// `OPENTREE_CLONE`
const OPEN_TREE_CLONE = 1;
/// `OPENTREE_CLOEXEC`
const OPEN_TREE_CLOEXEC = c::O_CLOEXEC as c::c_uint;
/// `AT_EMPTY_PATH`
const AT_EMPTY_PATH = c::AT_EMPTY_PATH as c::c_uint;
/// `AT_NO_AUTOMOUNT`
const AT_NO_AUTOMOUNT = c::AT_NO_AUTOMOUNT as c::c_uint;
/// `AT_RECURSIVE`
const AT_RECURSIVE = c::AT_RECURSIVE as c::c_uint;
/// `AT_SYMLINK_NOFOLLOW`
const AT_SYMLINK_NOFOLLOW = c::AT_SYMLINK_NOFOLLOW as c::c_uint;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
/// `FSPICK_*` constants for use with [`fspick`].
///
/// [`fspick`]: crate::mount::fspick
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FsPickFlags: ffi::c_uint {
/// `FSPICK_CLOEXEC`
const FSPICK_CLOEXEC = 0x0000_0001;
/// `FSPICK_SYMLINK_NOFOLLOW`
const FSPICK_SYMLINK_NOFOLLOW = 0x0000_0002;
/// `FSPICK_NO_AUTOMOUNT`
const FSPICK_NO_AUTOMOUNT = 0x0000_0004;
/// `FSPICK_EMPTY_PATH`
const FSPICK_EMPTY_PATH = 0x0000_0008;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
/// `MS_*` constants for use with [`mount_change`].
///
/// [`mount_change`]: crate::mount::mount_change
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct MountPropagationFlags: ffi::c_ulong {
/// `MS_SILENT`
const SILENT = c::MS_SILENT;
/// `MS_SHARED`
const SHARED = c::MS_SHARED;
/// `MS_PRIVATE`
const PRIVATE = c::MS_PRIVATE;
/// Mark a mount as a downstream of its current peer group.
///
/// Mount and unmount events propagate from the upstream peer group
/// into the downstream.
///
/// In Linux documentation, this flag is named `MS_SLAVE`, and the
/// concepts of “upstream” and “downstream” are called
/// “master” and “slave”.
#[doc(alias = "SLAVE")]
const DOWNSTREAM = c::MS_SLAVE;
/// `MS_UNBINDABLE`
const UNBINDABLE = c::MS_UNBINDABLE;
/// `MS_REC`
const REC = c::MS_REC;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub(crate) struct InternalMountFlags: c::c_ulong {
const REMOUNT = c::MS_REMOUNT;
const MOVE = c::MS_MOVE;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
pub(crate) struct MountFlagsArg(pub(crate) c::c_ulong);

View File

@@ -0,0 +1,390 @@
//! Socket address utilities.
use crate::backend::c;
use crate::net::AddressFamily;
#[cfg(unix)]
use {
crate::ffi::CStr,
crate::io,
crate::net::addr::SocketAddrLen,
crate::path,
core::cmp::Ordering,
core::fmt,
core::hash::{Hash, Hasher},
core::slice,
};
#[cfg(all(unix, feature = "alloc"))]
use {crate::ffi::CString, alloc::borrow::Cow, alloc::vec::Vec};
/// `struct sockaddr_un`
#[cfg(unix)]
#[derive(Clone)]
#[doc(alias = "sockaddr_un")]
pub struct SocketAddrUnix {
pub(crate) unix: c::sockaddr_un,
#[cfg(not(any(bsd, target_os = "haiku")))]
len: c::socklen_t,
}
#[cfg(unix)]
impl SocketAddrUnix {
/// Construct a new Unix-domain address from a filesystem path.
#[inline]
pub fn new<P: path::Arg>(path: P) -> io::Result<Self> {
path.into_with_c_str(Self::_new)
}
#[inline]
fn _new(path: &CStr) -> io::Result<Self> {
let mut unix = Self::init();
let mut bytes = path.to_bytes_with_nul();
if bytes.len() > unix.sun_path.len() {
bytes = path.to_bytes(); // without NUL
if bytes.len() > unix.sun_path.len() {
return Err(io::Errno::NAMETOOLONG);
}
}
for (i, b) in bytes.iter().enumerate() {
unix.sun_path[i] = *b as c::c_char;
}
#[cfg(any(bsd, target_os = "haiku"))]
{
unix.sun_len = (offsetof_sun_path() + bytes.len()).try_into().unwrap();
}
Ok(Self {
unix,
#[cfg(not(any(bsd, target_os = "haiku")))]
len: (offsetof_sun_path() + bytes.len()).try_into().unwrap(),
})
}
/// Construct a new abstract Unix-domain address from a byte slice.
#[cfg(linux_kernel)]
#[inline]
pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> {
let mut unix = Self::init();
if 1 + name.len() > unix.sun_path.len() {
return Err(io::Errno::NAMETOOLONG);
}
unix.sun_path[0] = 0;
for (i, b) in name.iter().enumerate() {
unix.sun_path[1 + i] = *b as c::c_char;
}
let len = offsetof_sun_path() + 1 + name.len();
let len = len.try_into().unwrap();
Ok(Self {
unix,
#[cfg(not(any(bsd, target_os = "haiku")))]
len,
})
}
/// Construct a new unnamed address.
///
/// The kernel will assign an abstract Unix-domain address to the socket
/// when you call [`bind`][crate::net::bind]. You can inspect the assigned
/// name with [`getsockname`][crate::net::getsockname].
///
/// # References
/// - [Linux]
///
/// [Linux]: https://www.man7.org/linux/man-pages/man7/unix.7.html
#[cfg(linux_kernel)]
#[inline]
pub fn new_unnamed() -> Self {
Self {
unix: Self::init(),
#[cfg(not(any(bsd, target_os = "haiku")))]
len: offsetof_sun_path() as c::socklen_t,
}
}
const fn init() -> c::sockaddr_un {
c::sockaddr_un {
#[cfg(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "hurd",
))]
sun_len: 0,
#[cfg(target_os = "vita")]
ss_len: 0,
sun_family: c::AF_UNIX as _,
#[cfg(any(bsd, target_os = "horizon", target_os = "nto"))]
sun_path: [0; 104],
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "horizon",
target_os = "nto"
)))]
sun_path: [0; 108],
#[cfg(target_os = "haiku")]
sun_path: [0; 126],
#[cfg(target_os = "aix")]
sun_path: [0; 1023],
}
}
/// For a filesystem path address, return the path.
#[inline]
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn path(&self) -> Option<Cow<'_, CStr>> {
let bytes = self.bytes()?;
if !bytes.is_empty() && bytes[0] != 0 {
if self.unix.sun_path.len() == bytes.len() {
// SAFETY: There are no NULs contained in bytes.
unsafe { Self::path_with_termination(bytes) }
} else {
// SAFETY: `from_bytes_with_nul_unchecked` since the string is
// NUL-terminated.
Some(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }.into())
}
} else {
None
}
}
/// If the `sun_path` field is not NUL-terminated, terminate it.
///
/// SAFETY: The input `bytes` must not contain any NULs.
#[cfg(feature = "alloc")]
#[cold]
unsafe fn path_with_termination(bytes: &[u8]) -> Option<Cow<'_, CStr>> {
let mut owned = Vec::with_capacity(bytes.len() + 1);
owned.extend_from_slice(bytes);
owned.push(b'\0');
// SAFETY: `from_vec_with_nul_unchecked` since the string is
// NUL-terminated and `bytes` does not contain any NULs.
Some(Cow::Owned(
CString::from_vec_with_nul_unchecked(owned).into(),
))
}
/// For a filesystem path address, return the path as a byte sequence,
/// excluding the NUL terminator.
#[inline]
pub fn path_bytes(&self) -> Option<&[u8]> {
let bytes = self.bytes()?;
if !bytes.is_empty() && bytes[0] != 0 {
if self.unix.sun_path.len() == self.len() - offsetof_sun_path() {
// There is no NUL terminator.
Some(bytes)
} else {
// Remove the NUL terminator.
Some(&bytes[..bytes.len() - 1])
}
} else {
None
}
}
/// For an abstract address, return the identifier.
#[cfg(linux_kernel)]
#[inline]
pub fn abstract_name(&self) -> Option<&[u8]> {
if let [0, bytes @ ..] = self.bytes()? {
Some(bytes)
} else {
None
}
}
/// `true` if the socket address is unnamed.
#[cfg(linux_kernel)]
#[inline]
pub fn is_unnamed(&self) -> bool {
self.bytes() == Some(&[])
}
#[inline]
pub(crate) fn addr_len(&self) -> SocketAddrLen {
#[cfg(not(any(bsd, target_os = "haiku")))]
{
bitcast!(self.len)
}
#[cfg(any(bsd, target_os = "haiku"))]
{
bitcast!(c::socklen_t::from(self.unix.sun_len))
}
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.addr_len() as usize
}
#[inline]
fn bytes(&self) -> Option<&[u8]> {
let len = self.len();
if len != 0 {
let bytes = &self.unix.sun_path[..len - offsetof_sun_path()];
// SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
Some(unsafe { slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len()) })
} else {
None
}
}
}
#[cfg(unix)]
impl PartialEq for SocketAddrUnix {
#[inline]
fn eq(&self, other: &Self) -> bool {
let self_len = self.len() - offsetof_sun_path();
let other_len = other.len() - offsetof_sun_path();
self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len])
}
}
#[cfg(unix)]
impl Eq for SocketAddrUnix {}
#[cfg(unix)]
impl PartialOrd for SocketAddrUnix {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(unix)]
impl Ord for SocketAddrUnix {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
let self_len = self.len() - offsetof_sun_path();
let other_len = other.len() - offsetof_sun_path();
self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len])
}
}
#[cfg(unix)]
impl Hash for SocketAddrUnix {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
let self_len = self.len() - offsetof_sun_path();
self.unix.sun_path[..self_len].hash(state)
}
}
#[cfg(unix)]
impl fmt::Debug for SocketAddrUnix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "alloc")]
if let Some(path) = self.path() {
return path.fmt(f);
}
if let Some(bytes) = self.path_bytes() {
if let Ok(s) = core::str::from_utf8(bytes) {
return s.fmt(f);
}
return bytes.fmt(f);
}
#[cfg(linux_kernel)]
if let Some(name) = self.abstract_name() {
return name.fmt(f);
}
"(unnamed)".fmt(f)
}
}
/// `struct sockaddr_storage`
///
/// This type is guaranteed to be large enough to hold any encoded socket
/// address.
#[repr(transparent)]
#[derive(Copy, Clone)]
#[doc(alias = "sockaddr_storage")]
pub struct SocketAddrStorage(c::sockaddr_storage);
impl SocketAddrStorage {
/// Return a socket addr storage initialized to all zero bytes. The
/// `sa_family` is set to [`AddressFamily::UNSPEC`].
pub fn zeroed() -> Self {
assert_eq!(c::AF_UNSPEC, 0);
// SAFETY: `sockaddr_storage` is meant to be zero-initializable.
unsafe { core::mem::zeroed() }
}
/// Return the `sa_family` of this socket address.
pub fn family(&self) -> AddressFamily {
// SAFETY: `self.0` is a `sockaddr_storage` so it has enough space.
unsafe {
AddressFamily::from_raw(crate::backend::net::read_sockaddr::read_sa_family(
crate::utils::as_ptr(&self.0).cast::<c::sockaddr>(),
))
}
}
/// Clear the `sa_family` of this socket address to
/// [`AddressFamily::UNSPEC`].
pub fn clear_family(&mut self) {
// SAFETY: `self.0` is a `sockaddr_storage` so it has enough space.
unsafe {
crate::backend::net::read_sockaddr::initialize_family_to_unspec(
crate::utils::as_mut_ptr(&mut self.0).cast::<c::sockaddr>(),
)
}
}
}
/// Return the offset of the `sun_path` field of `sockaddr_un`.
#[cfg(not(windows))]
#[inline]
pub(crate) fn offsetof_sun_path() -> usize {
let z = c::sockaddr_un {
#[cfg(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "horizon",
target_os = "hurd",
target_os = "nto",
))]
sun_len: 0_u8,
#[cfg(target_os = "vita")]
ss_len: 0,
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita"
))]
sun_family: 0_u8,
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita"
)))]
sun_family: 0_u16,
#[cfg(any(bsd, target_os = "horizon", target_os = "nto"))]
sun_path: [0; 104],
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "horizon",
target_os = "nto"
)))]
sun_path: [0; 108],
#[cfg(target_os = "haiku")]
sun_path: [0; 126],
#[cfg(target_os = "aix")]
sun_path: [0; 1023],
};
(crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize)
}

View File

@@ -0,0 +1,137 @@
use crate::backend::c;
/// The windows `sockaddr_in6` type is a union with accessor functions which
/// are not `const fn`. Define our own layout-compatible version so that we
/// can transmute in and out of it.
#[cfg(windows)]
#[repr(C)]
struct sockaddr_in6 {
sin6_family: u16,
sin6_port: u16,
sin6_flowinfo: u32,
sin6_addr: c::in6_addr,
sin6_scope_id: u32,
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn in_addr_s_addr(addr: c::in_addr) -> u32 {
addr.s_addr
}
#[cfg(windows)]
#[inline]
pub(crate) const fn in_addr_s_addr(addr: c::in_addr) -> u32 {
// This should be `*addr.S_un.S_addr()`, except that isn't a `const fn`.
unsafe { core::mem::transmute(addr) }
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn in_addr_new(s_addr: u32) -> c::in_addr {
c::in_addr { s_addr }
}
#[cfg(windows)]
#[inline]
pub(crate) const fn in_addr_new(s_addr: u32) -> c::in_addr {
unsafe { core::mem::transmute(s_addr) }
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn in6_addr_s6_addr(addr: c::in6_addr) -> [u8; 16] {
addr.s6_addr
}
#[cfg(windows)]
#[inline]
pub(crate) const fn in6_addr_s6_addr(addr: c::in6_addr) -> [u8; 16] {
unsafe { core::mem::transmute(addr) }
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr {
c::in6_addr { s6_addr }
}
#[cfg(windows)]
#[inline]
pub(crate) const fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr {
unsafe { core::mem::transmute(s6_addr) }
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: &c::sockaddr_in6) -> u32 {
addr.sin6_scope_id
}
#[cfg(windows)]
#[inline]
pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: &c::sockaddr_in6) -> u32 {
let addr: &sockaddr_in6 = unsafe { core::mem::transmute(addr) };
addr.sin6_scope_id
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn sockaddr_in6_new(
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita"
))]
sin6_len: u8,
sin6_family: c::sa_family_t,
sin6_port: u16,
sin6_flowinfo: u32,
sin6_addr: c::in6_addr,
sin6_scope_id: u32,
) -> c::sockaddr_in6 {
c::sockaddr_in6 {
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita"
))]
sin6_len,
sin6_family,
sin6_port,
sin6_flowinfo,
sin6_addr,
sin6_scope_id,
#[cfg(solarish)]
__sin6_src_id: 0,
#[cfg(target_os = "vita")]
sin6_vport: 0,
}
}
#[cfg(windows)]
#[inline]
pub(crate) const fn sockaddr_in6_new(
sin6_family: u16,
sin6_port: u16,
sin6_flowinfo: u32,
sin6_addr: c::in6_addr,
sin6_scope_id: u32,
) -> c::sockaddr_in6 {
let addr = sockaddr_in6 {
sin6_family,
sin6_port,
sin6_flowinfo,
sin6_addr,
sin6_scope_id,
};
unsafe { core::mem::transmute(addr) }
}

View File

@@ -0,0 +1,17 @@
pub(crate) mod addr;
pub(crate) mod ext;
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita"
)))]
pub(crate) mod msghdr;
#[cfg(linux_kernel)]
pub(crate) mod netdevice;
pub(crate) mod read_sockaddr;
pub(crate) mod send_recv;
pub(crate) mod sockopt;
pub(crate) mod syscalls;
pub(crate) mod write_sockaddr;

View File

@@ -0,0 +1,188 @@
//! Utilities for dealing with message headers.
//!
//! These take closures rather than returning a `c::msghdr` directly because
//! the message headers may reference stack-local data.
use crate::backend::c;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::net::addr::SocketAddrArg;
use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrBuf};
use core::mem::zeroed;
/// Convert the value to the `msg_iovlen` field of a `msghdr` struct.
#[cfg(all(
not(any(windows, target_os = "espidf", target_os = "wasi")),
any(
target_os = "android",
all(
target_os = "linux",
not(target_env = "musl"),
not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips")))
)
)
))]
#[inline]
fn msg_iov_len(len: usize) -> c::size_t {
len
}
/// Convert the value to the `msg_iovlen` field of a `msghdr` struct.
#[cfg(all(
not(any(
windows,
target_os = "espidf",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)),
not(any(
target_os = "android",
all(
target_os = "linux",
not(target_env = "musl"),
not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips")))
)
))
))]
#[inline]
fn msg_iov_len(len: usize) -> c::c_int {
len.try_into().unwrap_or(c::c_int::MAX)
}
/// Convert the value to a `socklen_t`.
#[cfg(any(
bsd,
solarish,
target_env = "musl",
target_os = "aix",
target_os = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
))]
#[inline]
fn msg_control_len(len: usize) -> c::socklen_t {
len.try_into().unwrap_or(c::socklen_t::MAX)
}
/// Convert the value to a `size_t`.
#[cfg(not(any(
bsd,
solarish,
windows,
target_env = "musl",
target_os = "aix",
target_os = "cygwin",
target_os = "emscripten",
target_os = "espidf",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
fn msg_control_len(len: usize) -> c::size_t {
len
}
/// Create a message header intended to receive a datagram.
///
/// # Safety
///
/// If `f` dereferences the pointers in the `msghdr`, it must do so only within
/// the bounds indicated by the associated lengths in the `msghdr`.
///
/// And, if `f` returns `Ok`, it must have updated the `msg_controllen` field
/// of the `msghdr` to indicate how many bytes it initialized.
pub(crate) unsafe fn with_recv_msghdr<R>(
name: &mut SocketAddrBuf,
iov: &mut [IoSliceMut<'_>],
control: &mut RecvAncillaryBuffer<'_>,
f: impl FnOnce(&mut c::msghdr) -> io::Result<R>,
) -> io::Result<R> {
control.clear();
let mut msghdr = zero_msghdr();
msghdr.msg_name = name.storage.as_mut_ptr().cast();
msghdr.msg_namelen = name.len;
msghdr.msg_iov = iov.as_mut_ptr().cast();
msghdr.msg_iovlen = msg_iov_len(iov.len());
msghdr.msg_control = control.as_control_ptr().cast();
msghdr.msg_controllen = msg_control_len(control.control_len());
let res = f(&mut msghdr);
// Reset the control length.
if res.is_ok() {
// SAFETY: `f` returned `Ok`, so our safety condition requires `f` to
// have initialized `msg_controllen` bytes.
control.set_control_len(msghdr.msg_controllen as usize);
}
name.len = msghdr.msg_namelen;
res
}
/// Create a message header intended to send without an address.
///
/// The returned `msghdr` will contain raw pointers to the memory
/// referenced by `iov` and `control`.
pub(crate) fn noaddr_msghdr(
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
) -> c::msghdr {
let mut h = zero_msghdr();
h.msg_iov = iov.as_ptr() as _;
h.msg_iovlen = msg_iov_len(iov.len());
h.msg_control = control.as_control_ptr().cast();
h.msg_controllen = msg_control_len(control.control_len());
h
}
/// Create a message header intended to send with the specified address.
///
/// This creates a `c::msghdr` and calls a function `f` on it. The `msghdr`'s
/// raw pointers may point to temporaries, so this function should avoid
/// storing the pointers anywhere that would outlive the function call.
///
/// # Safety
///
/// If `f` dereferences the pointers in the `msghdr`, it must do so only within
/// the bounds indicated by the associated lengths in the `msghdr`.
pub(crate) unsafe fn with_msghdr<R>(
addr: &impl SocketAddrArg,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(&c::msghdr) -> R,
) -> R {
addr.with_sockaddr(|addr_ptr, addr_len| {
let mut h = zero_msghdr();
h.msg_name = addr_ptr as *mut _;
h.msg_namelen = bitcast!(addr_len);
h.msg_iov = iov.as_ptr() as _;
h.msg_iovlen = msg_iov_len(iov.len());
h.msg_control = control.as_control_ptr().cast();
h.msg_controllen = msg_control_len(control.control_len());
// Pass a reference to the `c::msghdr` instead of passing it by value
// because it may contain pointers to temporary objects that won't
// live beyond the call to `with_sockaddr`.
f(&h)
})
}
/// Create a zero-initialized message header struct value.
#[cfg(all(unix, not(target_os = "redox")))]
pub(crate) fn zero_msghdr() -> c::msghdr {
// SAFETY: We can't initialize all the fields by value because on some
// platforms the `msghdr` struct in the libc crate contains private padding
// fields. But it is still a C type that's meant to be zero-initializable.
unsafe { zeroed() }
}

View File

@@ -0,0 +1,50 @@
//! Wrappers for netdevice ioctls.
#![allow(unsafe_code)]
use crate::backend::c;
use crate::backend::io::syscalls::ioctl;
use crate::fd::BorrowedFd;
use crate::io;
use c::{__c_anonymous_ifr_ifru, c_char, ifreq, IFNAMSIZ, SIOCGIFINDEX, SIOCGIFNAME};
pub(crate) fn name_to_index(fd: BorrowedFd<'_>, if_name: &str) -> io::Result<u32> {
let if_name_bytes = if_name.as_bytes();
if if_name_bytes.len() >= IFNAMSIZ as usize {
return Err(io::Errno::NODEV);
}
let mut ifreq = ifreq {
ifr_name: [0; 16],
ifr_ifru: __c_anonymous_ifr_ifru { ifru_ifindex: 0 },
};
let mut if_name_c_char_iter = if_name_bytes.iter().map(|byte| *byte as c_char);
ifreq.ifr_name[..if_name_bytes.len()].fill_with(|| if_name_c_char_iter.next().unwrap());
unsafe { ioctl(fd, SIOCGIFINDEX as _, &mut ifreq as *mut ifreq as _) }?;
let index = unsafe { ifreq.ifr_ifru.ifru_ifindex };
Ok(index as u32)
}
pub(crate) fn index_to_name(fd: BorrowedFd<'_>, index: u32) -> io::Result<(usize, [u8; 16])> {
let mut ifreq = ifreq {
ifr_name: [0; 16],
ifr_ifru: __c_anonymous_ifr_ifru {
ifru_ifindex: index as _,
},
};
unsafe { ioctl(fd, SIOCGIFNAME as _, &mut ifreq as *mut ifreq as _) }?;
if let Some(nul_byte) = ifreq.ifr_name.iter().position(|char| *char == 0) {
let mut buf = [0u8; 16];
ifreq.ifr_name.iter().enumerate().for_each(|(idx, c)| {
buf[idx] = *c as u8;
});
Ok((nul_byte, buf))
} else {
Err(io::Errno::INVAL)
}
}

View File

@@ -0,0 +1,264 @@
//! The BSD sockets API requires us to read the `sa_family` field before we can
//! interpret the rest of a `sockaddr` produced by the kernel.
#[cfg(unix)]
use super::addr::SocketAddrUnix;
use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id};
use crate::backend::c;
#[cfg(not(windows))]
use crate::ffi::CStr;
use crate::io::Errno;
use crate::net::addr::SocketAddrLen;
#[cfg(linux_kernel)]
use crate::net::netlink::SocketAddrNetlink;
#[cfg(target_os = "linux")]
use crate::net::xdp::{SocketAddrXdp, SocketAddrXdpFlags};
use crate::net::{AddressFamily, Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
use core::mem::size_of;
// This must match the header of `sockaddr`.
#[repr(C)]
pub(crate) struct sockaddr_header {
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
))]
sa_len: u8,
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
))]
sa_family: u8,
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
)))]
sa_family: u16,
}
/// Read the `sa_family` field from a socket address returned from the OS.
///
/// # Safety
///
/// `storage` must point to a valid socket address returned from the OS.
#[inline]
pub(crate) unsafe fn read_sa_family(storage: *const c::sockaddr) -> u16 {
// Assert that we know the layout of `sockaddr`.
let _ = c::sockaddr {
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita"
))]
sa_len: 0_u8,
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita"
))]
sa_family: 0_u8,
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita"
)))]
sa_family: 0_u16,
#[cfg(not(any(target_os = "haiku", target_os = "horizon")))]
sa_data: [0; 14],
#[cfg(target_os = "horizon")]
sa_data: [0; 26],
#[cfg(target_os = "haiku")]
sa_data: [0; 30],
};
(*storage.cast::<sockaddr_header>()).sa_family.into()
}
/// Read the first byte of the `sun_path` field, assuming we have an `AF_UNIX`
/// socket address.
#[cfg(apple)]
#[inline]
unsafe fn read_sun_path0(storage: *const c::sockaddr) -> u8 {
// In `read_sa_family` we assert that we know the layout of `sockaddr`.
storage
.cast::<u8>()
.add(super::addr::offsetof_sun_path())
.read()
}
/// Check if a socket address returned from the OS is considered non-empty.
///
/// # Safety
///
/// `storage` must point to a least an initialized `sockaddr_header`.
#[inline]
pub(crate) unsafe fn sockaddr_nonempty(storage: *const c::sockaddr, len: SocketAddrLen) -> bool {
if len == 0 {
return false;
}
assert!(len as usize >= size_of::<c::sa_family_t>());
let family: c::c_int = read_sa_family(storage.cast::<c::sockaddr>()).into();
if family == c::AF_UNSPEC {
return false;
}
// On macOS, if we get an `AF_UNIX` with an empty path, treat it as an
// absent address.
#[cfg(apple)]
if family == c::AF_UNIX && read_sun_path0(storage) == 0 {
return false;
}
true
}
/// Set the `sa_family` field of a socket address to `AF_UNSPEC`, so that we
/// can test for `AF_UNSPEC` to test whether it was stored to.
///
/// # Safety
///
/// `storage` must point to a least an initialized `sockaddr_header`.
pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr) {
(*storage.cast::<sockaddr_header>()).sa_family = c::AF_UNSPEC as _;
}
#[inline]
pub(crate) fn read_sockaddr_v4(addr: &SocketAddrAny) -> Result<SocketAddrV4, Errno> {
if addr.address_family() != AddressFamily::INET {
return Err(Errno::AFNOSUPPORT);
}
assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in>());
let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in>() };
Ok(SocketAddrV4::new(
Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
u16::from_be(decode.sin_port),
))
}
#[inline]
pub(crate) fn read_sockaddr_v6(addr: &SocketAddrAny) -> Result<SocketAddrV6, Errno> {
if addr.address_family() != AddressFamily::INET6 {
return Err(Errno::AFNOSUPPORT);
}
assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in6>());
let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in6>() };
Ok(SocketAddrV6::new(
Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)),
u16::from_be(decode.sin6_port),
u32::from_be(decode.sin6_flowinfo),
sockaddr_in6_sin6_scope_id(decode),
))
}
#[cfg(unix)]
#[inline]
pub(crate) fn read_sockaddr_unix(addr: &SocketAddrAny) -> Result<SocketAddrUnix, Errno> {
if addr.address_family() != AddressFamily::UNIX {
return Err(Errno::AFNOSUPPORT);
}
let offsetof_sun_path = super::addr::offsetof_sun_path();
let len = addr.addr_len() as usize;
assert!(len >= offsetof_sun_path);
if len == offsetof_sun_path {
SocketAddrUnix::new(&[][..])
} else {
let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_un>() };
// On Linux check for Linux's [abstract namespace].
//
// [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
#[cfg(linux_kernel)]
if decode.sun_path[0] == 0 {
let name = &decode.sun_path[1..len - offsetof_sun_path];
let name = unsafe { core::mem::transmute::<&[c::c_char], &[u8]>(name) };
return SocketAddrUnix::new_abstract_name(name);
}
// Otherwise we expect a NUL-terminated filesystem path.
// Trim off unused bytes from the end of `path_bytes`.
let path_bytes = if cfg!(any(solarish, target_os = "freebsd")) {
// FreeBSD and illumos sometimes set the length to longer
// than the length of the NUL-terminated string. Find the
// NUL and truncate the string accordingly.
&decode.sun_path[..decode
.sun_path
.iter()
.position(|b| *b == 0)
.ok_or(Errno::INVAL)?]
} else {
// Otherwise, use the provided length.
let provided_len = len - 1 - offsetof_sun_path;
if decode.sun_path[provided_len] != 0 {
return Err(Errno::INVAL);
}
debug_assert_eq!(
unsafe { CStr::from_ptr(decode.sun_path.as_ptr().cast()) }
.to_bytes()
.len(),
provided_len
);
&decode.sun_path[..provided_len]
};
SocketAddrUnix::new(unsafe { core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes) })
}
}
#[cfg(target_os = "linux")]
#[inline]
pub(crate) fn read_sockaddr_xdp(addr: &SocketAddrAny) -> Result<SocketAddrXdp, Errno> {
if addr.address_family() != AddressFamily::XDP {
return Err(Errno::AFNOSUPPORT);
}
assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_xdp>());
let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_xdp>() };
// This ignores the `sxdp_shared_umem_fd` field, which is only expected to
// be significant in `bind` calls, and not returned from `acceptfrom` or
// `recvmsg` or similar.
Ok(SocketAddrXdp::new(
SocketAddrXdpFlags::from_bits_retain(decode.sxdp_flags),
u32::from_be(decode.sxdp_ifindex),
u32::from_be(decode.sxdp_queue_id),
))
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn read_sockaddr_netlink(addr: &SocketAddrAny) -> Result<SocketAddrNetlink, Errno> {
if addr.address_family() != AddressFamily::NETLINK {
return Err(Errno::AFNOSUPPORT);
}
assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_nl>());
let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_nl>() };
Ok(SocketAddrNetlink::new(decode.nl_pid, decode.nl_groups))
}

View File

@@ -0,0 +1,153 @@
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `MSG_*` flags for use with [`send`], [`sendto`], and related
/// functions.
///
/// [`send`]: crate::net::send
/// [`sendto`]: crate::net::sendto
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct SendFlags: u32 {
/// `MSG_CONFIRM`
#[cfg(not(any(
bsd,
solarish,
windows,
target_os = "aix",
target_os = "cygwin",
target_os = "espidf",
target_os = "nto",
target_os = "haiku",
target_os = "horizon",
target_os = "hurd",
target_os = "redox",
target_os = "vita",
)))]
const CONFIRM = bitcast!(c::MSG_CONFIRM);
/// `MSG_DONTROUTE`
const DONTROUTE = bitcast!(c::MSG_DONTROUTE);
/// `MSG_DONTWAIT`
#[cfg(not(windows))]
const DONTWAIT = bitcast!(c::MSG_DONTWAIT);
/// `MSG_EOR`
#[cfg(not(any(windows, target_os = "horizon")))]
const EOR = bitcast!(c::MSG_EOR);
/// `MSG_MORE`
#[cfg(not(any(
bsd,
solarish,
windows,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
target_os = "vita",
)))]
const MORE = bitcast!(c::MSG_MORE);
#[cfg(not(any(apple, windows, target_os = "redox", target_os = "vita")))]
/// `MSG_NOSIGNAL`
const NOSIGNAL = bitcast!(c::MSG_NOSIGNAL);
/// `MSG_OOB`
const OOB = bitcast!(c::MSG_OOB);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `MSG_*` flags for use with [`recv`], [`recvfrom`], and related
/// functions.
///
/// [`recv`]: crate::net::recv
/// [`recvfrom`]: crate::net::recvfrom
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct RecvFlags: u32 {
/// `MSG_CMSG_CLOEXEC`
#[cfg(not(any(
apple,
solarish,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
)))]
const CMSG_CLOEXEC = bitcast!(c::MSG_CMSG_CLOEXEC);
/// `MSG_DONTWAIT`
#[cfg(not(windows))]
const DONTWAIT = bitcast!(c::MSG_DONTWAIT);
/// `MSG_ERRQUEUE`
#[cfg(not(any(
bsd,
solarish,
windows,
target_os = "aix",
target_os = "cygwin",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
target_os = "vita",
)))]
const ERRQUEUE = bitcast!(c::MSG_ERRQUEUE);
/// `MSG_OOB`
const OOB = bitcast!(c::MSG_OOB);
/// `MSG_PEEK`
const PEEK = bitcast!(c::MSG_PEEK);
/// `MSG_TRUNC`
// Apple, illumos, and NetBSD have `MSG_TRUNC` but it's not documented
// for use with `recv` and friends, and in practice appears to be
// ignored.
#[cfg(not(any(apple, solarish, target_os = "horizon", target_os = "netbsd")))]
const TRUNC = bitcast!(c::MSG_TRUNC);
/// `MSG_WAITALL`
const WAITALL = bitcast!(c::MSG_WAITALL);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `MSG_*` flags returned from [`recvmsg`], in the `flags` field of
/// [`RecvMsg`]
///
/// [`recvmsg`]: crate::net::recvmsg
/// [`RecvMsg`]: crate::net::RecvMsg
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ReturnFlags: u32 {
/// `MSG_OOB`
const OOB = bitcast!(c::MSG_OOB);
/// `MSG_EOR`
#[cfg(not(any(windows, target_os = "horizon")))]
const EOR = bitcast!(c::MSG_EOR);
/// `MSG_TRUNC`
#[cfg(not(target_os = "horizon"))]
const TRUNC = bitcast!(c::MSG_TRUNC);
/// `MSG_CTRUNC`
#[cfg(not(target_os = "horizon"))]
const CTRUNC = bitcast!(c::MSG_CTRUNC);
/// `MSG_CMSG_CLOEXEC`
#[cfg(linux_kernel)]
const CMSG_CLOEXEC = bitcast!(c::MSG_CMSG_CLOEXEC);
/// `MSG_ERRQUEUE`
#[cfg(linux_kernel)]
const ERRQUEUE = bitcast!(c::MSG_ERRQUEUE);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,438 @@
//! libc syscalls supporting `rustix::net`.
use super::read_sockaddr::initialize_family_to_unspec;
use super::send_recv::{RecvFlags, SendFlags};
use crate::backend::c;
#[cfg(target_os = "linux")]
use crate::backend::conv::ret_u32;
use crate::backend::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len};
use crate::fd::{BorrowedFd, OwnedFd};
use crate::io;
use crate::net::addr::SocketAddrArg;
#[cfg(target_os = "linux")]
use crate::net::MMsgHdr;
use crate::net::{
AddressFamily, Protocol, Shutdown, SocketAddrAny, SocketAddrBuf, SocketFlags, SocketType,
};
use crate::utils::as_ptr;
use core::mem::{size_of, MaybeUninit};
use core::ptr::null_mut;
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita"
)))]
use {
super::msghdr::{noaddr_msghdr, with_msghdr, with_recv_msghdr},
super::send_recv::ReturnFlags,
crate::io::{IoSlice, IoSliceMut},
crate::net::{RecvAncillaryBuffer, RecvMsg, SendAncillaryBuffer},
};
pub(crate) unsafe fn recv(
fd: BorrowedFd<'_>,
buf: (*mut u8, usize),
flags: RecvFlags,
) -> io::Result<usize> {
ret_send_recv(c::recv(
borrowed_fd(fd),
buf.0.cast(),
send_recv_len(buf.1),
bitflags_bits!(flags),
))
}
pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result<usize> {
unsafe {
ret_send_recv(c::send(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
bitflags_bits!(flags),
))
}
}
pub(crate) unsafe fn recvfrom(
fd: BorrowedFd<'_>,
buf: (*mut u8, usize),
flags: RecvFlags,
) -> io::Result<(usize, Option<SocketAddrAny>)> {
let mut addr = SocketAddrBuf::new();
// `recvfrom` does not write to the storage if the socket is
// connection-oriented sockets, so we initialize the family field to
// `AF_UNSPEC` so that we can detect this case.
initialize_family_to_unspec(addr.storage.as_mut_ptr().cast::<c::sockaddr>());
let nread = ret_send_recv(c::recvfrom(
borrowed_fd(fd),
buf.0.cast(),
send_recv_len(buf.1),
bitflags_bits!(flags),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
))?;
Ok((nread, addr.into_any_option()))
}
pub(crate) fn sendto(
fd: BorrowedFd<'_>,
buf: &[u8],
flags: SendFlags,
addr: &impl SocketAddrArg,
) -> io::Result<usize> {
unsafe {
addr.with_sockaddr(|addr_ptr, addr_len| {
ret_send_recv(c::sendto(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
bitflags_bits!(flags),
addr_ptr.cast(),
bitcast!(addr_len),
))
})
}
}
pub(crate) fn socket(
domain: AddressFamily,
type_: SocketType,
protocol: Option<Protocol>,
) -> io::Result<OwnedFd> {
let raw_protocol = match protocol {
Some(p) => p.0.get(),
None => 0,
};
unsafe {
ret_owned_fd(c::socket(
domain.0 as c::c_int,
type_.0 as c::c_int,
raw_protocol as c::c_int,
))
}
}
pub(crate) fn socket_with(
domain: AddressFamily,
type_: SocketType,
flags: SocketFlags,
protocol: Option<Protocol>,
) -> io::Result<OwnedFd> {
let raw_protocol = match protocol {
Some(p) => p.0.get(),
None => 0,
};
unsafe {
ret_owned_fd(c::socket(
domain.0 as c::c_int,
(type_.0 | flags.bits()) as c::c_int,
raw_protocol as c::c_int,
))
}
}
pub(crate) fn bind(sockfd: BorrowedFd<'_>, addr: &impl SocketAddrArg) -> io::Result<()> {
unsafe {
addr.with_sockaddr(|addr_ptr, addr_len| {
ret(c::bind(
borrowed_fd(sockfd),
addr_ptr.cast(),
bitcast!(addr_len),
))
})
}
}
pub(crate) fn connect(sockfd: BorrowedFd<'_>, addr: &impl SocketAddrArg) -> io::Result<()> {
unsafe {
addr.with_sockaddr(|addr_ptr, addr_len| {
ret(c::connect(
borrowed_fd(sockfd),
addr_ptr.cast(),
bitcast!(addr_len),
))
})
}
}
pub(crate) fn connect_unspec(sockfd: BorrowedFd<'_>) -> io::Result<()> {
debug_assert_eq!(c::AF_UNSPEC, 0);
let addr = MaybeUninit::<c::sockaddr_storage>::zeroed();
unsafe {
ret(c::connect(
borrowed_fd(sockfd),
as_ptr(&addr).cast(),
size_of::<c::sockaddr_storage>() as c::socklen_t,
))
}
}
pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> {
unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) }
}
pub(crate) fn accept(sockfd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
unsafe {
let owned_fd = ret_owned_fd(c::accept(borrowed_fd(sockfd), null_mut(), null_mut()))?;
Ok(owned_fd)
}
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita"
)))]
pub(crate) fn recvmsg(
sockfd: BorrowedFd<'_>,
iov: &mut [IoSliceMut<'_>],
control: &mut RecvAncillaryBuffer<'_>,
msg_flags: RecvFlags,
) -> io::Result<RecvMsg> {
let mut addr = SocketAddrBuf::new();
// SAFETY: This passes the `msghdr` reference to the OS which reads the
// buffers only within the designated bounds.
let (bytes, flags) = unsafe {
with_recv_msghdr(&mut addr, iov, control, |msghdr| {
let bytes = ret_send_recv(c::recvmsg(
borrowed_fd(sockfd),
msghdr,
bitflags_bits!(msg_flags),
))?;
Ok((bytes, msghdr.msg_flags))
})?
};
Ok(RecvMsg {
bytes,
address: unsafe { addr.into_any_option() },
flags: ReturnFlags::from_bits_retain(bitcast!(flags)),
})
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita"
)))]
pub(crate) fn sendmsg(
sockfd: BorrowedFd<'_>,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
let msghdr = noaddr_msghdr(iov, control);
unsafe {
ret_send_recv(c::sendmsg(
borrowed_fd(sockfd),
&msghdr,
bitflags_bits!(msg_flags),
))
}
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita"
)))]
pub(crate) fn sendmsg_addr(
sockfd: BorrowedFd<'_>,
addr: &impl SocketAddrArg,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
// SAFETY: This passes the `msghdr` reference to the OS which reads the
// buffers only within the designated bounds.
unsafe {
with_msghdr(addr, iov, control, |msghdr| {
ret_send_recv(c::sendmsg(
borrowed_fd(sockfd),
msghdr,
bitflags_bits!(msg_flags),
))
})
}
}
#[cfg(target_os = "linux")]
pub(crate) fn sendmmsg(
sockfd: BorrowedFd<'_>,
msgs: &mut [MMsgHdr<'_>],
flags: SendFlags,
) -> io::Result<usize> {
unsafe {
ret_u32(c::sendmmsg(
borrowed_fd(sockfd),
msgs.as_mut_ptr() as _,
msgs.len().try_into().unwrap_or(c::c_uint::MAX),
bitflags_bits!(flags),
))
.map(|ret| ret as usize)
}
}
#[cfg(not(any(
apple,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
)))]
pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, flags: SocketFlags) -> io::Result<OwnedFd> {
unsafe {
let owned_fd = ret_owned_fd(c::accept4(
borrowed_fd(sockfd),
null_mut(),
null_mut(),
flags.bits() as c::c_int,
))?;
Ok(owned_fd)
}
}
pub(crate) fn acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
unsafe {
let mut addr = SocketAddrBuf::new();
let owned_fd = ret_owned_fd(c::accept(
borrowed_fd(sockfd),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
))?;
Ok((owned_fd, addr.into_any_option()))
}
}
#[cfg(not(any(
apple,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
)))]
pub(crate) fn acceptfrom_with(
sockfd: BorrowedFd<'_>,
flags: SocketFlags,
) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
unsafe {
let mut addr = SocketAddrBuf::new();
let owned_fd = ret_owned_fd(c::accept4(
borrowed_fd(sockfd),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
flags.bits() as c::c_int,
))?;
Ok((owned_fd, addr.into_any_option()))
}
}
/// Darwin lacks `accept4`, but does have `accept`. We define `SocketFlags` to
/// have no flags, so we can discard it here.
#[cfg(any(
apple,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
))]
pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, _flags: SocketFlags) -> io::Result<OwnedFd> {
accept(sockfd)
}
/// Darwin lacks `accept4`, but does have `accept`. We define `SocketFlags` to
/// have no flags, so we can discard it here.
#[cfg(any(
apple,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
))]
pub(crate) fn acceptfrom_with(
sockfd: BorrowedFd<'_>,
_flags: SocketFlags,
) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
acceptfrom(sockfd)
}
pub(crate) fn shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> {
unsafe { ret(c::shutdown(borrowed_fd(sockfd), how as c::c_int)) }
}
pub(crate) fn getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny> {
unsafe {
let mut addr = SocketAddrBuf::new();
ret(c::getsockname(
borrowed_fd(sockfd),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
))?;
Ok(addr.into_any())
}
}
pub(crate) fn getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>> {
unsafe {
let mut addr = SocketAddrBuf::new();
ret(c::getpeername(
borrowed_fd(sockfd),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
))?;
Ok(addr.into_any_option())
}
}
#[cfg(not(windows))]
pub(crate) fn socketpair(
domain: AddressFamily,
type_: SocketType,
flags: SocketFlags,
protocol: Option<Protocol>,
) -> io::Result<(OwnedFd, OwnedFd)> {
let raw_protocol = match protocol {
Some(p) => p.0.get(),
None => 0,
};
unsafe {
let mut fds = MaybeUninit::<[OwnedFd; 2]>::uninit();
ret(c::socketpair(
c::c_int::from(domain.0),
(type_.0 | flags.bits()) as c::c_int,
raw_protocol as c::c_int,
fds.as_mut_ptr().cast::<c::c_int>(),
))?;
let [fd0, fd1] = fds.assume_init();
Ok((fd0, fd1))
}
}

View File

@@ -0,0 +1,72 @@
//! The BSD sockets API requires us to read the `sa_family` field before we can
//! interpret the rest of a `sockaddr` produced by the kernel.
use super::ext::{in6_addr_new, in_addr_new, sockaddr_in6_new};
use crate::backend::c;
use crate::net::{SocketAddrV4, SocketAddrV6};
pub(crate) fn encode_sockaddr_v4(v4: &SocketAddrV4) -> c::sockaddr_in {
c::sockaddr_in {
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita",
))]
sin_len: core::mem::size_of::<c::sockaddr_in>() as _,
sin_family: c::AF_INET as _,
sin_port: u16::to_be(v4.port()),
sin_addr: in_addr_new(u32::from_ne_bytes(v4.ip().octets())),
#[cfg(not(any(target_os = "haiku", target_os = "vita")))]
sin_zero: [0; 8_usize],
#[cfg(target_os = "haiku")]
sin_zero: [0; 24_usize],
#[cfg(target_os = "vita")]
sin_zero: [0; 6_usize],
#[cfg(target_os = "vita")]
sin_vport: 0,
}
}
pub(crate) fn encode_sockaddr_v6(v6: &SocketAddrV6) -> c::sockaddr_in6 {
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita"
))]
{
sockaddr_in6_new(
core::mem::size_of::<c::sockaddr_in6>() as _,
c::AF_INET6 as _,
u16::to_be(v6.port()),
u32::to_be(v6.flowinfo()),
in6_addr_new(v6.ip().octets()),
v6.scope_id(),
)
}
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita"
)))]
{
sockaddr_in6_new(
c::AF_INET6 as _,
u16::to_be(v6.port()),
u32::to_be(v6.flowinfo()),
in6_addr_new(v6.ip().octets()),
v6.scope_id(),
)
}
}

View File

@@ -0,0 +1,67 @@
use crate::backend::c;
#[cfg(any(
all(target_os = "android", target_pointer_width = "64"),
target_os = "linux",
))]
use crate::ffi::CStr;
// `getauxval` wasn't supported in glibc until 2.16.
#[cfg(any(
all(target_os = "android", target_pointer_width = "64"),
target_os = "linux",
))]
weak!(fn getauxval(c::c_ulong) -> *mut c::c_void);
#[inline]
pub(crate) fn page_size() -> usize {
unsafe { c::sysconf(c::_SC_PAGESIZE) as usize }
}
#[cfg(not(any(target_os = "horizon", target_os = "vita", target_os = "wasi")))]
#[inline]
pub(crate) fn clock_ticks_per_second() -> u64 {
unsafe { c::sysconf(c::_SC_CLK_TCK) as u64 }
}
#[cfg(any(
all(target_os = "android", target_pointer_width = "64"),
target_os = "linux",
))]
#[inline]
pub(crate) fn linux_hwcap() -> (usize, usize) {
if let Some(libc_getauxval) = getauxval.get() {
unsafe {
let hwcap = libc_getauxval(c::AT_HWCAP) as usize;
let hwcap2 = libc_getauxval(c::AT_HWCAP2) as usize;
(hwcap, hwcap2)
}
} else {
(0, 0)
}
}
#[cfg(any(
all(target_os = "android", target_pointer_width = "64"),
target_os = "linux",
))]
#[inline]
pub(crate) fn linux_minsigstksz() -> usize {
if let Some(libc_getauxval) = getauxval.get() {
unsafe { libc_getauxval(c::AT_MINSIGSTKSZ) as usize }
} else {
0
}
}
#[cfg(any(
all(target_os = "android", target_pointer_width = "64"),
target_os = "linux",
))]
#[inline]
pub(crate) fn linux_execfn() -> &'static CStr {
if let Some(libc_getauxval) = getauxval.get() {
unsafe { CStr::from_ptr(libc_getauxval(c::AT_EXECFN).cast()) }
} else {
cstr!("")
}
}

View File

@@ -0,0 +1 @@
pub(crate) mod auxv;

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -0,0 +1,14 @@
//! libc syscalls for PIDs
use crate::backend::c;
use crate::pid::Pid;
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getpid() -> Pid {
unsafe {
let pid = c::getpid();
Pid::from_raw_unchecked(pid)
}
}

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,126 @@
use crate::backend::c;
use crate::backend::conv::ret;
use crate::fd::OwnedFd;
use crate::io;
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "wasi"
)))]
use crate::pipe::PipeFlags;
use core::mem::MaybeUninit;
#[cfg(linux_kernel)]
use {
crate::backend::conv::{borrowed_fd, ret_c_int, ret_usize},
crate::backend::MAX_IOV,
crate::fd::BorrowedFd,
crate::pipe::{IoSliceRaw, SpliceFlags},
crate::utils::option_as_mut_ptr,
core::cmp::min,
};
#[cfg(not(target_os = "wasi"))]
pub(crate) fn pipe() -> io::Result<(OwnedFd, OwnedFd)> {
unsafe {
let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit();
ret(c::pipe(result.as_mut_ptr().cast::<i32>()))?;
let [p0, p1] = result.assume_init();
Ok((p0, p1))
}
}
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "wasi"
)))]
pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> {
unsafe {
let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit();
ret(c::pipe2(
result.as_mut_ptr().cast::<i32>(),
bitflags_bits!(flags),
))?;
let [p0, p1] = result.assume_init();
Ok((p0, p1))
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn splice(
fd_in: BorrowedFd<'_>,
off_in: Option<&mut u64>,
fd_out: BorrowedFd<'_>,
off_out: Option<&mut u64>,
len: usize,
flags: SpliceFlags,
) -> io::Result<usize> {
let off_in = option_as_mut_ptr(off_in).cast();
let off_out = option_as_mut_ptr(off_out).cast();
unsafe {
ret_usize(c::splice(
borrowed_fd(fd_in),
off_in,
borrowed_fd(fd_out),
off_out,
len,
flags.bits(),
))
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) unsafe fn vmsplice(
fd: BorrowedFd<'_>,
bufs: &[IoSliceRaw<'_>],
flags: SpliceFlags,
) -> io::Result<usize> {
ret_usize(c::vmsplice(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), MAX_IOV),
flags.bits(),
))
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn tee(
fd_in: BorrowedFd<'_>,
fd_out: BorrowedFd<'_>,
len: usize,
flags: SpliceFlags,
) -> io::Result<usize> {
unsafe {
ret_usize(c::tee(
borrowed_fd(fd_in),
borrowed_fd(fd_out),
len,
flags.bits(),
))
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn fcntl_getpipe_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETPIPE_SZ)).map(|size| size as usize) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn fcntl_setpipe_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<usize> {
let size: c::c_int = size.try_into().map_err(|_| io::Errno::PERM)?;
unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_SETPIPE_SZ, size)).map(|size| size as usize) }
}

View File

@@ -0,0 +1,117 @@
#[cfg(linux_kernel)]
use crate::ffi;
#[cfg(linux_kernel)]
use core::marker::PhantomData;
#[cfg(not(any(apple, target_os = "wasi")))]
use {crate::backend::c, bitflags::bitflags};
#[cfg(not(any(apple, target_os = "wasi")))]
bitflags! {
/// `O_*` constants for use with [`pipe_with`].
///
/// [`pipe_with`]: crate::pipe::pipe_with
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct PipeFlags: u32 {
/// `O_CLOEXEC`
const CLOEXEC = bitcast!(c::O_CLOEXEC);
/// `O_DIRECT`
#[cfg(not(any(
solarish,
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "hurd",
target_os = "nto",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
)))]
const DIRECT = bitcast!(c::O_DIRECT);
/// `O_NONBLOCK`
const NONBLOCK = bitcast!(c::O_NONBLOCK);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
/// `SPLICE_F_*` constants for use with [`splice`], [`vmsplice`], and
/// [`tee`].
///
/// [`splice`]: crate::pipe::splice
/// [`vmsplice`]: crate::pipe::splice
/// [`tee`]: crate::pipe::tee
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct SpliceFlags: ffi::c_uint {
/// `SPLICE_F_MOVE`
const MOVE = c::SPLICE_F_MOVE;
/// `SPLICE_F_NONBLOCK`
const NONBLOCK = c::SPLICE_F_NONBLOCK;
/// `SPLICE_F_MORE`
const MORE = c::SPLICE_F_MORE;
/// `SPLICE_F_GIFT`
const GIFT = c::SPLICE_F_GIFT;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
/// A buffer type for use with [`vmsplice`].
///
/// It is guaranteed to be ABI compatible with the iovec type on Unix platforms
/// and `WSABUF` on Windows. Unlike `IoSlice` and `IoSliceMut` it is
/// semantically like a raw pointer, and therefore can be shared or mutated as
/// needed.
///
/// [`vmsplice`]: crate::pipe::vmsplice
#[cfg(linux_kernel)]
#[repr(transparent)]
pub struct IoSliceRaw<'a> {
_buf: c::iovec,
_lifetime: PhantomData<&'a ()>,
}
#[cfg(linux_kernel)]
impl<'a> IoSliceRaw<'a> {
/// Creates a new `IoSlice` wrapping a byte slice.
pub fn from_slice(buf: &'a [u8]) -> Self {
IoSliceRaw {
_buf: c::iovec {
iov_base: (buf.as_ptr() as *mut u8).cast::<c::c_void>(),
iov_len: buf.len() as _,
},
_lifetime: PhantomData,
}
}
/// Creates a new `IoSlice` wrapping a mutable byte slice.
pub fn from_slice_mut(buf: &'a mut [u8]) -> Self {
IoSliceRaw {
_buf: c::iovec {
iov_base: buf.as_mut_ptr().cast::<c::c_void>(),
iov_len: buf.len() as _,
},
_lifetime: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[cfg(not(any(apple, target_os = "wasi")))]
#[test]
fn test_types() {
assert_eq_size!(PipeFlags, c::c_int);
#[cfg(linux_kernel)]
assert_eq_size!(SpliceFlags, c::c_int);
}
}

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -0,0 +1,14 @@
use crate::backend::c;
use crate::backend::conv::ret_c_int;
use crate::io;
#[inline]
pub(crate) unsafe fn prctl(
option: c::c_int,
arg2: *mut c::c_void,
arg3: *mut c::c_void,
arg4: *mut c::c_void,
arg5: *mut c::c_void,
) -> io::Result<c::c_int> {
ret_c_int(c::prctl(option, arg2, arg3, arg4, arg5))
}

View File

@@ -0,0 +1,5 @@
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))]
pub(crate) mod wait;

View File

@@ -0,0 +1,704 @@
//! libc syscalls supporting `rustix::process`.
use crate::backend::c;
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
use crate::backend::conv::borrowed_fd;
#[cfg(any(target_os = "linux", feature = "fs"))]
use crate::backend::conv::c_str;
#[cfg(all(feature = "alloc", feature = "fs", not(target_os = "wasi")))]
use crate::backend::conv::ret_discarded_char_ptr;
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use crate::backend::conv::ret_infallible;
#[cfg(not(target_os = "wasi"))]
use crate::backend::conv::ret_pid_t;
#[cfg(all(feature = "alloc", not(target_os = "wasi")))]
use crate::backend::conv::ret_usize;
use crate::backend::conv::{ret, ret_c_int};
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
use crate::fd::BorrowedFd;
#[cfg(target_os = "linux")]
use crate::fd::{AsRawFd as _, OwnedFd, RawFd};
#[cfg(any(target_os = "linux", feature = "fs"))]
use crate::ffi::CStr;
#[cfg(feature = "fs")]
use crate::fs::Mode;
use crate::io;
#[cfg(not(any(
target_os = "emscripten",
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use crate::process::Flock;
#[cfg(all(feature = "alloc", not(target_os = "wasi")))]
use crate::process::Gid;
#[cfg(not(target_os = "wasi"))]
use crate::process::Pid;
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
use crate::process::Signal;
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "vita",
target_os = "wasi"
)))]
use crate::process::Uid;
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))]
use crate::process::{RawPid, WaitOptions, WaitStatus};
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use crate::process::{Resource, Rlimit};
#[cfg(not(any(
target_os = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
use crate::process::{WaitId, WaitIdOptions, WaitIdStatus};
use core::mem::MaybeUninit;
#[cfg(target_os = "linux")]
use {
super::super::conv::ret_owned_fd, crate::process::PidfdFlags, crate::process::PidfdGetfdFlags,
};
#[cfg(feature = "fs")]
#[cfg(not(target_os = "wasi"))]
pub(crate) fn chdir(path: &CStr) -> io::Result<()> {
unsafe { ret(c::chdir(c_str(path))) }
}
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
pub(crate) fn fchdir(dirfd: BorrowedFd<'_>) -> io::Result<()> {
unsafe { ret(c::fchdir(borrowed_fd(dirfd))) }
}
#[cfg(feature = "fs")]
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
pub(crate) fn chroot(path: &CStr) -> io::Result<()> {
unsafe { ret(c::chroot(c_str(path))) }
}
#[cfg(all(feature = "alloc", feature = "fs"))]
#[cfg(not(target_os = "wasi"))]
pub(crate) fn getcwd(buf: &mut [MaybeUninit<u8>]) -> io::Result<()> {
unsafe { ret_discarded_char_ptr(c::getcwd(buf.as_mut_ptr().cast(), buf.len())) }
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getppid() -> Option<Pid> {
unsafe {
let pid: i32 = c::getppid();
Pid::from_raw(pid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn getpgid(pid: Option<Pid>) -> io::Result<Pid> {
unsafe {
let pgid = ret_pid_t(c::getpgid(Pid::as_raw(pid) as _))?;
Ok(Pid::from_raw_unchecked(pgid))
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn setpgid(pid: Option<Pid>, pgid: Option<Pid>) -> io::Result<()> {
unsafe { ret(c::setpgid(Pid::as_raw(pid) as _, Pid::as_raw(pgid) as _)) }
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getpgrp() -> Pid {
unsafe {
let pgid = c::getpgrp();
Pid::from_raw_unchecked(pgid)
}
}
#[cfg(not(target_os = "wasi"))]
#[cfg(feature = "fs")]
#[inline]
pub(crate) fn umask(mask: Mode) -> Mode {
unsafe { Mode::from_bits_retain(c::umask(mask.bits() as c::mode_t).into()) }
}
#[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))]
#[inline]
pub(crate) fn nice(inc: i32) -> io::Result<i32> {
libc_errno::set_errno(libc_errno::Errno(0));
let r = unsafe { c::nice(inc) };
if libc_errno::errno().0 != 0 {
ret_c_int(r)
} else {
Ok(r)
}
}
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn getpriority_user(uid: Uid) -> io::Result<i32> {
libc_errno::set_errno(libc_errno::Errno(0));
let r = unsafe { c::getpriority(c::PRIO_USER, uid.as_raw() as _) };
if libc_errno::errno().0 != 0 {
ret_c_int(r)
} else {
Ok(r)
}
}
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32> {
libc_errno::set_errno(libc_errno::Errno(0));
let r = unsafe { c::getpriority(c::PRIO_PGRP, Pid::as_raw(pgid) as _) };
if libc_errno::errno().0 != 0 {
ret_c_int(r)
} else {
Ok(r)
}
}
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn getpriority_process(pid: Option<Pid>) -> io::Result<i32> {
libc_errno::set_errno(libc_errno::Errno(0));
let r = unsafe { c::getpriority(c::PRIO_PROCESS, Pid::as_raw(pid) as _) };
if libc_errno::errno().0 != 0 {
ret_c_int(r)
} else {
Ok(r)
}
}
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> {
unsafe { ret(c::setpriority(c::PRIO_USER, uid.as_raw() as _, priority)) }
}
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()> {
unsafe {
ret(c::setpriority(
c::PRIO_PGRP,
Pid::as_raw(pgid) as _,
priority,
))
}
}
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()> {
unsafe {
ret(c::setpriority(
c::PRIO_PROCESS,
Pid::as_raw(pid) as _,
priority,
))
}
}
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn getrlimit(limit: Resource) -> Rlimit {
let mut result = MaybeUninit::<c::rlimit>::uninit();
unsafe {
ret_infallible(c::getrlimit(limit as _, result.as_mut_ptr()));
rlimit_from_libc(result.assume_init())
}
}
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> {
let lim = rlimit_to_libc(new)?;
unsafe { ret(c::setrlimit(limit as _, &lim)) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit> {
let lim = rlimit_to_libc(new)?;
let mut result = MaybeUninit::<c::rlimit>::uninit();
unsafe {
ret(c::prlimit(
Pid::as_raw(pid),
limit as _,
&lim,
result.as_mut_ptr(),
))?;
Ok(rlimit_from_libc(result.assume_init()))
}
}
/// Convert a C `c::rlimit` to a Rust `Rlimit`.
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
fn rlimit_from_libc(lim: c::rlimit) -> Rlimit {
let current = if lim.rlim_cur == c::RLIM_INFINITY {
None
} else {
Some(lim.rlim_cur.try_into().unwrap())
};
let maximum = if lim.rlim_max == c::RLIM_INFINITY {
None
} else {
Some(lim.rlim_max.try_into().unwrap())
};
Rlimit { current, maximum }
}
/// Convert a Rust [`Rlimit`] to a C `c::rlimit`.
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
fn rlimit_to_libc(lim: Rlimit) -> io::Result<c::rlimit> {
let Rlimit { current, maximum } = lim;
let rlim_cur = match current {
Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
None => c::RLIM_INFINITY as _,
};
let rlim_max = match maximum {
Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
None => c::RLIM_INFINITY as _,
};
Ok(c::rlimit { rlim_cur, rlim_max })
}
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))]
#[inline]
pub(crate) fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
_waitpid(!0, waitopts)
}
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))]
#[inline]
pub(crate) fn waitpid(
pid: Option<Pid>,
waitopts: WaitOptions,
) -> io::Result<Option<(Pid, WaitStatus)>> {
_waitpid(Pid::as_raw(pid), waitopts)
}
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))]
#[inline]
pub(crate) fn waitpgid(pgid: Pid, waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
_waitpid(-pgid.as_raw_nonzero().get(), waitopts)
}
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))]
#[inline]
pub(crate) fn _waitpid(
pid: RawPid,
waitopts: WaitOptions,
) -> io::Result<Option<(Pid, WaitStatus)>> {
unsafe {
let mut status: c::c_int = 0;
let pid = ret_c_int(c::waitpid(pid as _, &mut status, waitopts.bits() as _))?;
Ok(Pid::from_raw(pid).map(|pid| (pid, WaitStatus::new(status as _))))
}
}
#[cfg(not(any(
target_os = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
pub(crate) fn waitid(id: WaitId<'_>, options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
// Get the id to wait on.
match id {
WaitId::All => _waitid_all(options),
WaitId::Pid(pid) => _waitid_pid(pid, options),
WaitId::Pgid(pgid) => _waitid_pgid(pgid, options),
#[cfg(target_os = "linux")]
WaitId::PidFd(fd) => _waitid_pidfd(fd, options),
#[cfg(not(target_os = "linux"))]
WaitId::__EatLifetime(_) => unreachable!(),
}
}
#[cfg(not(any(
target_os = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
fn _waitid_all(options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
// `waitid` can return successfully without initializing the struct (no
// children found when using `WNOHANG`)
let mut status = MaybeUninit::<c::siginfo_t>::zeroed();
unsafe {
ret(c::waitid(
c::P_ALL,
0,
status.as_mut_ptr(),
options.bits() as _,
))?
};
Ok(unsafe { cvt_waitid_status(status) })
}
#[cfg(not(any(
target_os = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
fn _waitid_pid(pid: Pid, options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
// `waitid` can return successfully without initializing the struct (no
// children found when using `WNOHANG`)
let mut status = MaybeUninit::<c::siginfo_t>::zeroed();
unsafe {
ret(c::waitid(
c::P_PID,
Pid::as_raw(Some(pid)) as _,
status.as_mut_ptr(),
options.bits() as _,
))?
};
Ok(unsafe { cvt_waitid_status(status) })
}
#[cfg(not(any(
target_os = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
fn _waitid_pgid(pgid: Option<Pid>, options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
// `waitid` can return successfully without initializing the struct (no
// children found when using `WNOHANG`)
let mut status = MaybeUninit::<c::siginfo_t>::zeroed();
unsafe {
ret(c::waitid(
c::P_PGID,
Pid::as_raw(pgid) as _,
status.as_mut_ptr(),
options.bits() as _,
))?
};
Ok(unsafe { cvt_waitid_status(status) })
}
#[cfg(target_os = "linux")]
#[inline]
fn _waitid_pidfd(fd: BorrowedFd<'_>, options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
// `waitid` can return successfully without initializing the struct (no
// children found when using `WNOHANG`)
let mut status = MaybeUninit::<c::siginfo_t>::zeroed();
unsafe {
ret(c::waitid(
c::P_PIDFD,
fd.as_raw_fd() as _,
status.as_mut_ptr(),
options.bits() as _,
))?
};
Ok(unsafe { cvt_waitid_status(status) })
}
/// Convert a `siginfo_t` to a `WaitIdStatus`.
///
/// # Safety
///
/// The caller must ensure that `status` is initialized and that `waitid`
/// returned successfully.
#[cfg(not(any(
target_os = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
unsafe fn cvt_waitid_status(status: MaybeUninit<c::siginfo_t>) -> Option<WaitIdStatus> {
let status = status.assume_init();
// `si_pid` is supposedly the better way to check that the struct has been
// filled, e.g. the Linux manual page says about the `WNOHANG` case “zero
// out the si_pid field before the call and check for a nonzero value”.
// But e.g. NetBSD/OpenBSD don't have it exposed in the libc crate for now,
// and some platforms don't have it at all. For simplicity, always check
// `si_signo`. We have zero-initialized the whole struct, and all kernels
// should set `SIGCHLD` here.
if status.si_signo == 0 {
None
} else {
Some(WaitIdStatus(status))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
#[inline]
pub(crate) fn getsid(pid: Option<Pid>) -> io::Result<Pid> {
unsafe {
let pid = ret_pid_t(c::getsid(Pid::as_raw(pid) as _))?;
Ok(Pid::from_raw_unchecked(pid))
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn setsid() -> io::Result<Pid> {
unsafe {
let pid = ret_c_int(c::setsid())?;
Ok(Pid::from_raw_unchecked(pid))
}
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
#[inline]
pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> {
unsafe { ret(c::kill(pid.as_raw_nonzero().get(), sig.as_raw())) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
#[inline]
pub(crate) fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> {
unsafe {
ret(c::kill(
pid.as_raw_nonzero().get().wrapping_neg(),
sig.as_raw(),
))
}
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
#[inline]
pub(crate) fn kill_current_process_group(sig: Signal) -> io::Result<()> {
unsafe { ret(c::kill(0, sig.as_raw())) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
pub(crate) fn test_kill_process(pid: Pid) -> io::Result<()> {
unsafe { ret(c::kill(pid.as_raw_nonzero().get(), 0)) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
#[inline]
pub(crate) fn test_kill_process_group(pid: Pid) -> io::Result<()> {
unsafe { ret(c::kill(pid.as_raw_nonzero().get().wrapping_neg(), 0)) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
#[inline]
pub(crate) fn test_kill_current_process_group() -> io::Result<()> {
unsafe { ret(c::kill(0, 0)) }
}
#[cfg(freebsdlike)]
#[inline]
pub(crate) unsafe fn procctl(
idtype: c::idtype_t,
id: c::id_t,
option: c::c_int,
data: *mut c::c_void,
) -> io::Result<()> {
ret(c::procctl(idtype, id, option, data))
}
#[cfg(target_os = "linux")]
pub(crate) fn pidfd_open(pid: Pid, flags: PidfdFlags) -> io::Result<OwnedFd> {
syscall! {
fn pidfd_open(
pid: c::pid_t,
flags: c::c_uint
) via SYS_pidfd_open -> c::c_int
}
unsafe {
ret_owned_fd(pidfd_open(
pid.as_raw_nonzero().get(),
bitflags_bits!(flags),
))
}
}
#[cfg(target_os = "linux")]
pub(crate) fn pidfd_send_signal(pidfd: BorrowedFd<'_>, sig: Signal) -> io::Result<()> {
syscall! {
fn pidfd_send_signal(
pid: c::pid_t,
sig: c::c_int,
info: *const c::siginfo_t,
flags: c::c_int
) via SYS_pidfd_send_signal -> c::c_int
}
unsafe {
ret(pidfd_send_signal(
borrowed_fd(pidfd),
sig.as_raw(),
core::ptr::null(),
0,
))
}
}
#[cfg(target_os = "linux")]
pub(crate) fn pidfd_getfd(
pidfd: BorrowedFd<'_>,
targetfd: RawFd,
flags: PidfdGetfdFlags,
) -> io::Result<OwnedFd> {
syscall! {
fn pidfd_getfd(
pidfd: c::c_int,
targetfd: c::c_int,
flags: c::c_uint
) via SYS_pidfd_getfd -> c::c_int
}
unsafe {
ret_owned_fd(pidfd_getfd(
borrowed_fd(pidfd),
targetfd,
bitflags_bits!(flags),
))
}
}
#[cfg(target_os = "linux")]
pub(crate) fn pivot_root(new_root: &CStr, put_old: &CStr) -> io::Result<()> {
syscall! {
fn pivot_root(
new_root: *const c::c_char,
put_old: *const c::c_char
) via SYS_pivot_root -> c::c_int
}
unsafe { ret(pivot_root(c_str(new_root), c_str(put_old))) }
}
#[cfg(all(feature = "alloc", not(target_os = "wasi")))]
pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result<usize> {
let len = buf.len().try_into().map_err(|_| io::Errno::NOMEM)?;
unsafe { ret_usize(c::getgroups(len, buf.as_mut_ptr().cast()) as isize) }
}
#[cfg(not(any(
target_os = "emscripten",
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn fcntl_getlk(fd: BorrowedFd<'_>, lock: &Flock) -> io::Result<Option<Flock>> {
let mut curr_lock: c::flock = lock.as_raw();
unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_GETLK, &mut curr_lock))? };
// If no blocking lock is found, `fcntl(GETLK, ..)` sets `l_type` to
// `F_UNLCK`.
if curr_lock.l_type == c::F_UNLCK as _ {
Ok(None)
} else {
Ok(Some(unsafe { Flock::from_raw_unchecked(curr_lock) }))
}
}

View File

@@ -0,0 +1,139 @@
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use crate::backend::c;
/// A resource value for use with [`getrlimit`], [`setrlimit`], and
/// [`prlimit`].
///
/// [`getrlimit`]: crate::process::getrlimit
/// [`setrlimit`]: crate::process::setrlimit
/// [`prlimit`]: crate::process::prlimit
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(not(target_os = "l4re"), repr(u32))]
#[cfg_attr(target_os = "l4re", repr(u64))]
#[non_exhaustive]
pub enum Resource {
/// `RLIMIT_CPU`
Cpu = bitcast!(c::RLIMIT_CPU),
/// `RLIMIT_FSIZE`
Fsize = bitcast!(c::RLIMIT_FSIZE),
/// `RLIMIT_DATA`
Data = bitcast!(c::RLIMIT_DATA),
/// `RLIMIT_STACK`
Stack = bitcast!(c::RLIMIT_STACK),
/// `RLIMIT_CORE`
#[cfg(not(target_os = "haiku"))]
Core = bitcast!(c::RLIMIT_CORE),
/// `RLIMIT_RSS`
// "nto" has `RLIMIT_RSS`, but it has the same value as `RLIMIT_AS`.
#[cfg(not(any(
apple,
solarish,
target_os = "cygwin",
target_os = "haiku",
target_os = "nto",
)))]
Rss = bitcast!(c::RLIMIT_RSS),
/// `RLIMIT_NPROC`
#[cfg(not(any(solarish, target_os = "cygwin", target_os = "haiku")))]
Nproc = bitcast!(c::RLIMIT_NPROC),
/// `RLIMIT_NOFILE`
Nofile = bitcast!(c::RLIMIT_NOFILE),
/// `RLIMIT_MEMLOCK`
#[cfg(not(any(solarish, target_os = "aix", target_os = "cygwin", target_os = "haiku")))]
Memlock = bitcast!(c::RLIMIT_MEMLOCK),
/// `RLIMIT_AS`
#[cfg(not(target_os = "openbsd"))]
As = bitcast!(c::RLIMIT_AS),
/// `RLIMIT_LOCKS`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
)))]
Locks = bitcast!(c::RLIMIT_LOCKS),
/// `RLIMIT_SIGPENDING`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
)))]
Sigpending = bitcast!(c::RLIMIT_SIGPENDING),
/// `RLIMIT_MSGQUEUE`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
)))]
Msgqueue = bitcast!(c::RLIMIT_MSGQUEUE),
/// `RLIMIT_NICE`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
)))]
Nice = bitcast!(c::RLIMIT_NICE),
/// `RLIMIT_RTPRIO`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
)))]
Rtprio = bitcast!(c::RLIMIT_RTPRIO),
/// `RLIMIT_RTTIME`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "android",
target_os = "cygwin",
target_os = "emscripten",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
)))]
Rttime = bitcast!(c::RLIMIT_RTTIME),
}
#[cfg(apple)]
#[allow(non_upper_case_globals)]
impl Resource {
/// `RLIMIT_RSS`
pub const Rss: Self = Self::As;
}
#[cfg(freebsdlike)]
pub type RawId = c::id_t;

View File

@@ -0,0 +1,17 @@
use crate::backend::c;
pub(crate) use c::{
WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WSTOPSIG, WTERMSIG,
};
#[cfg(not(target_os = "horizon"))]
pub(crate) use c::{WCONTINUED, WUNTRACED};
#[cfg(not(any(
target_os = "cygwin",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi",
)))]
pub(crate) use c::{WEXITED, WNOWAIT, WSTOPPED};

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -0,0 +1,118 @@
//! libc syscalls supporting `rustix::pty`.
use crate::backend::c;
use crate::backend::conv::{borrowed_fd, ret};
use crate::fd::BorrowedFd;
use crate::io;
#[cfg(all(
feature = "alloc",
any(
apple,
linux_like,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos"
)
))]
use {
crate::ffi::{CStr, CString},
crate::path::SMALL_PATH_BUFFER_SIZE,
alloc::borrow::ToOwned as _,
alloc::vec::Vec,
};
#[cfg(not(linux_kernel))]
use crate::{backend::conv::ret_owned_fd, fd::OwnedFd, pty::OpenptFlags};
#[cfg(not(linux_kernel))]
#[inline]
pub(crate) fn openpt(flags: OpenptFlags) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::posix_openpt(flags.bits() as _)) }
}
#[cfg(all(
feature = "alloc",
any(
apple,
linux_like,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos"
)
))]
#[inline]
pub(crate) fn ptsname(fd: BorrowedFd<'_>, mut buffer: Vec<u8>) -> io::Result<CString> {
// This code would benefit from having a better way to read into
// uninitialized memory, but that requires `unsafe`.
buffer.clear();
buffer.reserve(SMALL_PATH_BUFFER_SIZE);
buffer.resize(buffer.capacity(), 0_u8);
loop {
// On platforms with `ptsname_r`, use it.
#[cfg(any(linux_like, target_os = "fuchsia", target_os = "illumos"))]
let r = unsafe { c::ptsname_r(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len()) };
// FreeBSD 12 doesn't have `ptsname_r`.
#[cfg(target_os = "freebsd")]
let r = unsafe {
weak! {
fn ptsname_r(
c::c_int,
*mut c::c_char,
c::size_t
) -> c::c_int
}
if let Some(func) = ptsname_r.get() {
func(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len())
} else {
c::ENOSYS
}
};
// macOS 10.13.4 has `ptsname_r`; use it if we have it, otherwise fall
// back to calling the underlying ioctl directly.
#[cfg(apple)]
let r = unsafe {
weak! { fn ptsname_r(c::c_int, *mut c::c_char, c::size_t) -> c::c_int }
if let Some(libc_ptsname_r) = ptsname_r.get() {
libc_ptsname_r(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len())
} else {
// The size declared in the `TIOCPTYGNAME` macro in
// sys/ttycom.h is 128.
let mut name: [u8; 128] = [0_u8; 128];
match c::ioctl(borrowed_fd(fd), c::TIOCPTYGNAME as _, &mut name) {
0 => {
let len = CStr::from_ptr(name.as_ptr().cast()).to_bytes().len();
core::ptr::copy_nonoverlapping(name.as_ptr(), buffer.as_mut_ptr(), len + 1);
0
}
_ => libc_errno::errno().0,
}
}
};
if r == 0 {
return Ok(unsafe { CStr::from_ptr(buffer.as_ptr().cast()).to_owned() });
}
if r != c::ERANGE {
return Err(io::Errno::from_raw_os_error(r));
}
// Use `Vec` reallocation strategy to grow capacity exponentially.
buffer.reserve(1);
buffer.resize(buffer.capacity(), 0_u8);
}
}
#[inline]
pub(crate) fn unlockpt(fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe { ret(c::unlockpt(borrowed_fd(fd))) }
}
#[cfg(not(linux_kernel))]
#[inline]
pub(crate) fn grantpt(fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe { ret(c::grantpt(borrowed_fd(fd))) }
}

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,14 @@
//! libc syscalls supporting `rustix::rand`.
#[cfg(linux_kernel)]
use {crate::backend::c, crate::backend::conv::ret_usize, crate::io, crate::rand::GetRandomFlags};
#[cfg(linux_kernel)]
pub(crate) unsafe fn getrandom(buf: (*mut u8, usize), flags: GetRandomFlags) -> io::Result<usize> {
// `getrandom` wasn't supported in glibc until 2.25.
weak_or_syscall! {
fn getrandom(buf: *mut c::c_void, buflen: c::size_t, flags: c::c_uint) via SYS_getrandom -> c::ssize_t
}
ret_usize(getrandom(buf.0.cast(), buf.1, flags.bits()))
}

View File

@@ -0,0 +1,24 @@
#[cfg(linux_kernel)]
use crate::backend::c;
#[cfg(linux_kernel)]
use bitflags::bitflags;
#[cfg(linux_kernel)]
bitflags! {
/// `GRND_*` flags for use with [`getrandom`].
///
/// [`getrandom`]: crate::rand::getrandom
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct GetRandomFlags: u32 {
/// `GRND_RANDOM`
const RANDOM = c::GRND_RANDOM;
/// `GRND_NONBLOCK`
const NONBLOCK = c::GRND_NONBLOCK;
/// `GRND_INSECURE`
const INSECURE = c::GRND_INSECURE;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,24 @@
use crate::ffi::CStr;
use crate::backend::c;
use crate::backend::conv::{c_str, ret, ret_owned_fd};
use crate::fd::OwnedFd;
use crate::fs::Mode;
use crate::{io, shm};
pub(crate) fn shm_open(name: &CStr, oflags: shm::OFlags, mode: Mode) -> io::Result<OwnedFd> {
// On this platforms, `mode_t` is `u16` and can't be passed directly to a
// variadic function.
#[cfg(apple)]
let mode: c::c_uint = mode.bits().into();
// Otherwise, cast to `mode_t` as that's what `open` is documented to take.
#[cfg(not(apple))]
let mode: c::mode_t = mode.bits() as _;
unsafe { ret_owned_fd(c::shm_open(c_str(name), bitflags_bits!(oflags), mode)) }
}
pub(crate) fn shm_unlink(name: &CStr) -> io::Result<()> {
unsafe { ret(c::shm_unlink(c_str(name))) }
}

View File

@@ -0,0 +1,30 @@
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `O_*` constants for use with [`shm::open`].
///
/// [`shm::open`]: crate:shm::open
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ShmOFlags: u32 {
/// `O_CREAT`
#[doc(alias = "CREAT")]
const CREATE = bitcast!(c::O_CREAT);
/// `O_EXCL`
const EXCL = bitcast!(c::O_EXCL);
/// `O_RDONLY`
const RDONLY = bitcast!(c::O_RDONLY);
/// `O_RDWR`
const RDWR = bitcast!(c::O_RDWR);
/// `O_TRUNC`
const TRUNC = bitcast!(c::O_TRUNC);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,3 @@
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,162 @@
//! libc syscalls supporting `rustix::process`.
use super::types::RawUname;
use crate::backend::c;
#[cfg(not(target_os = "wasi"))]
use crate::backend::conv::ret_infallible;
#[cfg(target_os = "linux")]
use crate::system::RebootCommand;
use core::mem::MaybeUninit;
#[cfg(linux_kernel)]
use {
crate::backend::conv::c_str, crate::fd::BorrowedFd, crate::ffi::CStr, crate::system::Sysinfo,
};
#[cfg(not(any(
target_os = "emscripten",
target_os = "espidf",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use {crate::backend::conv::ret, crate::io};
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn uname() -> RawUname {
let mut uname = MaybeUninit::<RawUname>::uninit();
unsafe {
let r = c::uname(uname.as_mut_ptr());
// On POSIX, `uname` is documented to return non-negative on success
// instead of the usual 0, though some specific systems do document
// that they always use zero allowing us to skip this check.
#[cfg(not(any(apple, freebsdlike, linux_like, target_os = "netbsd")))]
let r = core::cmp::min(r, 0);
ret_infallible(r);
uname.assume_init()
}
}
#[cfg(linux_kernel)]
pub(crate) fn sysinfo() -> Sysinfo {
let mut info = MaybeUninit::<Sysinfo>::uninit();
unsafe {
ret_infallible(c::sysinfo(info.as_mut_ptr()));
info.assume_init()
}
}
#[cfg(not(any(
target_os = "emscripten",
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
pub(crate) fn sethostname(name: &[u8]) -> io::Result<()> {
unsafe {
ret(c::sethostname(
name.as_ptr().cast(),
name.len().try_into().map_err(|_| io::Errno::INVAL)?,
))
}
}
#[cfg(not(any(
target_os = "android",
target_os = "cygwin",
target_os = "emscripten",
target_os = "espidf",
target_os = "illumos",
target_os = "haiku",
target_os = "horizon",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
target_os = "wasi",
)))]
pub(crate) fn setdomainname(name: &[u8]) -> io::Result<()> {
unsafe {
ret(c::setdomainname(
name.as_ptr().cast(),
name.len().try_into().map_err(|_| io::Errno::INVAL)?,
))
}
}
// <https://github.com/rust-lang/libc/pull/4212>
#[cfg(target_os = "android")]
pub(crate) fn setdomainname(name: &[u8]) -> io::Result<()> {
syscall! {
fn setdomainname(
name: *const c::c_char,
len: c::size_t
) via SYS_setdomainname -> c::c_int
}
unsafe {
ret(setdomainname(
name.as_ptr().cast(),
name.len().try_into().map_err(|_| io::Errno::INVAL)?,
))
}
}
#[cfg(target_os = "linux")]
pub(crate) fn reboot(cmd: RebootCommand) -> io::Result<()> {
unsafe { ret(c::reboot(cmd as i32)) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn init_module(image: &[u8], param_values: &CStr) -> io::Result<()> {
syscall! {
fn init_module(
module_image: *const c::c_void,
len: c::c_ulong,
param_values: *const c::c_char
) via SYS_init_module -> c::c_int
}
unsafe {
ret(init_module(
image.as_ptr().cast(),
image.len() as _,
c_str(param_values),
))
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn finit_module(
fd: BorrowedFd<'_>,
param_values: &CStr,
flags: c::c_int,
) -> io::Result<()> {
use crate::fd::AsRawFd as _;
syscall! {
fn finit_module(
fd: c::c_int,
param_values: *const c::c_char,
flags: c::c_int
) via SYS_finit_module -> c::c_int
}
unsafe { ret(finit_module(fd.as_raw_fd(), c_str(param_values), flags)) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn delete_module(name: &CStr, flags: c::c_int) -> io::Result<()> {
syscall! {
fn delete_module(
name: *const c::c_char,
flags: c::c_int
) via SYS_delete_module -> c::c_int
}
unsafe { ret(delete_module(c_str(name), flags)) }
}

View File

@@ -0,0 +1,8 @@
use crate::backend::c;
/// `sysinfo`
#[cfg(linux_kernel)]
pub type Sysinfo = c::sysinfo;
#[cfg(not(target_os = "wasi"))]
pub(crate) type RawUname = c::utsname;

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,531 @@
//! libc syscalls supporting `rustix::termios`.
//!
//! # Safety
//!
//! See the `rustix::backend::syscalls` module documentation for details.
use crate::backend::c;
#[cfg(not(target_os = "wasi"))]
use crate::backend::conv::ret_pid_t;
use crate::backend::conv::{borrowed_fd, ret};
use crate::fd::BorrowedFd;
#[cfg(feature = "alloc")]
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
use crate::ffi::CStr;
#[cfg(any(
not(target_os = "espidf"),
not(any(target_os = "fuchsia", target_os = "wasi"))
))]
use core::mem::MaybeUninit;
#[cfg(not(target_os = "wasi"))]
use {crate::io, crate::pid::Pid};
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
use {
crate::termios::{Action, OptionalActions, QueueSelector, Termios, Winsize},
crate::utils::as_mut_ptr,
};
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
pub(crate) fn tcgetattr(fd: BorrowedFd<'_>) -> io::Result<Termios> {
// On Linux, use `TCGETS2`, and fall back to `TCGETS` if needed.
#[cfg(linux_kernel)]
{
use crate::termios::{ControlModes, InputModes, LocalModes, OutputModes, SpecialCodes};
let mut termios2 = MaybeUninit::<c::termios2>::uninit();
let ptr = termios2.as_mut_ptr();
// SAFETY: This invokes the `TCGETS2` ioctl, which initializes the full
// `Termios` structure.
let termios2 = unsafe {
match ret(c::ioctl(borrowed_fd(fd), c::TCGETS2 as _, ptr)) {
Ok(()) => {}
// A `NOTTY` or `ACCESS` might mean the OS doesn't support
// `TCGETS2`, for example a seccomp environment or WSL that
// only knows about `TCGETS`. Fall back to the old `TCGETS`.
#[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
Err(io::Errno::NOTTY) | Err(io::Errno::ACCESS) => {
tcgetattr_fallback(fd, &mut termios2)?
}
Err(err) => return Err(err),
}
// Now all the fields are set.
termios2.assume_init()
};
// Convert from the Linux `termios2` to our `Termios`.
let mut result = Termios {
input_modes: InputModes::from_bits_retain(termios2.c_iflag),
output_modes: OutputModes::from_bits_retain(termios2.c_oflag),
control_modes: ControlModes::from_bits_retain(termios2.c_cflag),
local_modes: LocalModes::from_bits_retain(termios2.c_lflag),
line_discipline: termios2.c_line,
special_codes: SpecialCodes(Default::default()),
// On PowerPC musl targets, `c_ispeed`/`c_ospeed` are named
// `__c_ispeed`/`__c_ospeed`.
#[cfg(not(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
)))]
input_speed: termios2.c_ispeed,
#[cfg(not(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
)))]
output_speed: termios2.c_ospeed,
#[cfg(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
input_speed: termios2.__c_ispeed,
#[cfg(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
output_speed: termios2.__c_ospeed,
};
// Copy in the control codes, since libc's `c_cc` array may have a
// different length from the ioctl's.
let nccs = termios2.c_cc.len();
result.special_codes.0[..nccs].copy_from_slice(&termios2.c_cc);
Ok(result)
}
#[cfg(not(linux_kernel))]
unsafe {
let mut result = MaybeUninit::<Termios>::uninit();
// `result` is a `Termios` which starts with the same layout as
// `c::termios`, so we can cast the pointer.
ret(c::tcgetattr(borrowed_fd(fd), result.as_mut_ptr().cast()))?;
Ok(result.assume_init())
}
}
/// Implement `tcgetattr` using the old `TCGETS` ioctl.
#[cfg(all(
linux_kernel,
not(any(target_arch = "powerpc", target_arch = "powerpc64"))
))]
#[cold]
fn tcgetattr_fallback(
fd: BorrowedFd<'_>,
termios: &mut MaybeUninit<c::termios2>,
) -> io::Result<()> {
use crate::termios::speed;
use core::ptr::{addr_of, addr_of_mut};
// SAFETY: This invokes the `TCGETS` ioctl, which, if it succeeds,
// initializes the `Termios` structure except for the `input_speed` and
// `output_speed` fields, which we manually initialize before forming a
// reference to the full `Termios`.
unsafe {
let ptr = termios.as_mut_ptr();
// Do the old `TCGETS` call, which doesn't initialize `input_speed` or
// `output_speed`.
ret(c::ioctl(borrowed_fd(fd), c::TCGETS as _, ptr))?;
// Read the `control_modes` field without forming a reference to the
// `Termios` because it isn't fully initialized yet.
let control_modes = addr_of!((*ptr).c_cflag).read();
// Infer `output_speed`.
let encoded_out = control_modes & c::CBAUD;
let output_speed = match speed::decode(encoded_out) {
Some(output_speed) => output_speed,
None => return Err(io::Errno::RANGE),
};
addr_of_mut!((*ptr).c_ospeed).write(output_speed);
// Infer `input_speed`. For input speeds, `B0` is special-cased to mean
// the input speed is the same as the output speed.
let encoded_in = (control_modes & c::CIBAUD) >> c::IBSHIFT;
let input_speed = if encoded_in == c::B0 {
output_speed
} else {
match speed::decode(encoded_in) {
Some(input_speed) => input_speed,
None => return Err(io::Errno::RANGE),
}
};
addr_of_mut!((*ptr).c_ispeed).write(input_speed);
}
Ok(())
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result<Pid> {
unsafe {
let pid = ret_pid_t(c::tcgetpgrp(borrowed_fd(fd)))?;
// This doesn't appear to be documented, but on Linux, it appears
// `tcsetpgrp` can succeed and set the pid to 0 if we pass it a
// pseudo-terminal device fd. For now, translate it into `OPNOTSUPP`.
#[cfg(linux_kernel)]
if pid == 0 {
return Err(io::Errno::OPNOTSUPP);
}
Ok(Pid::from_raw_unchecked(pid))
}
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcsetpgrp(fd: BorrowedFd<'_>, pid: Pid) -> io::Result<()> {
unsafe { ret(c::tcsetpgrp(borrowed_fd(fd), pid.as_raw_nonzero().get())) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
pub(crate) fn tcsetattr(
fd: BorrowedFd<'_>,
optional_actions: OptionalActions,
termios: &Termios,
) -> io::Result<()> {
// On Linux, use `TCSETS2`, and fall back to `TCSETS` if needed.
#[cfg(linux_kernel)]
{
use crate::termios::speed;
let output_speed = termios.output_speed();
let input_speed = termios.input_speed();
let mut termios2 = c::termios2 {
c_iflag: termios.input_modes.bits(),
c_oflag: termios.output_modes.bits(),
c_cflag: termios.control_modes.bits(),
c_lflag: termios.local_modes.bits(),
c_line: termios.line_discipline,
c_cc: Default::default(),
// On PowerPC musl targets, `c_ispeed`/`c_ospeed` are named
// `__c_ispeed`/`__c_ospeed`.
#[cfg(not(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
)))]
c_ispeed: input_speed,
#[cfg(not(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
)))]
c_ospeed: output_speed,
#[cfg(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
__c_ispeed: input_speed,
#[cfg(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
__c_ospeed: output_speed,
};
// Ensure that our input and output speeds are set, as `libc`
// routines don't always support setting these separately.
termios2.c_cflag &= !c::CBAUD;
termios2.c_cflag |= speed::encode(output_speed).unwrap_or(c::BOTHER);
termios2.c_cflag &= !c::CIBAUD;
termios2.c_cflag |= speed::encode(input_speed).unwrap_or(c::BOTHER) << c::IBSHIFT;
// Copy in the control codes, since libc's `c_cc` array may have a
// different length from the ioctl's.
let nccs = termios2.c_cc.len();
termios2
.c_cc
.copy_from_slice(&termios.special_codes.0[..nccs]);
// Translate from `optional_actions` into a `TCSETS2` ioctl request
// code. On MIPS, `optional_actions` has `TCSETS` added to it.
let request = c::TCSETS2 as c::c_ulong
+ if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
)) {
optional_actions as c::c_ulong - c::TCSETS as c::c_ulong
} else {
optional_actions as c::c_ulong
};
// SAFETY: This invokes the `TCSETS2` ioctl.
unsafe {
match ret(c::ioctl(borrowed_fd(fd), request as _, &termios2)) {
Ok(()) => Ok(()),
// Similar to `tcgetattr_fallback`, `NOTTY` or `ACCESS` might
// mean the OS doesn't support `TCSETS2`. Fall back to the old
// `TCSETS`.
#[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
Err(io::Errno::NOTTY) | Err(io::Errno::ACCESS) => {
tcsetattr_fallback(fd, optional_actions, &termios2)
}
Err(err) => Err(err),
}
}
}
#[cfg(not(linux_kernel))]
unsafe {
ret(c::tcsetattr(
borrowed_fd(fd),
optional_actions as _,
crate::utils::as_ptr(termios).cast(),
))
}
}
/// Implement `tcsetattr` using the old `TCSETS` ioctl.
#[cfg(all(
linux_kernel,
not(any(target_arch = "powerpc", target_arch = "powerpc64"))
))]
#[cold]
fn tcsetattr_fallback(
fd: BorrowedFd<'_>,
optional_actions: OptionalActions,
termios2: &c::termios2,
) -> io::Result<()> {
// `TCSETS` silently accepts `BOTHER` in `c_cflag` even though it doesn't
// read `c_ispeed`/`c_ospeed`, so detect this case and fail if needed.
let encoded_out = termios2.c_cflag & c::CBAUD;
let encoded_in = (termios2.c_cflag & c::CIBAUD) >> c::IBSHIFT;
if encoded_out == c::BOTHER || encoded_in == c::BOTHER {
return Err(io::Errno::RANGE);
}
// Translate from `optional_actions` into a `TCSETS` ioctl request code. On
// MIPS, `optional_actions` already has `TCSETS` added to it.
let request = if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
)) {
optional_actions as c::c_ulong
} else {
optional_actions as c::c_ulong + c::TCSETS as c::c_ulong
};
// SAFETY: This invokes the `TCSETS` ioctl.
unsafe { ret(c::ioctl(borrowed_fd(fd), request as _, termios2)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcsendbreak(fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe { ret(c::tcsendbreak(borrowed_fd(fd), 0)) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
pub(crate) fn tcdrain(fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe { ret(c::tcdrain(borrowed_fd(fd))) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
pub(crate) fn tcflush(fd: BorrowedFd<'_>, queue_selector: QueueSelector) -> io::Result<()> {
unsafe { ret(c::tcflush(borrowed_fd(fd), queue_selector as _)) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
pub(crate) fn tcflow(fd: BorrowedFd<'_>, action: Action) -> io::Result<()> {
unsafe { ret(c::tcflow(borrowed_fd(fd), action as _)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcgetsid(fd: BorrowedFd<'_>) -> io::Result<Pid> {
unsafe {
let pid = ret_pid_t(c::tcgetsid(borrowed_fd(fd)))?;
Ok(Pid::from_raw_unchecked(pid))
}
}
#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "wasi")))]
pub(crate) fn tcsetwinsize(fd: BorrowedFd<'_>, winsize: Winsize) -> io::Result<()> {
unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCSWINSZ, &winsize)) }
}
#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "wasi")))]
pub(crate) fn tcgetwinsize(fd: BorrowedFd<'_>) -> io::Result<Winsize> {
unsafe {
let mut buf = MaybeUninit::<Winsize>::uninit();
ret(c::ioctl(
borrowed_fd(fd),
c::TIOCGWINSZ.into(),
buf.as_mut_ptr(),
))?;
Ok(buf.assume_init())
}
}
#[cfg(not(any(target_os = "espidf", target_os = "nto", target_os = "wasi")))]
#[inline]
pub(crate) fn set_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> {
#[cfg(bsd)]
let encoded_speed = arbitrary_speed;
#[cfg(not(bsd))]
let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) {
Some(encoded_speed) => encoded_speed,
#[cfg(linux_kernel)]
None => c::BOTHER,
#[cfg(not(linux_kernel))]
None => return Err(io::Errno::INVAL),
};
#[cfg(not(linux_kernel))]
unsafe {
ret(c::cfsetspeed(
as_mut_ptr(termios).cast(),
encoded_speed.into(),
))
}
// Linux libc implementations don't support arbitrary speeds, so we encode
// the speed manually.
#[cfg(linux_kernel)]
{
use crate::termios::ControlModes;
debug_assert_eq!(encoded_speed & !c::CBAUD, 0);
termios.control_modes -= ControlModes::from_bits_retain(c::CBAUD | c::CIBAUD);
termios.control_modes |=
ControlModes::from_bits_retain(encoded_speed | (encoded_speed << c::IBSHIFT));
termios.input_speed = arbitrary_speed;
termios.output_speed = arbitrary_speed;
Ok(())
}
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
#[inline]
pub(crate) fn set_output_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> {
#[cfg(bsd)]
let encoded_speed = arbitrary_speed;
#[cfg(not(bsd))]
let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) {
Some(encoded_speed) => encoded_speed,
#[cfg(linux_kernel)]
None => c::BOTHER,
#[cfg(not(linux_kernel))]
None => return Err(io::Errno::INVAL),
};
#[cfg(not(linux_kernel))]
unsafe {
ret(c::cfsetospeed(
as_mut_ptr(termios).cast(),
encoded_speed.into(),
))
}
// Linux libc implementations don't support arbitrary speeds or setting the
// input and output speeds separately, so we encode the speed manually.
#[cfg(linux_kernel)]
{
use crate::termios::ControlModes;
debug_assert_eq!(encoded_speed & !c::CBAUD, 0);
termios.control_modes -= ControlModes::from_bits_retain(c::CBAUD);
termios.control_modes |= ControlModes::from_bits_retain(encoded_speed);
termios.output_speed = arbitrary_speed;
Ok(())
}
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
#[inline]
pub(crate) fn set_input_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> {
#[cfg(bsd)]
let encoded_speed = arbitrary_speed;
#[cfg(not(bsd))]
let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) {
Some(encoded_speed) => encoded_speed,
#[cfg(linux_kernel)]
None => c::BOTHER,
#[cfg(not(linux_kernel))]
None => return Err(io::Errno::INVAL),
};
#[cfg(not(linux_kernel))]
unsafe {
ret(c::cfsetispeed(
as_mut_ptr(termios).cast(),
encoded_speed.into(),
))
}
// Linux libc implementations don't support arbitrary speeds or setting the
// input and output speeds separately, so we encode the speed manually.
#[cfg(linux_kernel)]
{
use crate::termios::ControlModes;
debug_assert_eq!(encoded_speed & !c::CBAUD, 0);
termios.control_modes -= ControlModes::from_bits_retain(c::CIBAUD);
termios.control_modes |= ControlModes::from_bits_retain(encoded_speed << c::IBSHIFT);
termios.input_speed = arbitrary_speed;
Ok(())
}
}
#[cfg(not(any(target_os = "espidf", target_os = "nto", target_os = "wasi")))]
#[inline]
pub(crate) fn cfmakeraw(termios: &mut Termios) {
unsafe {
// On AIX, cfmakeraw() has a return type of 'int' instead of 'void'.
// If the argument 'termios' is NULL, it returns -1; otherwise, it returns 0.
// We believe it is safe to ignore the return value.
#[cfg(target_os = "aix")]
{
let _ = c::cfmakeraw(as_mut_ptr(termios).cast());
}
#[cfg(not(target_os = "aix"))]
{
c::cfmakeraw(as_mut_ptr(termios).cast());
}
}
}
pub(crate) fn isatty(fd: BorrowedFd<'_>) -> bool {
// Use the return value of `isatty` alone. We don't check `errno` because
// we return `bool` rather than `io::Result<bool>`, because we assume
// `BorrowedFd` protects us from `EBADF`, and any other reasonably
// anticipated `errno` value would end up interpreted as “assume it's not a
// terminal” anyway.
unsafe { c::isatty(borrowed_fd(fd)) != 0 }
}
#[cfg(feature = "alloc")]
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
pub(crate) fn ttyname(dirfd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
unsafe {
// `ttyname_r` returns its error status rather than using `errno`.
match c::ttyname_r(borrowed_fd(dirfd), buf.as_mut_ptr().cast(), buf.len()) {
0 => Ok(CStr::from_ptr(buf.as_ptr().cast()).to_bytes().len()),
err => Err(io::Errno::from_raw_os_error(err)),
}
}
}

View File

@@ -0,0 +1,17 @@
//! Types for the `termios` module.
#![allow(non_camel_case_types)]
#[cfg(not(any(target_os = "espidf", target_os = "redox")))]
use crate::ffi;
// We don't want to use `tcflag_t` directly so we don't expose libc
// publicly. Redox uses `u32`, apple uses `c_ulong`, everything else
// seems to use `c_uint`.
#[cfg(apple)]
pub type tcflag_t = ffi::c_ulong;
#[cfg(target_os = "redox")]
pub type tcflag_t = u32;
#[cfg(not(any(apple, target_os = "espidf", target_os = "redox", target_os = "wasi")))]
pub type tcflag_t = ffi::c_uint;

View File

@@ -0,0 +1,68 @@
//! Rust implementation of the `CPU_*` macro API.
#![allow(non_snake_case)]
use super::types::{RawCpuSet, CPU_SETSIZE};
use crate::backend::c;
#[inline]
pub(crate) fn CPU_SET(cpu: usize, cpuset: &mut RawCpuSet) {
assert!(
cpu < CPU_SETSIZE,
"cpu out of bounds: the cpu max is {} but the cpu is {}",
CPU_SETSIZE,
cpu
);
unsafe { c::CPU_SET(cpu, cpuset) }
}
#[inline]
pub(crate) fn CPU_ZERO(cpuset: &mut RawCpuSet) {
unsafe { c::CPU_ZERO(cpuset) }
}
#[inline]
pub(crate) fn CPU_CLR(cpu: usize, cpuset: &mut RawCpuSet) {
assert!(
cpu < CPU_SETSIZE,
"cpu out of bounds: the cpu max is {} but the cpu is {}",
CPU_SETSIZE,
cpu
);
unsafe { c::CPU_CLR(cpu, cpuset) }
}
#[inline]
pub(crate) fn CPU_ISSET(cpu: usize, cpuset: &RawCpuSet) -> bool {
assert!(
cpu < CPU_SETSIZE,
"cpu out of bounds: the cpu max is {} but the cpu is {}",
CPU_SETSIZE,
cpu
);
unsafe { c::CPU_ISSET(cpu, cpuset) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn CPU_COUNT(cpuset: &RawCpuSet) -> u32 {
unsafe { c::CPU_COUNT(cpuset).try_into().unwrap() }
}
#[inline]
pub(crate) fn CPU_EQUAL(this: &RawCpuSet, that: &RawCpuSet) -> bool {
#[cfg(any(linux_like, target_os = "fuchsia", target_os = "hurd"))]
unsafe {
c::CPU_EQUAL(this, that)
}
#[cfg(not(any(linux_like, target_os = "fuchsia", target_os = "hurd")))]
unsafe {
for i in 0..c::CPU_SETSIZE as usize {
if c::CPU_ISSET(i, this) != c::CPU_ISSET(i, that) {
return false;
}
}
true
}
}

View File

@@ -0,0 +1,91 @@
use crate::backend::c;
bitflags::bitflags! {
/// `FUTEX_*` flags for use with the functions in [`futex`].
///
/// [`futex`]: mod@crate::thread::futex
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Flags: u32 {
/// `FUTEX_PRIVATE_FLAG`
const PRIVATE = bitcast!(c::FUTEX_PRIVATE_FLAG);
/// `FUTEX_CLOCK_REALTIME`
const CLOCK_REALTIME = bitcast!(c::FUTEX_CLOCK_REALTIME);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags::bitflags! {
/// `FUTEX2_*` flags for use with the functions in [`Waitv`].
///
/// Not to be confused with [`WaitvFlags`], which is passed as an argument
/// to the `waitv` function.
///
/// [`Waitv`]: crate::thread::futex::Waitv
/// [`WaitvFlags`]: crate::thread::futex::WaitvFlags
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct WaitFlags: u32 {
/// `FUTEX_U8`
const SIZE_U8 = linux_raw_sys::general::FUTEX2_SIZE_U8;
/// `FUTEX_U16`
const SIZE_U16 = linux_raw_sys::general::FUTEX2_SIZE_U16;
/// `FUTEX_U32`
const SIZE_U32 = linux_raw_sys::general::FUTEX2_SIZE_U32;
/// `FUTEX_U64`
const SIZE_U64 = linux_raw_sys::general::FUTEX2_SIZE_U64;
/// `FUTEX_SIZE_MASK`
const SIZE_MASK = linux_raw_sys::general::FUTEX2_SIZE_MASK;
/// `FUTEX2_NUMA`
const NUMA = linux_raw_sys::general::FUTEX2_NUMA;
/// `FUTEX2_PRIVATE`
const PRIVATE = linux_raw_sys::general::FUTEX2_PRIVATE;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
/// `FUTEX_*` operations for use with the futex syscall wrappers.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
pub(crate) enum Operation {
/// `FUTEX_WAIT`
Wait = bitcast!(c::FUTEX_WAIT),
/// `FUTEX_WAKE`
Wake = bitcast!(c::FUTEX_WAKE),
/// `FUTEX_FD`
Fd = bitcast!(c::FUTEX_FD),
/// `FUTEX_REQUEUE`
Requeue = bitcast!(c::FUTEX_REQUEUE),
/// `FUTEX_CMP_REQUEUE`
CmpRequeue = bitcast!(c::FUTEX_CMP_REQUEUE),
/// `FUTEX_WAKE_OP`
WakeOp = bitcast!(c::FUTEX_WAKE_OP),
/// `FUTEX_LOCK_PI`
LockPi = bitcast!(c::FUTEX_LOCK_PI),
/// `FUTEX_UNLOCK_PI`
UnlockPi = bitcast!(c::FUTEX_UNLOCK_PI),
/// `FUTEX_TRYLOCK_PI`
TrylockPi = bitcast!(c::FUTEX_TRYLOCK_PI),
/// `FUTEX_WAIT_BITSET`
WaitBitset = bitcast!(c::FUTEX_WAIT_BITSET),
/// `FUTEX_WAKE_BITSET`
WakeBitset = bitcast!(c::FUTEX_WAKE_BITSET),
/// `FUTEX_WAIT_REQUEUE_PI`
WaitRequeuePi = bitcast!(c::FUTEX_WAIT_REQUEUE_PI),
/// `FUTEX_CMP_REQUEUE_PI`
CmpRequeuePi = bitcast!(c::FUTEX_CMP_REQUEUE_PI),
/// `FUTEX_LOCK_PI2`
LockPi2 = bitcast!(c::FUTEX_LOCK_PI2),
}
/// `FUTEX_WAITERS`
pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS;
/// `FUTEX_OWNER_DIED`
pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED;

View File

@@ -0,0 +1,7 @@
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
pub(crate) mod cpu_set;
#[cfg(linux_kernel)]
pub(crate) mod futex;
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,792 @@
//! libc syscalls supporting `rustix::thread`.
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
use super::types::RawCpuSet;
use crate::backend::c;
use crate::backend::conv::ret;
use crate::io;
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
use crate::pid::Pid;
#[cfg(not(any(
apple,
freebsdlike,
target_os = "emscripten",
target_os = "espidf",
target_os = "haiku",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
use crate::thread::ClockId;
#[cfg(linux_kernel)]
use crate::thread::{Cpuid, MembarrierCommand, MembarrierQuery};
#[cfg(not(target_os = "redox"))]
use crate::thread::{NanosleepRelativeResult, Timespec};
#[cfg(all(target_env = "gnu", fix_y2038))]
use crate::timespec::LibcTimespec;
#[cfg(not(fix_y2038))]
use crate::timespec::{as_libc_timespec_mut_ptr, as_libc_timespec_ptr};
#[cfg(linux_kernel)]
use crate::utils::option_as_ptr;
use core::mem::MaybeUninit;
#[cfg(linux_kernel)]
use core::sync::atomic::AtomicU32;
#[cfg(linux_kernel)]
use {
crate::backend::conv::{borrowed_fd, ret_c_int, ret_u32, ret_usize},
crate::fd::BorrowedFd,
crate::thread::futex,
crate::utils::as_mut_ptr,
};
#[cfg(all(target_env = "gnu", fix_y2038))]
weak!(fn __clock_nanosleep_time64(c::clockid_t, c::c_int, *const LibcTimespec, *mut LibcTimespec) -> c::c_int);
#[cfg(all(target_env = "gnu", fix_y2038))]
weak!(fn __nanosleep64(*const LibcTimespec, *mut LibcTimespec) -> c::c_int);
#[cfg(not(any(
apple,
target_os = "dragonfly",
target_os = "emscripten",
target_os = "espidf",
target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
target_os = "haiku",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
pub(crate) fn clock_nanosleep_relative(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
// Old 32-bit version: libc has `clock_nanosleep` but it is not y2038 safe
// by default. But there may be a `__clock_nanosleep_time64` we can use.
#[cfg(fix_y2038)]
{
#[cfg(target_env = "gnu")]
if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
let flags = 0;
let mut remain = MaybeUninit::<LibcTimespec>::uninit();
unsafe {
return match libc_clock_nanosleep(
id as c::clockid_t,
flags,
&request.clone().into(),
remain.as_mut_ptr(),
) {
0 => NanosleepRelativeResult::Ok,
err if err == io::Errno::INTR.0 => {
NanosleepRelativeResult::Interrupted(remain.assume_init().into())
}
err => NanosleepRelativeResult::Err(io::Errno(err)),
};
}
}
clock_nanosleep_relative_old(id, request)
}
// Main version: libc is y2038 safe and has `clock_nanosleep`.
#[cfg(not(fix_y2038))]
unsafe {
let flags = 0;
let mut remain = MaybeUninit::<Timespec>::uninit();
match c::clock_nanosleep(
id as c::clockid_t,
flags,
as_libc_timespec_ptr(request),
as_libc_timespec_mut_ptr(&mut remain),
) {
0 => NanosleepRelativeResult::Ok,
err if err == io::Errno::INTR.0 => {
NanosleepRelativeResult::Interrupted(remain.assume_init())
}
err => NanosleepRelativeResult::Err(io::Errno(err)),
}
}
}
#[cfg(all(
fix_y2038,
not(any(
apple,
target_os = "emscripten",
target_os = "haiku",
target_os = "horizon",
target_os = "vita"
))
))]
fn clock_nanosleep_relative_old(
id: crate::clockid::ClockId,
request: &Timespec,
) -> NanosleepRelativeResult {
let tv_sec = match request.tv_sec.try_into() {
Ok(tv_sec) => tv_sec,
Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
};
let tv_nsec = match request.tv_nsec.try_into() {
Ok(tv_nsec) => tv_nsec,
Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
};
let old_request = c::timespec { tv_sec, tv_nsec };
let mut old_remain = MaybeUninit::<c::timespec>::uninit();
let flags = 0;
unsafe {
match c::clock_nanosleep(
id as c::clockid_t,
flags,
&old_request,
old_remain.as_mut_ptr(),
) {
0 => NanosleepRelativeResult::Ok,
err if err == io::Errno::INTR.0 => {
let old_remain = old_remain.assume_init();
let remain = Timespec {
tv_sec: old_remain.tv_sec.into(),
tv_nsec: old_remain.tv_nsec.into(),
};
NanosleepRelativeResult::Interrupted(remain)
}
err => NanosleepRelativeResult::Err(io::Errno(err)),
}
}
}
#[cfg(not(any(
apple,
target_os = "dragonfly",
target_os = "emscripten",
target_os = "espidf",
target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
target_os = "haiku",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
pub(crate) fn clock_nanosleep_absolute(id: ClockId, request: &Timespec) -> io::Result<()> {
// Old 32-bit version: libc has `clock_nanosleep` but it is not y2038 safe
// by default. But there may be a `__clock_nanosleep_time64` we can use.
#[cfg(fix_y2038)]
{
#[cfg(target_env = "gnu")]
if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
let flags = c::TIMER_ABSTIME;
unsafe {
return match libc_clock_nanosleep(
id as c::clockid_t,
flags,
&request.clone().into(),
core::ptr::null_mut(),
) {
0 => Ok(()),
err => Err(io::Errno(err)),
};
}
}
clock_nanosleep_absolute_old(id, request)
}
// Main version: libc is y2038 safe and has `clock_nanosleep`.
#[cfg(not(fix_y2038))]
{
let flags = c::TIMER_ABSTIME;
match unsafe {
c::clock_nanosleep(
id as c::clockid_t,
flags as _,
as_libc_timespec_ptr(request),
core::ptr::null_mut(),
)
} {
0 => Ok(()),
err => Err(io::Errno(err)),
}
}
}
#[cfg(all(
fix_y2038,
not(any(
apple,
target_os = "emscripten",
target_os = "haiku",
target_os = "horizon",
target_os = "vita"
))
))]
fn clock_nanosleep_absolute_old(id: crate::clockid::ClockId, request: &Timespec) -> io::Result<()> {
let flags = c::TIMER_ABSTIME;
let old_request = c::timespec {
tv_sec: request.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: request.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
};
match unsafe {
c::clock_nanosleep(
id as c::clockid_t,
flags,
&old_request,
core::ptr::null_mut(),
)
} {
0 => Ok(()),
err => Err(io::Errno(err)),
}
}
#[cfg(not(target_os = "redox"))]
#[inline]
pub(crate) fn nanosleep(request: &Timespec) -> NanosleepRelativeResult {
// Old 32-bit version: libc has `nanosleep` but it is not y2038 safe by
// default. But there may be a `__nanosleep64` we can use.
#[cfg(fix_y2038)]
{
#[cfg(target_env = "gnu")]
if let Some(libc_nanosleep) = __nanosleep64.get() {
let mut remain = MaybeUninit::<LibcTimespec>::uninit();
unsafe {
return match ret(libc_nanosleep(&request.clone().into(), remain.as_mut_ptr())) {
Ok(()) => NanosleepRelativeResult::Ok,
Err(io::Errno::INTR) => {
NanosleepRelativeResult::Interrupted(remain.assume_init().into())
}
Err(err) => NanosleepRelativeResult::Err(err),
};
}
}
nanosleep_old(request)
}
// Main version: libc is y2038 safe and has `nanosleep`.
#[cfg(not(fix_y2038))]
unsafe {
let mut remain = MaybeUninit::<Timespec>::uninit();
match ret(c::nanosleep(
as_libc_timespec_ptr(request),
as_libc_timespec_mut_ptr(&mut remain),
)) {
Ok(()) => NanosleepRelativeResult::Ok,
Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(remain.assume_init()),
Err(err) => NanosleepRelativeResult::Err(err),
}
}
}
#[cfg(fix_y2038)]
fn nanosleep_old(request: &Timespec) -> NanosleepRelativeResult {
let tv_sec = match request.tv_sec.try_into() {
Ok(tv_sec) => tv_sec,
Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
};
let tv_nsec = match request.tv_nsec.try_into() {
Ok(tv_nsec) => tv_nsec,
Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
};
let old_request = c::timespec { tv_sec, tv_nsec };
let mut old_remain = MaybeUninit::<c::timespec>::uninit();
unsafe {
match ret(c::nanosleep(&old_request, old_remain.as_mut_ptr())) {
Ok(()) => NanosleepRelativeResult::Ok,
Err(io::Errno::INTR) => {
let old_remain = old_remain.assume_init();
let remain = Timespec {
tv_sec: old_remain.tv_sec.into(),
tv_nsec: old_remain.tv_nsec.into(),
};
NanosleepRelativeResult::Interrupted(remain)
}
Err(err) => NanosleepRelativeResult::Err(err),
}
}
}
#[cfg(linux_kernel)]
#[inline]
#[must_use]
pub(crate) fn gettid() -> Pid {
// `gettid` wasn't supported in glibc until 2.30, and musl until 1.2.2,
// so use `syscall`.
// <https://sourceware.org/bugzilla/show_bug.cgi?id=6399#c62>
weak_or_syscall! {
fn gettid() via SYS_gettid -> c::pid_t
}
unsafe {
let tid = gettid();
Pid::from_raw_unchecked(tid)
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result<c::c_int> {
// `setns` wasn't supported in glibc until 2.14, and musl until 0.9.5,
// so use `syscall`.
weak_or_syscall! {
fn setns(fd: c::c_int, nstype: c::c_int) via SYS_setns -> c::c_int
}
unsafe { ret_c_int(setns(borrowed_fd(fd), nstype)) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) unsafe fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> {
ret(c::unshare(flags.bits() as i32))
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn capget(
header: &mut linux_raw_sys::general::__user_cap_header_struct,
data: &mut [MaybeUninit<linux_raw_sys::general::__user_cap_data_struct>],
) -> io::Result<()> {
syscall! {
fn capget(
hdrp: *mut linux_raw_sys::general::__user_cap_header_struct,
data: *mut linux_raw_sys::general::__user_cap_data_struct
) via SYS_capget -> c::c_int
}
unsafe {
ret(capget(
as_mut_ptr(header),
data.as_mut_ptr()
.cast::<linux_raw_sys::general::__user_cap_data_struct>(),
))
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn capset(
header: &mut linux_raw_sys::general::__user_cap_header_struct,
data: &[linux_raw_sys::general::__user_cap_data_struct],
) -> io::Result<()> {
syscall! {
fn capset(
hdrp: *mut linux_raw_sys::general::__user_cap_header_struct,
data: *const linux_raw_sys::general::__user_cap_data_struct
) via SYS_capset -> c::c_int
}
unsafe { ret(capset(as_mut_ptr(header), data.as_ptr())) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn setuid_thread(uid: crate::ugid::Uid) -> io::Result<()> {
syscall! {
fn setuid(uid: c::uid_t) via SYS_setuid -> c::c_int
}
unsafe { ret(setuid(uid.as_raw())) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn setresuid_thread(
ruid: Option<crate::ugid::Uid>,
euid: Option<crate::ugid::Uid>,
suid: Option<crate::ugid::Uid>,
) -> io::Result<()> {
#[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))]
const SYS: c::c_long = c::SYS_setresuid32 as c::c_long;
#[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))]
const SYS: c::c_long = c::SYS_setresuid as c::c_long;
syscall! {
fn setresuid(ruid: c::uid_t, euid: c::uid_t, suid: c::uid_t) via SYS -> c::c_int
}
unsafe {
ret(setresuid(
ruid.map_or(-1_i32 as u32, |x| x.as_raw()),
euid.map_or(-1_i32 as u32, |x| x.as_raw()),
suid.map_or(-1_i32 as u32, |x| x.as_raw()),
))
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn setgid_thread(gid: crate::ugid::Gid) -> io::Result<()> {
syscall! {
fn setgid(gid: c::gid_t) via SYS_setgid -> c::c_int
}
unsafe { ret(setgid(gid.as_raw())) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn setresgid_thread(
rgid: Option<crate::ugid::Gid>,
egid: Option<crate::ugid::Gid>,
sgid: Option<crate::ugid::Gid>,
) -> io::Result<()> {
#[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))]
const SYS: c::c_long = c::SYS_setresgid32 as c::c_long;
#[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))]
const SYS: c::c_long = c::SYS_setresgid as c::c_long;
syscall! {
fn setresgid(rgid: c::gid_t, egid: c::gid_t, sgid: c::gid_t) via SYS -> c::c_int
}
unsafe {
ret(setresgid(
rgid.map_or(-1_i32 as u32, |x| x.as_raw()),
egid.map_or(-1_i32 as u32, |x| x.as_raw()),
sgid.map_or(-1_i32 as u32, |x| x.as_raw()),
))
}
}
/// # Safety
///
/// The raw pointers must point to valid aligned memory.
#[cfg(linux_kernel)]
pub(crate) unsafe fn futex_val2(
uaddr: *const AtomicU32,
op: super::futex::Operation,
flags: futex::Flags,
val: u32,
val2: u32,
uaddr2: *const AtomicU32,
val3: u32,
) -> io::Result<usize> {
// Pass `val2` in the least-significant bytes of the `timeout` argument.
// [“the kernel casts the timeout value first to unsigned long, then to
// uint32_t”], so we perform that exact conversion in reverse to create
// the pointer.
//
// [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html
let timeout = val2 as usize as *const Timespec;
#[cfg(all(
target_pointer_width = "32",
not(any(target_arch = "aarch64", target_arch = "x86_64"))
))]
{
// TODO: Upstream this to the libc crate.
#[allow(non_upper_case_globals)]
const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32;
syscall! {
fn futex_time64(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex_time64 -> c::ssize_t
}
ret_usize(futex_time64(
uaddr,
op as i32 | flags.bits() as i32,
val,
timeout,
uaddr2,
val3,
))
}
#[cfg(any(
target_pointer_width = "64",
target_arch = "aarch64",
target_arch = "x86_64"
))]
{
syscall! {
fn futex(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex -> c::c_long
}
ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
timeout.cast(),
uaddr2,
val3,
) as isize)
}
}
/// # Safety
///
/// The raw pointers must point to valid aligned memory.
#[cfg(linux_kernel)]
pub(crate) unsafe fn futex_timeout(
uaddr: *const AtomicU32,
op: super::futex::Operation,
flags: futex::Flags,
val: u32,
timeout: Option<&Timespec>,
uaddr2: *const AtomicU32,
val3: u32,
) -> io::Result<usize> {
#[cfg(all(
target_pointer_width = "32",
not(any(target_arch = "aarch64", target_arch = "x86_64"))
))]
{
// TODO: Upstream this to the libc crate.
#[allow(non_upper_case_globals)]
const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32;
syscall! {
fn futex_time64(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex_time64 -> c::ssize_t
}
ret_usize(futex_time64(
uaddr,
op as i32 | flags.bits() as i32,
val,
option_as_ptr(timeout),
uaddr2,
val3,
))
.or_else(|err| {
// See the comments in `clock_gettime_via_syscall` about emulation.
if err == io::Errno::NOSYS {
futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3)
} else {
Err(err)
}
})
}
#[cfg(any(
target_pointer_width = "64",
target_arch = "aarch64",
target_arch = "x86_64"
))]
{
syscall! {
fn futex(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex -> c::c_long
}
ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
option_as_ptr(timeout).cast(),
uaddr2,
val3,
) as isize)
}
}
/// # Safety
///
/// The raw pointers must point to valid aligned memory.
#[cfg(linux_kernel)]
#[cfg(all(
target_pointer_width = "32",
not(any(target_arch = "aarch64", target_arch = "x86_64"))
))]
unsafe fn futex_old_timespec(
uaddr: *const AtomicU32,
op: super::futex::Operation,
flags: futex::Flags,
val: u32,
timeout: Option<&Timespec>,
uaddr2: *const AtomicU32,
val3: u32,
) -> io::Result<usize> {
syscall! {
fn futex(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const linux_raw_sys::general::__kernel_old_timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex -> c::c_long
}
let old_timeout = if let Some(timeout) = timeout {
Some(linux_raw_sys::general::__kernel_old_timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_nsec: timeout.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
})
} else {
None
};
ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
option_as_ptr(old_timeout.as_ref()),
uaddr2,
val3,
) as isize)
}
#[cfg(linux_kernel)]
pub(crate) fn futex_waitv(
waiters: &[futex::Wait],
flags: futex::WaitvFlags,
timeout: Option<&Timespec>,
clockid: ClockId,
) -> io::Result<usize> {
use futex::Wait as FutexWait;
use linux_raw_sys::general::__kernel_clockid_t as clockid_t;
syscall! {
fn futex_waitv(
waiters: *const FutexWait,
nr_futexes: c::c_uint,
flags: c::c_uint,
timeout: *const Timespec,
clockid: clockid_t
) via SYS_futex_waitv -> c::c_int
}
let nr_futexes: c::c_uint = waiters.len().try_into().map_err(|_| io::Errno::INVAL)?;
unsafe {
ret_c_int(futex_waitv(
waiters.as_ptr(),
nr_futexes,
flags.bits(),
option_as_ptr(timeout).cast(),
clockid as _,
))
.map(|n| n as usize)
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn setgroups_thread(groups: &[crate::ugid::Gid]) -> io::Result<()> {
syscall! {
fn setgroups(size: c::size_t, list: *const c::gid_t) via SYS_setgroups -> c::c_int
}
ret(unsafe { setgroups(groups.len(), groups.as_ptr().cast()) })
}
#[cfg(any(linux_kernel, target_os = "dragonfly"))]
#[inline]
pub(crate) fn sched_getcpu() -> usize {
let r = unsafe { c::sched_getcpu() };
debug_assert!(r >= 0);
r as usize
}
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
#[inline]
pub(crate) fn sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()> {
unsafe {
ret(c::sched_getaffinity(
Pid::as_raw(pid) as _,
core::mem::size_of::<RawCpuSet>(),
cpuset,
))
}
}
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
#[inline]
pub(crate) fn sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()> {
unsafe {
ret(c::sched_setaffinity(
Pid::as_raw(pid) as _,
core::mem::size_of::<RawCpuSet>(),
cpuset,
))
}
}
#[inline]
pub(crate) fn sched_yield() {
unsafe {
let _ = c::sched_yield();
}
}
// The `membarrier` syscall has a third argument, but it's only used when
// the `flags` argument is `MEMBARRIER_CMD_FLAG_CPU`.
#[cfg(linux_kernel)]
syscall! {
fn membarrier_all(
cmd: c::c_int,
flags: c::c_uint
) via SYS_membarrier -> c::c_int
}
#[cfg(linux_kernel)]
pub(crate) fn membarrier_query() -> MembarrierQuery {
// glibc does not have a wrapper for `membarrier`; [the documentation]
// says to use `syscall`.
//
// [the documentation]: https://man7.org/linux/man-pages/man2/membarrier.2.html#NOTES
const MEMBARRIER_CMD_QUERY: u32 = 0;
unsafe {
match ret_u32(membarrier_all(MEMBARRIER_CMD_QUERY as i32, 0)) {
Ok(query) => MembarrierQuery::from_bits_retain(query),
Err(_) => MembarrierQuery::empty(),
}
}
}
#[cfg(linux_kernel)]
pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> {
unsafe { ret(membarrier_all(cmd as i32, 0)) }
}
#[cfg(linux_kernel)]
pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()> {
const MEMBARRIER_CMD_FLAG_CPU: u32 = 1;
syscall! {
fn membarrier_cpu(
cmd: c::c_int,
flags: c::c_uint,
cpu_id: c::c_int
) via SYS_membarrier -> c::c_int
}
unsafe {
ret(membarrier_cpu(
cmd as i32,
MEMBARRIER_CMD_FLAG_CPU,
bitcast!(cpu.as_raw()),
))
}
}

View File

@@ -0,0 +1,60 @@
#[cfg(all(
any(freebsdlike, linux_kernel, target_os = "fuchsia"),
not(any(target_os = "espidf", target_os = "vita"))
))]
use crate::backend::c;
/// A command for use with [`membarrier`] and [`membarrier_cpu`].
///
/// For `MEMBARRIER_CMD_QUERY`, see [`membarrier_query`].
///
/// [`membarrier`]: crate::thread::membarrier
/// [`membarrier_cpu`]: crate::thread::membarrier_cpu
/// [`membarrier_query`]: crate::thread::membarrier_query
#[cfg(linux_kernel)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u32)]
#[non_exhaustive]
pub enum MembarrierCommand {
/// `MEMBARRIER_CMD_GLOBAL`
#[doc(alias = "Shared")]
#[doc(alias = "MEMBARRIER_CMD_SHARED")]
Global = c::MEMBARRIER_CMD_GLOBAL as u32,
/// `MEMBARRIER_CMD_GLOBAL_EXPEDITED`
GlobalExpedited = c::MEMBARRIER_CMD_GLOBAL_EXPEDITED as u32,
/// `MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED`
RegisterGlobalExpedited = c::MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED as u32,
/// `MEMBARRIER_CMD_PRIVATE_EXPEDITED`
PrivateExpedited = c::MEMBARRIER_CMD_PRIVATE_EXPEDITED as u32,
/// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED`
RegisterPrivateExpedited = c::MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED as u32,
/// `MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE`
PrivateExpeditedSyncCore = c::MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE as u32,
/// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE`
RegisterPrivateExpeditedSyncCore =
c::MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE as u32,
/// `MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ` (since Linux 5.10)
PrivateExpeditedRseq = c::MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ as u32,
/// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ` (since Linux 5.10)
RegisterPrivateExpeditedRseq = c::MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ as u32,
}
/// A CPU identifier as a raw integer.
#[cfg(linux_kernel)]
pub type RawCpuid = u32;
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
pub(crate) type RawCpuSet = c::cpu_set_t;
#[cfg(freebsdlike)]
pub(crate) type RawCpuSet = c::cpuset_t;
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
#[inline]
pub(crate) fn raw_cpu_set_new() -> RawCpuSet {
let mut set = unsafe { core::mem::zeroed() };
super::cpu_set::CPU_ZERO(&mut set);
set
}
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
pub(crate) const CPU_SETSIZE: usize = c::CPU_SETSIZE as usize;

View File

@@ -0,0 +1,3 @@
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,526 @@
//! libc syscalls supporting `rustix::time`.
use crate::backend::c;
use crate::backend::conv::ret;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(any(all(target_env = "gnu", fix_y2038), not(fix_y2038)))]
use crate::backend::time::types::LibcItimerspec;
#[cfg(not(target_os = "wasi"))]
use crate::clockid::{ClockId, DynamicClockId};
use crate::io;
#[cfg(not(fix_y2038))]
use crate::timespec::as_libc_timespec_mut_ptr;
#[cfg(not(fix_y2038))]
#[cfg(not(any(
target_os = "redox",
target_os = "wasi",
all(apple, not(target_os = "macos"))
)))]
use crate::timespec::as_libc_timespec_ptr;
#[cfg(all(target_env = "gnu", fix_y2038))]
use crate::timespec::LibcTimespec;
use crate::timespec::Timespec;
use core::mem::MaybeUninit;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
use {
crate::backend::conv::{borrowed_fd, ret_owned_fd},
crate::fd::{BorrowedFd, OwnedFd},
crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags},
};
#[cfg(all(target_env = "gnu", fix_y2038))]
weak!(fn __clock_gettime64(c::clockid_t, *mut LibcTimespec) -> c::c_int);
#[cfg(all(target_env = "gnu", fix_y2038))]
weak!(fn __clock_settime64(c::clockid_t, *const LibcTimespec) -> c::c_int);
#[cfg(all(target_env = "gnu", fix_y2038))]
weak!(fn __clock_getres64(c::clockid_t, *mut LibcTimespec) -> c::c_int);
#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))]
#[cfg(all(target_env = "gnu", fix_y2038))]
weak!(fn __timerfd_gettime64(c::c_int, *mut LibcItimerspec) -> c::c_int);
#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))]
#[cfg(all(target_env = "gnu", fix_y2038))]
weak!(fn __timerfd_settime64(c::c_int, c::c_int, *const LibcItimerspec, *mut LibcItimerspec) -> c::c_int);
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
#[inline]
#[must_use]
pub(crate) fn clock_getres(id: ClockId) -> Timespec {
// Old 32-bit version: libc has `clock_getres` but it is not y2038 safe by
// default. But there may be a `__clock_getres64` we can use.
#[cfg(fix_y2038)]
{
#[cfg(target_env = "gnu")]
if let Some(libc_clock_getres) = __clock_getres64.get() {
let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
unsafe {
ret(libc_clock_getres(id as c::clockid_t, timespec.as_mut_ptr())).unwrap();
return timespec.assume_init().into();
}
}
clock_getres_old(id)
}
// Main version: libc is y2038 safe and has `clock_getres`.
#[cfg(not(fix_y2038))]
unsafe {
let mut timespec = MaybeUninit::<Timespec>::uninit();
let _ = c::clock_getres(id as c::clockid_t, as_libc_timespec_mut_ptr(&mut timespec));
timespec.assume_init()
}
}
#[cfg(fix_y2038)]
#[must_use]
fn clock_getres_old(id: ClockId) -> Timespec {
let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
let old_timespec = unsafe {
ret(c::clock_getres(
id as c::clockid_t,
old_timespec.as_mut_ptr(),
))
.unwrap();
old_timespec.assume_init()
};
Timespec {
tv_sec: old_timespec.tv_sec.into(),
tv_nsec: old_timespec.tv_nsec.into(),
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn clock_gettime(id: ClockId) -> Timespec {
// Old 32-bit version: libc has `clock_gettime` but it is not y2038 safe by
// default. But there may be a `__clock_gettime64` we can use.
#[cfg(fix_y2038)]
{
#[cfg(target_env = "gnu")]
if let Some(libc_clock_gettime) = __clock_gettime64.get() {
let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
unsafe {
ret(libc_clock_gettime(
id as c::clockid_t,
timespec.as_mut_ptr(),
))
.unwrap();
return timespec.assume_init().into();
}
}
clock_gettime_old(id)
}
// Use `.unwrap()` here because `clock_getres` can fail if the clock itself
// overflows a number of seconds, but if that happens, the monotonic clocks
// can't maintain their invariants, or the realtime clocks aren't properly
// configured.
#[cfg(not(fix_y2038))]
unsafe {
let mut timespec = MaybeUninit::<Timespec>::uninit();
ret(c::clock_gettime(
id as c::clockid_t,
as_libc_timespec_mut_ptr(&mut timespec),
))
.unwrap();
let timespec = timespec.assume_init();
#[cfg(apple)]
let timespec = fix_negative_timespec_nsecs(timespec);
timespec
}
}
#[cfg(fix_y2038)]
#[must_use]
fn clock_gettime_old(id: ClockId) -> Timespec {
let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
let old_timespec = unsafe {
ret(c::clock_gettime(
id as c::clockid_t,
old_timespec.as_mut_ptr(),
))
.unwrap();
old_timespec.assume_init()
};
Timespec {
tv_sec: old_timespec.tv_sec.into(),
tv_nsec: old_timespec.tv_nsec.into(),
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result<Timespec> {
let id: c::clockid_t = match id {
DynamicClockId::Known(id) => id as c::clockid_t,
#[cfg(linux_kernel)]
DynamicClockId::Dynamic(fd) => {
use crate::fd::AsRawFd as _;
const CLOCKFD: i32 = 3;
(!fd.as_raw_fd() << 3) | CLOCKFD
}
#[cfg(not(linux_kernel))]
DynamicClockId::Dynamic(_fd) => {
// Dynamic clocks are not supported on this platform.
return Err(io::Errno::INVAL);
}
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
DynamicClockId::RealtimeAlarm => c::CLOCK_REALTIME_ALARM,
#[cfg(linux_kernel)]
DynamicClockId::Tai => c::CLOCK_TAI,
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd"
))]
DynamicClockId::Boottime => c::CLOCK_BOOTTIME,
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
DynamicClockId::BoottimeAlarm => c::CLOCK_BOOTTIME_ALARM,
};
// Old 32-bit version: libc has `clock_gettime` but it is not y2038
// safe by default. But there may be a `__clock_gettime64` we can use.
#[cfg(fix_y2038)]
{
#[cfg(target_env = "gnu")]
if let Some(libc_clock_gettime) = __clock_gettime64.get() {
let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
unsafe {
ret(libc_clock_gettime(
id as c::clockid_t,
timespec.as_mut_ptr(),
))?;
return Ok(timespec.assume_init().into());
}
}
clock_gettime_dynamic_old(id)
}
// Main version: libc is y2038 safe and has `clock_gettime`.
#[cfg(not(fix_y2038))]
unsafe {
let mut timespec = MaybeUninit::<Timespec>::uninit();
ret(c::clock_gettime(
id as c::clockid_t,
as_libc_timespec_mut_ptr(&mut timespec),
))?;
let timespec = timespec.assume_init();
#[cfg(apple)]
let timespec = fix_negative_timespec_nsecs(timespec);
Ok(timespec)
}
}
#[cfg(fix_y2038)]
#[inline]
fn clock_gettime_dynamic_old(id: c::clockid_t) -> io::Result<Timespec> {
let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
let old_timespec = unsafe {
ret(c::clock_gettime(
id as c::clockid_t,
old_timespec.as_mut_ptr(),
))?;
old_timespec.assume_init()
};
Ok(Timespec {
tv_sec: old_timespec.tv_sec.into(),
tv_nsec: old_timespec.tv_nsec.into(),
})
}
#[cfg(not(any(
target_os = "redox",
target_os = "wasi",
all(apple, not(target_os = "macos"))
)))]
#[inline]
pub(crate) fn clock_settime(id: ClockId, timespec: Timespec) -> io::Result<()> {
// Old 32-bit version: libc has `clock_gettime` but it is not y2038 safe by
// default. But there may be a `__clock_settime64` we can use.
#[cfg(fix_y2038)]
{
#[cfg(target_env = "gnu")]
if let Some(libc_clock_settime) = __clock_settime64.get() {
unsafe {
let mut new_timespec = core::mem::zeroed::<LibcTimespec>();
new_timespec.tv_sec = timespec.tv_sec;
new_timespec.tv_nsec = timespec.tv_nsec as _;
return ret(libc_clock_settime(id as c::clockid_t, &new_timespec));
}
}
clock_settime_old(id, timespec)
}
// Main version: libc is y2038 safe and has `clock_settime`.
#[cfg(not(fix_y2038))]
unsafe {
ret(c::clock_settime(
id as c::clockid_t,
as_libc_timespec_ptr(&timespec),
))
}
}
#[cfg(not(any(
target_os = "redox",
target_os = "wasi",
all(apple, not(target_os = "macos"))
)))]
#[cfg(fix_y2038)]
fn clock_settime_old(id: ClockId, timespec: Timespec) -> io::Result<()> {
let old_timespec = c::timespec {
tv_sec: timespec
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timespec.tv_nsec as _,
};
unsafe { ret(c::clock_settime(id as c::clockid_t, &old_timespec)) }
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
pub(crate) fn timerfd_create(id: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::timerfd_create(id as c::clockid_t, bitflags_bits!(flags))) }
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
pub(crate) fn timerfd_settime(
fd: BorrowedFd<'_>,
flags: TimerfdTimerFlags,
new_value: &Itimerspec,
) -> io::Result<Itimerspec> {
// Old 32-bit version: libc has `timerfd_settime` but it is not y2038 safe
// by default. But there may be a `__timerfd_settime64` we can use.
#[cfg(fix_y2038)]
{
#[cfg(target_env = "gnu")]
if let Some(libc_timerfd_settime) = __timerfd_settime64.get() {
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
unsafe {
ret(libc_timerfd_settime(
borrowed_fd(fd),
bitflags_bits!(flags),
&new_value.clone().into(),
result.as_mut_ptr(),
))?;
return Ok(result.assume_init().into());
}
}
timerfd_settime_old(fd, flags, new_value)
}
#[cfg(not(fix_y2038))]
unsafe {
use crate::backend::time::types::{as_libc_itimerspec_mut_ptr, as_libc_itimerspec_ptr};
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
ret(c::timerfd_settime(
borrowed_fd(fd),
bitflags_bits!(flags),
as_libc_itimerspec_ptr(new_value),
as_libc_itimerspec_mut_ptr(&mut result),
))?;
Ok(result.assume_init())
}
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(fix_y2038)]
fn timerfd_settime_old(
fd: BorrowedFd<'_>,
flags: TimerfdTimerFlags,
new_value: &Itimerspec,
) -> io::Result<Itimerspec> {
let mut old_result = MaybeUninit::<c::itimerspec>::uninit();
// Convert `new_value` to the old `itimerspec` format.
let old_new_value = c::itimerspec {
it_interval: c::timespec {
tv_sec: new_value
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: new_value
.it_interval
.tv_nsec
.try_into()
.map_err(|_| io::Errno::INVAL)?,
},
it_value: c::timespec {
tv_sec: new_value
.it_value
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: new_value
.it_value
.tv_nsec
.try_into()
.map_err(|_| io::Errno::INVAL)?,
},
};
let old_result = unsafe {
ret(c::timerfd_settime(
borrowed_fd(fd),
bitflags_bits!(flags),
&old_new_value,
old_result.as_mut_ptr(),
))?;
old_result.assume_init()
};
Ok(Itimerspec {
it_interval: Timespec {
tv_sec: old_result
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: old_result.it_interval.tv_nsec as _,
},
it_value: Timespec {
tv_sec: old_result
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: old_result.it_interval.tv_nsec as _,
},
})
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
// Old 32-bit version: libc has `timerfd_gettime` but it is not y2038 safe
// by default. But there may be a `__timerfd_gettime64` we can use.
#[cfg(fix_y2038)]
{
#[cfg(target_env = "gnu")]
if let Some(libc_timerfd_gettime) = __timerfd_gettime64.get() {
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
unsafe {
ret(libc_timerfd_gettime(borrowed_fd(fd), result.as_mut_ptr()))?;
return Ok(result.assume_init().into());
}
}
timerfd_gettime_old(fd)
}
#[cfg(not(fix_y2038))]
unsafe {
use crate::backend::time::types::as_libc_itimerspec_mut_ptr;
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
ret(c::timerfd_gettime(
borrowed_fd(fd),
as_libc_itimerspec_mut_ptr(&mut result),
))?;
Ok(result.assume_init())
}
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(fix_y2038)]
fn timerfd_gettime_old(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
let mut old_result = MaybeUninit::<c::itimerspec>::uninit();
let old_result = unsafe {
ret(c::timerfd_gettime(borrowed_fd(fd), old_result.as_mut_ptr()))?;
old_result.assume_init()
};
Ok(Itimerspec {
it_interval: Timespec {
tv_sec: old_result
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: old_result.it_interval.tv_nsec as _,
},
it_value: Timespec {
tv_sec: old_result
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: old_result.it_interval.tv_nsec as _,
},
})
}
/// See [`crate::timespec::fix_negative_nsecs`] for details.
#[cfg(apple)]
#[cfg(not(fix_y2038))]
fn fix_negative_timespec_nsecs(mut ts: Timespec) -> Timespec {
let (sec, nsec) = crate::timespec::fix_negative_nsecs(ts.tv_sec as _, ts.tv_nsec as _);
ts.tv_sec = sec as _;
ts.tv_nsec = nsec as _;
ts
}

View File

@@ -0,0 +1,266 @@
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
use crate::backend::c;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
use crate::time::Itimerspec;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(fix_y2038)]
use crate::timespec::LibcTimespec;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
use bitflags::bitflags;
/// On most platforms, `LibcItimerspec` is just `Itimerspec`.
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(not(fix_y2038))]
pub(crate) type LibcItimerspec = Itimerspec;
/// On 32-bit glibc platforms, `LibcTimespec` differs from `Timespec`, so we
/// define our own struct, with bidirectional `From` impls.
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(fix_y2038)]
#[repr(C)]
#[derive(Debug, Clone)]
pub(crate) struct LibcItimerspec {
pub it_interval: LibcTimespec,
pub it_value: LibcTimespec,
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(fix_y2038)]
impl From<LibcItimerspec> for Itimerspec {
#[inline]
fn from(t: LibcItimerspec) -> Self {
Self {
it_interval: t.it_interval.into(),
it_value: t.it_value.into(),
}
}
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(fix_y2038)]
impl From<Itimerspec> for LibcItimerspec {
#[inline]
fn from(t: Itimerspec) -> Self {
Self {
it_interval: t.it_interval.into(),
it_value: t.it_value.into(),
}
}
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(not(fix_y2038))]
pub(crate) fn as_libc_itimerspec_ptr(itimerspec: &Itimerspec) -> *const c::itimerspec {
#[cfg(test)]
{
assert_eq_size!(Itimerspec, c::itimerspec);
}
crate::utils::as_ptr(itimerspec).cast::<c::itimerspec>()
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(not(fix_y2038))]
pub(crate) fn as_libc_itimerspec_mut_ptr(
itimerspec: &mut core::mem::MaybeUninit<Itimerspec>,
) -> *mut c::itimerspec {
#[cfg(test)]
{
assert_eq_size!(Itimerspec, c::itimerspec);
}
itimerspec.as_mut_ptr().cast::<c::itimerspec>()
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
bitflags! {
/// `TFD_*` flags for use with [`timerfd_create`].
///
/// [`timerfd_create`]: crate::time::timerfd_create
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct TimerfdFlags: u32 {
/// `TFD_NONBLOCK`
#[doc(alias = "TFD_NONBLOCK")]
const NONBLOCK = bitcast!(c::TFD_NONBLOCK);
/// `TFD_CLOEXEC`
#[doc(alias = "TFD_CLOEXEC")]
const CLOEXEC = bitcast!(c::TFD_CLOEXEC);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
bitflags! {
/// `TFD_TIMER_*` flags for use with [`timerfd_settime`].
///
/// [`timerfd_settime`]: crate::time::timerfd_settime
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct TimerfdTimerFlags: u32 {
/// `TFD_TIMER_ABSTIME`
#[doc(alias = "TFD_TIMER_ABSTIME")]
const ABSTIME = bitcast!(c::TFD_TIMER_ABSTIME);
/// `TFD_TIMER_CANCEL_ON_SET`
#[cfg(not(target_os = "fuchsia"))]
#[doc(alias = "TFD_TIMER_CANCEL_ON_SET")]
const CANCEL_ON_SET = bitcast!(c::TFD_TIMER_CANCEL_ON_SET);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
/// `CLOCK_*` constants for use with [`timerfd_create`].
///
/// [`timerfd_create`]: crate::time::timerfd_create
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u32)]
#[non_exhaustive]
pub enum TimerfdClockId {
/// `CLOCK_REALTIME`—A clock that tells the “real” time.
///
/// This is a clock that tells the amount of time elapsed since the Unix
/// epoch, 1970-01-01T00:00:00Z. The clock is externally settable, so it is
/// not monotonic. Successive reads may see decreasing times, so it isn't
/// reliable for measuring durations.
#[doc(alias = "CLOCK_REALTIME")]
Realtime = bitcast!(c::CLOCK_REALTIME),
/// `CLOCK_MONOTONIC`—A clock that tells an abstract time.
///
/// Unlike `Realtime`, this clock is not based on a fixed known epoch, so
/// individual times aren't meaningful. However, since it isn't settable,
/// it is reliable for measuring durations.
///
/// This clock does not advance while the system is suspended; see
/// `Boottime` for a clock that does.
#[doc(alias = "CLOCK_MONOTONIC")]
Monotonic = bitcast!(c::CLOCK_MONOTONIC),
/// `CLOCK_BOOTTIME`—Like `Monotonic`, but advances while suspended.
///
/// This clock is similar to `Monotonic`, but does advance while the system
/// is suspended.
#[doc(alias = "CLOCK_BOOTTIME")]
#[cfg(any(linux_kernel, target_os = "fuchsia", target_os = "openbsd"))]
Boottime = bitcast!(c::CLOCK_BOOTTIME),
/// `CLOCK_REALTIME_ALARM`—Like `Realtime`, but wakes a suspended system.
///
/// This clock is like `Realtime`, but can wake up a suspended system.
///
/// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability.
#[cfg(linux_kernel)]
#[doc(alias = "CLOCK_REALTIME_ALARM")]
RealtimeAlarm = bitcast!(c::CLOCK_REALTIME_ALARM),
/// `CLOCK_BOOTTIME_ALARM`—Like `Boottime`, but wakes a suspended system.
///
/// This clock is like `Boottime`, but can wake up a suspended system.
///
/// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability.
#[cfg(any(linux_kernel, target_os = "cygwin", target_os = "fuchsia"))]
#[doc(alias = "CLOCK_BOOTTIME_ALARM")]
BoottimeAlarm = bitcast!(c::CLOCK_BOOTTIME_ALARM),
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[test]
fn test_types() {
assert_eq_size!(TimerfdFlags, c::c_int);
assert_eq_size!(TimerfdTimerFlags, c::c_int);
}
}

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -0,0 +1,42 @@
use crate::backend::c;
use crate::ugid::{Gid, RawGid, RawUid, Uid};
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getuid() -> Uid {
unsafe {
let uid = c::getuid() as RawUid;
Uid::from_raw(uid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn geteuid() -> Uid {
unsafe {
let uid = c::geteuid() as RawUid;
Uid::from_raw(uid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getgid() -> Gid {
unsafe {
let gid = c::getgid() as RawGid;
Gid::from_raw(gid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getegid() -> Gid {
unsafe {
let gid = c::getegid() as RawGid;
Gid::from_raw(gid)
}
}

View File

@@ -0,0 +1,67 @@
//! Adapt the Winsock API to resemble a POSIX-style libc API.
#![allow(unused_imports)]
#![allow(non_camel_case_types)]
#![allow(dead_code)]
use windows_sys::Win32::Networking::WinSock;
// Define the basic C types. With Rust 1.64, we can use these from `core::ffi`.
pub(crate) type c_schar = i8;
pub(crate) type c_uchar = u8;
pub(crate) type c_short = i16;
pub(crate) type c_ushort = u16;
pub(crate) type c_int = i32;
pub(crate) type c_uint = u32;
pub(crate) type c_longlong = i64;
pub(crate) type c_ulonglong = u64;
pub(crate) type ssize_t = isize;
pub(crate) type c_char = i8;
pub(crate) type c_long = i32;
pub(crate) type c_ulong = u32;
pub(crate) use core::ffi::c_void;
// windows-sys declares these constants as `u16`. For better compatibility with
// Unix-family APIs, redeclare them as `i32`.
pub(crate) const AF_INET: i32 = WinSock::AF_INET as _;
pub(crate) const AF_INET6: i32 = WinSock::AF_INET6 as _;
pub(crate) const AF_UNSPEC: i32 = WinSock::AF_UNSPEC as _;
// Include the contents of `WinSock`, renaming as needed to match POSIX.
//
// Use `WSA_E_CANCELLED` for `ECANCELED` instead of `WSAECANCELLED`, because
// `WSAECANCELLED` will be removed in the future.
// <https://docs.microsoft.com/en-us/windows/win32/api/ws2spi/nc-ws2spi-lpnsplookupserviceend#remarks>
pub(crate) use WinSock::{
closesocket as close, ioctlsocket as ioctl, WSAPoll as poll, ADDRESS_FAMILY as sa_family_t,
ADDRINFOA as addrinfo, IN6_ADDR as in6_addr, IN_ADDR as in_addr, IPV6_MREQ as ipv6_mreq,
IP_MREQ as ip_mreq, LINGER as linger, SD_BOTH as SHUT_RDWR, SD_RECEIVE as SHUT_RD,
SD_SEND as SHUT_WR, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in,
SOCKADDR_IN6 as sockaddr_in6, SOCKADDR_STORAGE as sockaddr_storage, WSAEACCES as EACCES,
WSAEADDRINUSE as EADDRINUSE, WSAEADDRNOTAVAIL as EADDRNOTAVAIL,
WSAEAFNOSUPPORT as EAFNOSUPPORT, WSAEALREADY as EALREADY, WSAEBADF as EBADF,
WSAECONNABORTED as ECONNABORTED, WSAECONNREFUSED as ECONNREFUSED, WSAECONNRESET as ECONNRESET,
WSAEDESTADDRREQ as EDESTADDRREQ, WSAEDISCON as EDISCON, WSAEDQUOT as EDQUOT,
WSAEFAULT as EFAULT, WSAEHOSTDOWN as EHOSTDOWN, WSAEHOSTUNREACH as EHOSTUNREACH,
WSAEINPROGRESS as EINPROGRESS, WSAEINTR as EINTR, WSAEINVAL as EINVAL,
WSAEINVALIDPROCTABLE as EINVALIDPROCTABLE, WSAEINVALIDPROVIDER as EINVALIDPROVIDER,
WSAEISCONN as EISCONN, WSAELOOP as ELOOP, WSAEMFILE as EMFILE, WSAEMSGSIZE as EMSGSIZE,
WSAENAMETOOLONG as ENAMETOOLONG, WSAENETDOWN as ENETDOWN, WSAENETRESET as ENETRESET,
WSAENETUNREACH as ENETUNREACH, WSAENOBUFS as ENOBUFS, WSAENOMORE as ENOMORE,
WSAENOPROTOOPT as ENOPROTOOPT, WSAENOTCONN as ENOTCONN, WSAENOTEMPTY as ENOTEMPTY,
WSAENOTSOCK as ENOTSOCK, WSAEOPNOTSUPP as EOPNOTSUPP, WSAEPFNOSUPPORT as EPFNOSUPPORT,
WSAEPROCLIM as EPROCLIM, WSAEPROTONOSUPPORT as EPROTONOSUPPORT, WSAEPROTOTYPE as EPROTOTYPE,
WSAEPROVIDERFAILEDINIT as EPROVIDERFAILEDINIT, WSAEREFUSED as EREFUSED, WSAEREMOTE as EREMOTE,
WSAESHUTDOWN as ESHUTDOWN, WSAESOCKTNOSUPPORT as ESOCKTNOSUPPORT, WSAESTALE as ESTALE,
WSAETIMEDOUT as ETIMEDOUT, WSAETOOMANYREFS as ETOOMANYREFS, WSAEUSERS as EUSERS,
WSAEWOULDBLOCK as EWOULDBLOCK, WSAEWOULDBLOCK as EAGAIN, WSAPOLLFD as pollfd,
WSA_E_CANCELLED as ECANCELED, *,
};
// Windows doesn't have `timespec`, just `timeval`. Rustix only uses `timespec`
// in its public API. So define one, and we'll convert it internally.
pub struct timespec {
pub tv_sec: time_t,
pub tv_nsec: crate::ffi::c_long,
}
pub type time_t = i64;

View File

@@ -0,0 +1,269 @@
//! aarch64 Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[cfg(target_pointer_width = "32")]
compile_error!("arm64-ilp32 is not supported yet");
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
lateout("x0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"svc 0",
"brk #0x1",
in("x8") nr.to_asm(),
in("x0") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
in("x5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
in("x5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,266 @@
//! arm Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
lateout("r0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"svc 0",
"udf #16",
in("r7") nr.to_asm(),
in("r0") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,544 @@
//! mipsel Linux system calls.
//!
//! On mipsel, Linux indicates success or failure using `$a3` rather
//! than by returning a negative error code as most other architectures do.
//!
//! MIPS-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, A6, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
"teq $0,$0",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall7_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"sw {}, 24($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
in(reg) a6.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View File

@@ -0,0 +1,544 @@
//! mipsisa32r6el Linux system calls.
//!
//! On mipsisa32r6el, Linux indicates success or failure using `$a3` rather
//! than by returning a negative error code as most other architectures do.
//!
//! MIPS-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, A6, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
"teq $0,$0",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall7_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"sw {}, 24($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
in(reg) a6.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View File

@@ -0,0 +1,467 @@
//! mips64el Linux system calls.
//!
//! On mips64el, Linux indicates success or failure using `$a3` (`$7`) rather
//! than by returning a negative error code as most other architectures do.
//!
//! MIPS-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
"teq $0,$0",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View File

@@ -0,0 +1,471 @@
//! mipsisa64r6el Linux system calls.
//!
//! On mipsisa64r6el, Linux indicates success or failure using `$a3` (`$7`)
//! rather than by returning a negative error code as most other architectures
//! do.
//!
//! MIPS-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
//!
//! MIPS R6 inline assembly currently doesn't differ from MIPS, because no
//! explicit call of R6-only or R2-only instructions exist here.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
"teq $0,$0",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View File

@@ -0,0 +1,321 @@
//! Architecture-specific syscall code.
//!
//! This module also has a `choose` submodule which chooses a scheme and is
//! what most of the `rustix` syscalls use.
//!
//! Compilers should really have intrinsics for making system calls. They're
//! much like regular calls, with custom calling conventions, and calling
//! conventions are otherwise the compiler's job. But for now, use inline asm.
//!
//! The calling conventions for Linux syscalls are [documented here].
//!
//! [documented here]: https://man7.org/linux/man-pages/man2/syscall.2.html
//!
//! # Safety
//!
//! This contains the inline `asm` statements performing the syscall
//! instructions.
#![allow(unsafe_code)]
#![cfg_attr(not(feature = "all-apis"), allow(unused_imports))]
// We'll use as many arguments as syscalls need.
#![allow(clippy::too_many_arguments)]
// These functions always use the machine's syscall instruction, even when it
// isn't the fastest option available.
#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
#[cfg_attr(all(target_arch = "arm", not(thumb_mode)), path = "arm.rs")]
#[cfg_attr(all(target_arch = "arm", thumb_mode), path = "thumb.rs")]
#[cfg_attr(target_arch = "mips", path = "mips.rs")]
#[cfg_attr(target_arch = "mips32r6", path = "mips32r6.rs")]
#[cfg_attr(target_arch = "mips64", path = "mips64.rs")]
#[cfg_attr(target_arch = "mips64r6", path = "mips64r6.rs")]
#[cfg_attr(target_arch = "powerpc", path = "powerpc.rs")]
#[cfg_attr(target_arch = "powerpc64", path = "powerpc64.rs")]
#[cfg_attr(target_arch = "riscv64", path = "riscv64.rs")]
#[cfg_attr(target_arch = "s390x", path = "s390x.rs")]
#[cfg_attr(target_arch = "x86", path = "x86.rs")]
#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
pub(in crate::backend) mod asm;
// On most architectures, the architecture syscall instruction is fast, so use
// it directly.
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "riscv64",
target_arch = "s390x",
target_arch = "x86_64",
))]
pub(in crate::backend) use self::asm as choose;
// On 32-bit x86, use vDSO wrappers for all syscalls. We could use the
// architecture syscall instruction (`int 0x80`), but the vDSO
// `__kernel_vsyscall` mechanism is much faster.
#[cfg(target_arch = "x86")]
pub(in crate::backend) use super::vdso_wrappers::x86_via_vdso as choose;
// This would be the code for always using `int 0x80` on 32-bit x86.
//#[cfg(target_arch = "x86")]
//pub(in crate::backend) use self::asm as choose;
// Macros for invoking system calls.
//
// These factor out:
// - Calling `nr` on the syscall number to convert it into `SyscallNumber`.
// - Calling `.into()` on each of the arguments to convert them into `ArgReg`.
// - Qualifying the `syscall*` and `__NR_*` identifiers.
// - Counting the number of arguments.
macro_rules! syscall {
($nr:ident) => {
$crate::backend::arch::choose::syscall0($crate::backend::reg::nr(
linux_raw_sys::general::$nr,
))
};
($nr:ident, $a0:expr) => {
$crate::backend::arch::choose::syscall1(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
($nr:ident, $a0:expr, $a1:expr) => {
$crate::backend::arch::choose::syscall2(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
$crate::backend::arch::choose::syscall3(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
$crate::backend::arch::choose::syscall4(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$crate::backend::arch::choose::syscall5(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
$crate::backend::arch::choose::syscall6(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
$crate::backend::arch::choose::syscall7(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
$a6.into(),
)
};
}
// Macro to invoke a syscall that always uses direct assembly, rather than the
// vDSO. Useful when still finding the vDSO.
#[allow(unused_macros)]
macro_rules! syscall_always_asm {
($nr:ident) => {
$crate::backend::arch::asm::syscall0($crate::backend::reg::nr(linux_raw_sys::general::$nr))
};
($nr:ident, $a0:expr) => {
$crate::backend::arch::asm::syscall1(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
($nr:ident, $a0:expr, $a1:expr) => {
$crate::backend::arch::asm::syscall2(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
$crate::backend::arch::asm::syscall3(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
$crate::backend::arch::asm::syscall4(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$crate::backend::arch::asm::syscall5(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
$crate::backend::arch::asm::syscall6(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
$crate::backend::arch::asm::syscall7(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
$a6.into(),
)
};
}
/// Like `syscall`, but adds the `readonly` attribute to the inline asm, which
/// indicates that the syscall does not mutate any memory.
macro_rules! syscall_readonly {
($nr:ident) => {
$crate::backend::arch::choose::syscall0_readonly($crate::backend::reg::nr(
linux_raw_sys::general::$nr,
))
};
($nr:ident, $a0:expr) => {
$crate::backend::arch::choose::syscall1_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
($nr:ident, $a0:expr, $a1:expr) => {
$crate::backend::arch::choose::syscall2_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
$crate::backend::arch::choose::syscall3_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
$crate::backend::arch::choose::syscall4_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$crate::backend::arch::choose::syscall5_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
$crate::backend::arch::choose::syscall6_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
$crate::backend::arch::choose::syscall7_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
$a6.into(),
)
};
}
/// Like `syscall`, but indicates that the syscall does not return.
#[cfg(feature = "runtime")]
macro_rules! syscall_noreturn {
($nr:ident, $a0:expr) => {
$crate::backend::arch::choose::syscall1_noreturn(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
}

View File

@@ -0,0 +1,414 @@
//! powerpc Linux system calls.
//!
//! On powerpc, Linux indicates success or failure using `cr0.SO` rather
//! than by returning a negative error code as most other architectures do. In
//! theory we could immediately translate this into a `Result`, and it'd save a
//! few branches. And in theory we could have specialized sequences for use
//! with syscalls that are known to never fail. However, those would require
//! more extensive changes in rustix's platform-independent code. For now, we
//! check the flag and negate the error value to make PowerPC look like other
//! architectures.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
lateout("r3") r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"sc",
"trap",
in("r0") nr.to_asm(),
in("r3") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,414 @@
//! powerpc64le Linux system calls.
//!
//! On powerpc64le, Linux indicates success or failure using `cr0.SO` rather
//! than by returning a negative error code as most other architectures do. In
//! theory we could immediately translate this into a `Result`, and it'd save a
//! few branches. And in theory we could have specialized sequences for use
//! with syscalls that are known to never fail. However, those would require
//! more extensive changes in rustix's platform-independent code. For now, we
//! check the flag and negate the error value to make PowerPC64 look like other
//! architectures.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
lateout("r3") r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"sc",
"trap",
in("r0") nr.to_asm(),
in("r3") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

Some files were not shown because too many files have changed in this diff Show More