mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-30 13:19:44 +02:00
fix(android): avoid blocked statx probes on legacy ARM32 devices
Vendor the pinned Rustix package with an Android-only weak libc lookup so missing statx returns ENOSYS and uses the existing stat64 fallback. Add an isolated Android smoke test with a hidden symbol and a seccomp filter that kills raw statx probes. Verified 177 workspace tests, host Clippy, and ARM64 Android sandbox smoke test. ARM32 cross-target Clippy passes with the existing needless_return warning in binary.rs exempted; physical Android 10 ARM32 validation remains required.
This commit is contained in:
1 parent
9d530cea9e
commit
1d53609b51
339 files changed
+75413
-2
No files matched your search
Generated
+1
-2
@@ -1990,8 +1990,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -2396,6 +2394,7 @@ dependencies = [
|
||||
"getrandom 0.4.3",
|
||||
"hmac",
|
||||
"httpdate",
|
||||
"libc",
|
||||
"md-5",
|
||||
"rcgen",
|
||||
"regex",
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
[workspace]
|
||||
members = ["crates/core", "crates/network", "crates/providers", "crates/extensions", "crates/mobile", "crates/bindgen"]
|
||||
exclude = ["vendor/rustix-1.1.4"]
|
||||
resolver = "3"
|
||||
|
||||
[patch.crates-io]
|
||||
# Android < 30 may kill processes that probe statx directly (SIGSYS).
|
||||
# Keep all direct and transitive users on the same compatibility fix.
|
||||
rustix = { path = "vendor/rustix-1.1.4" }
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
@@ -35,6 +35,7 @@ cap-std.workspace = true
|
||||
rustix.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
libc = "=0.2.189"
|
||||
rustls.workspace = true
|
||||
rcgen = { version = "=0.14.10", default-features = false, features = ["crypto", "ring", "pem"] }
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
//! Run on Android with `-C link-arg=-Wl,--wrap=dlsym`.
|
||||
//! `--legacy` hides Bionic's statx symbol and kills raw statx syscalls, modeling
|
||||
//! the older app sandbox. `--probe-blocked-syscall` must exit with SIGSYS.
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(unsafe_code, reason = "isolated native sandbox regression test")]
|
||||
mod android {
|
||||
use std::ffi::{CStr, c_char, c_void};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
static HIDE_STATX: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
unsafe extern "C" {
|
||||
fn __real_dlsym(handle: *mut c_void, name: *const c_char) -> *mut c_void;
|
||||
}
|
||||
|
||||
// This symbol only exists in the smoke-test executable, never in the app.
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn __wrap_dlsym(handle: *mut c_void, name: *const c_char) -> *mut c_void {
|
||||
// SAFETY: dlsym receives a valid NUL-terminated symbol name.
|
||||
if HIDE_STATX.load(Ordering::Relaxed) && unsafe { CStr::from_ptr(name) } == c"statx" {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
// SAFETY: forward the original dlsym arguments without modification.
|
||||
unsafe { __real_dlsym(handle, name) }
|
||||
}
|
||||
|
||||
fn forbid_raw_statx() {
|
||||
// Load seccomp_data.nr; terminate only statx, allow other syscalls.
|
||||
let filter = [
|
||||
libc::sock_filter {
|
||||
code: 0x20,
|
||||
jt: 0,
|
||||
jf: 0,
|
||||
k: 0,
|
||||
},
|
||||
libc::sock_filter {
|
||||
code: 0x15,
|
||||
jt: 0,
|
||||
jf: 1,
|
||||
k: libc::SYS_statx as u32,
|
||||
},
|
||||
libc::sock_filter {
|
||||
code: 0x06,
|
||||
jt: 0,
|
||||
jf: 0,
|
||||
k: 0x8000_0000,
|
||||
},
|
||||
libc::sock_filter {
|
||||
code: 0x06,
|
||||
jt: 0,
|
||||
jf: 0,
|
||||
k: 0x7fff_0000,
|
||||
},
|
||||
];
|
||||
let program = libc::sock_fprog {
|
||||
len: filter.len() as u16,
|
||||
filter: filter.as_ptr().cast_mut(),
|
||||
};
|
||||
// SAFETY: the kernel copies this initialized filter synchronously.
|
||||
unsafe {
|
||||
assert_eq!(libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0), 0);
|
||||
assert_eq!(libc::prctl(libc::PR_SET_SECCOMP, 2, &program, 0, 0), 0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run() {
|
||||
let legacy = std::env::args().any(|arg| arg == "--legacy");
|
||||
let probe = std::env::args().any(|arg| arg == "--probe-blocked-syscall");
|
||||
if legacy || probe {
|
||||
HIDE_STATX.store(true, Ordering::Relaxed);
|
||||
forbid_raw_statx();
|
||||
}
|
||||
if probe {
|
||||
// SAFETY: the sandbox must terminate this process before executing
|
||||
// statx. Null output would otherwise fail with EFAULT, not write.
|
||||
unsafe {
|
||||
libc::syscall(
|
||||
libc::SYS_statx,
|
||||
-100,
|
||||
c".".as_ptr(),
|
||||
0,
|
||||
0,
|
||||
std::ptr::null_mut::<c_void>(),
|
||||
);
|
||||
}
|
||||
panic!("raw statx was not blocked");
|
||||
}
|
||||
|
||||
let temp = tempfile::tempdir_in("/data/local/tmp").unwrap();
|
||||
std::fs::write(temp.path().join("track.flac"), b"audio payload").unwrap();
|
||||
let dir =
|
||||
cap_std::fs::Dir::open_ambient_dir(temp.path(), cap_std::ambient_authority()).unwrap();
|
||||
let file = dir.open("track.flac").unwrap();
|
||||
assert_eq!(file.metadata().unwrap().len(), 13);
|
||||
assert_eq!(dir.metadata("track.flac").unwrap().len(), 13);
|
||||
assert_eq!(dir.symlink_metadata("track.flac").unwrap().len(), 13);
|
||||
assert_eq!(rustix::fs::fstat(&file).unwrap().st_size, 13);
|
||||
assert_eq!(
|
||||
rustix::fs::statat(&dir, "track.flac", rustix::fs::AtFlags::empty())
|
||||
.unwrap()
|
||||
.st_size,
|
||||
13
|
||||
);
|
||||
let extended = rustix::fs::statx(
|
||||
&dir,
|
||||
"track.flac",
|
||||
rustix::fs::AtFlags::empty(),
|
||||
rustix::fs::StatxFlags::BASIC_STATS,
|
||||
);
|
||||
if legacy {
|
||||
assert_eq!(extended.unwrap_err(), rustix::io::Errno::NOSYS);
|
||||
} else if let Ok(stat) = extended {
|
||||
assert_eq!(stat.stx_size, 13);
|
||||
}
|
||||
dir.rename("track.flac", &dir, "renamed.flac").unwrap();
|
||||
assert_eq!(dir.read("renamed.flac").unwrap(), b"audio payload");
|
||||
println!("metadata, capability paths, and statx fallback passed (legacy={legacy})");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(target_os = "android")]
|
||||
android::run();
|
||||
#[cfg(not(target_os = "android"))]
|
||||
eprintln!("This smoke test runs on Android.");
|
||||
}
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
# Rustix Android compatibility patch
|
||||
|
||||
`rustix-1.1.4` is vendored from the pinned crates.io package using `cargo vendor`.
|
||||
Its upstream license files are retained. Cargo's workspace patch applies to both
|
||||
direct users and transitive dependencies such as `cap-std`.
|
||||
The original package checksum is
|
||||
`b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190`.
|
||||
|
||||
The sole upstream source change is in `src/backend/libc/fs/syscalls.rs`: Android
|
||||
uses `weakcall!` for `statx`, rather than `weak_or_syscall!`. Bionic exports this
|
||||
function from API 30. On earlier releases, probing the raw syscall can terminate
|
||||
the app with `SIGSYS/SYS_SECCOMP` instead of returning an error (observed on
|
||||
Android 10 ARM32, syscall 397 during `read_audio_metadata`). Missing symbols now
|
||||
return `ENOSYS`, allowing the existing `fstat64`/`fstatat64` fallback. Newer Android
|
||||
continues to use Bionic's `statx`; non-Android targets retain upstream behavior.
|
||||
|
||||
References:
|
||||
- [Bionic stat.h](https://android.googlesource.com/platform/bionic/+/refs/heads/main/libc/include/sys/stat.h)
|
||||
- [Upstream Rustix](https://github.com/bytecodealliance/rustix)
|
||||
|
||||
When upgrading Rustix, check whether upstream has fixed this path before removing
|
||||
the patch. Keep the Android metadata smoke test and verify both APK ABIs.
|
||||
|
||||
Run `bash scripts/check_android_statx.sh arm64-v8a` (or `armeabi-v7a`) with
|
||||
`ANDROID_NDK_HOME` set and a matching Android device connected through `adb`.
|
||||
The isolated test executable checks regular metadata access, then hides the libc
|
||||
symbol and blocks the raw syscall to exercise the legacy fallback. A final probe
|
||||
deliberately exits with `SIGSYS` to verify that the filter is active. Neither the
|
||||
symbol wrapper nor the filter is included in the application. This simulated
|
||||
sandbox check complements testing the APK on an actual older ARM32 device.
|
||||
@@ -0,0 +1 @@
|
||||
{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"a4a3c3581f9874c0375276fc58bdad605866df834795fa430ce493da48aa4778","CHANGES.md":"cb41541f3476ea8d2f1755e7fd14fb06b97c7e591b8cdf8571bd2c62d345af0a","CODE_OF_CONDUCT.md":"f210602311e3f74b32f46237fd55f4ce36d798e85e3db1432ec667f63a7ffc44","CONTRIBUTING.md":"63d06b36bf3e44bd905be68990004e4d6098870885c7d1019c56f847327e5a31","COPYRIGHT":"377c2e7c53250cc5905c0b0532d35973392af16ffb9596a41d99d202cf3617c9","Cargo.lock":"c062a8c6722b7b93c942b0b861a4e1138a7f97dedf5e2f6dc512221d0a81f54c","Cargo.toml":"13302af30f997a373ad50b7aa668d9612483e85be6b455e09d95bbd59fb5296f","Cargo.toml.orig":"219f712befc2057b71e7a5b156eaff40b7239dbb69966e8475542cbcd750cef7","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-Apache-2.0_WITH_LLVM-exception":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","ORG_CODE_OF_CONDUCT.md":"a62b69bf86e605ee1bcbb2f0a12ba79e4cebb6983a7b6491949750aecc4f2178","README.md":"de51d792f8c9f28d1b9b364c31da9d2d5ca00166404d339850863875d0c21909","SECURITY.md":"4d75afb09dd28eb5982e3a1f768ee398d90204669ceef3240a16b31dcf04148a","benches/mod.rs":"9bcb9df020cb3d95db20dfcd549d5b912cdd4ca5a28de248a295001b5eeb47fe","build.rs":"74cb32e64aa6fe99c2496a425b016e22f4e43c438a8237966b8acae04a98eaf9","src/backend/libc/c.rs":"9ec8819d06a657fa202e6775950a938286618bc6cd2bd2e891b0998b6bcaba1c","src/backend/libc/conv.rs":"7a0d66156f7e26eedcc148694c2ce6e7c9c7f281e9fea3ee6bce3e23be11bb56","src/backend/libc/event/epoll.rs":"fabf21b7ab278a469fdc71d823c3414d3c72e24d72dab2bebe409105a93ad0e1","src/backend/libc/event/mod.rs":"554bcf6d614a99c32d021d0a2eb3018705f516120d4e2e52d4376cd835f3f28c","src/backend/libc/event/poll_fd.rs":"f65971706707fedbe1df2a5c1eafcb2d1a562f383a355e3bef87a905f306afbe","src/backend/libc/event/syscalls.rs":"4ec44f572ab04ad78b6776bb04ed0dc294fa85cd3d9eccad7c28e01dda90aec1","src/backend/libc/event/types.rs":"b966e0a3a018ca2ce507371be900fc73b2814cca450b784583207ad2df473f13","src/backend/libc/event/windows_syscalls.rs":"594ce6df675d1e318a876cb8a4c7f8787532c0f6c40305e3d2d8d3ab96dbdc29","src/backend/libc/fs/dir.rs":"a81037979ab8556c3628d506f310b4a051a0b4f5cb7e73d46ea67bd3738ea461","src/backend/libc/fs/inotify.rs":"69376ba2e81f98164029888d0cae554ca1702b532b0045dc55e282f1dd52d7bb","src/backend/libc/fs/makedev.rs":"e2baefed008497ace767da7b89a4513f23429d228b251ef70bee42c821d4c111","src/backend/libc/fs/mod.rs":"cc85f3b47b4318094859d7077a829d8bf2285a985e02496213b68b345fdc1ba1","src/backend/libc/fs/syscalls.rs":"35ff1ef84e1ae46182e771e0fd4bb7f67f85311ba46e84e336def370bcf1217e","src/backend/libc/fs/types.rs":"ef5c4b9612ca1cdbfe6b547cd4e2d7193ea6d7ed9cd09e8cd19a81c45a2a0a39","src/backend/libc/io/errno.rs":"355f9e7c6e617e62494e97f0a5e03ebc341dc4b28b66b30d2a25ba54ee9fb451","src/backend/libc/io/mod.rs":"746647bd864e4ec7717925b6d176cebdb392b7d015070244cc48d92780351dd6","src/backend/libc/io/syscalls.rs":"00ff59d1dabbea998ed018b9d8c2fa6e507116019b9e3e43cc800a91f710b1fe","src/backend/libc/io/types.rs":"a966b120932df9533410143d926a0140002ecf41051cd82673d17a04bbe48f0b","src/backend/libc/io/windows_syscalls.rs":"dd9e7e10aa02ae1f83bf6dd4b22b0ea472389dce762815593a3994ca6567f396","src/backend/libc/io_uring/mod.rs":"2c6478857a0751625edabd61acb841819bfba1093b1faeded15693c805d84952","src/backend/libc/io_uring/syscalls.rs":"5221a498e6c396d7c4cb60168ce8459ebca7d6720349a426d8083c227c694212","src/backend/libc/mm/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/mm/syscalls.rs":"c04c61ad3d7f3b24d13f89144d7fa0a05658cea4763207f9250db4f9362de2fe","src/backend/libc/mm/types.rs":"5c50a3d7b971209ba62a536e31fa3c0613b58328c27e8722fbdba86f1b76b1a1","src/backend/libc/mod.rs":"92d16eed953b10d3a0eed59f1413352c6a1c64a2b75be2c8d20628c59e7da127","src/backend/libc/mount/mod.rs":"8aa966faf3853d1a93d0ed91f7e5f4a53539b0287b25a5bfe489fa1d07f7cfd7","src/backend/libc/mount/syscalls.rs":"b1d167b33764c94fbf1bde891478a3d370c576b2080b3daacb0a4313d240d6a0","src/backend/libc/mount/types.rs":"06f95e29ce60a9913189313f6450852227a6d6c39f89885cb91d0c9b243344fe","src/backend/libc/net/addr.rs":"6372c483e8ef78cd85eb26a94f61cd1efb8d2be6665fc8e13de67e4c5b28871d","src/backend/libc/net/ext.rs":"cfce41130eb2e3dd04271e0aa2c1e2de9866397d7d2a59e917d26b784ea30df5","src/backend/libc/net/mod.rs":"43004d73e16ba3107c0da4651dc749230a43597ccefc24caf75950f9ef91a7cc","src/backend/libc/net/msghdr.rs":"a1c6f46c1348b5e7b3d1cc2c58096783f2f7392d1d1f6d994585183200ec3bf0","src/backend/libc/net/netdevice.rs":"7c439090f99cb603de555f5acb065814a3344c4897f916e022ad07b979ccf025","src/backend/libc/net/read_sockaddr.rs":"95b573a014bf819a63fbe82169a63057f2f860abc8246d55a0d874e3acf7423b","src/backend/libc/net/send_recv.rs":"b18c8f7ccdf4b5dceb9ae8fb8590fd041aba590a2b49858Line truncated
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "c4caf5caaa7e93828a2e4a4cdba1dd0171e45717"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
+391
@@ -0,0 +1,391 @@
|
||||
# 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
|
||||
|
||||
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.
|
||||
@@ -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/
|
||||
@@ -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
@@ -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.
|
||||
+1205
File diff suppressed because it is too large.
Load diff
+292
@@ -0,0 +1,292 @@
|
||||
# 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.4"
|
||||
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", target_os = "android"), 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.12"
|
||||
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.182"
|
||||
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.12"
|
||||
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.182"
|
||||
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(sanitize_memory)",
|
||||
"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"))',
|
||||
]
|
||||
|
||||
[hints]
|
||||
mostly-unused = true
|
||||
+201
@@ -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.
|
||||
@@ -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
@@ -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.
|
||||
@@ -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.
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
<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
|
||||
[`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
|
||||
[`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
@@ -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
@@ -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);
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
use std::env::var;
|
||||
use std::io::Write as _;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// 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 == "linux" || os == "android") && !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)
|
||||
};
|
||||
|
||||
let out_dir = var("OUT_DIR").unwrap();
|
||||
let out_file = PathBuf::from(out_dir).join("rustix_test_can_compile");
|
||||
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(out_file)
|
||||
.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()
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
//! 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 _;
|
||||
|
||||
// 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 = "android",
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the file descriptor associated with the directory stream.
|
||||
///
|
||||
/// The file descriptor is used internally by the directory stream. As a result, it is useful
|
||||
/// only for functions which do not depend or alter the file position.
|
||||
///
|
||||
/// # References
|
||||
///
|
||||
/// - [POSIX]
|
||||
///
|
||||
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/dirfd.html
|
||||
#[inline]
|
||||
#[doc(alias = "dirfd")]
|
||||
pub fn fd<'a>(&'a self) -> io::Result<BorrowedFd<'a>> {
|
||||
let raw_fd = unsafe { c::dirfd(self.libc_dir.as_ptr()) };
|
||||
if raw_fd < 0 {
|
||||
Err(io::Errno::last_os_error())
|
||||
} else {
|
||||
Ok(unsafe { BorrowedFd::borrow_raw(raw_fd) })
|
||||
}
|
||||
}
|
||||
|
||||
/// 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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#[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 = "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
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod syscalls;
|
||||
@@ -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,
|
||||
))
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub(crate) mod syscalls;
|
||||
pub(crate) mod types;
|
||||
@@ -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()) }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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(all(linux_kernel, not(target_os = "android")))]
|
||||
#[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.
|
||||
@@ -0,0 +1,2 @@
|
||||
pub(crate) mod syscalls;
|
||||
pub(crate) mod types;
|
||||
@@ -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,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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) }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
pub(crate) mod addr;
|
||||
pub(crate) mod ext;
|
||||
#[cfg(not(any(
|
||||
windows,
|
||||
target_os = "espidf",
|
||||
target_os = "horizon",
|
||||
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;
|
||||
@@ -0,0 +1,183 @@
|
||||
//! 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",
|
||||
target_os = "redox",
|
||||
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 = "vita", target_os = "wasi")),
|
||||
not(any(
|
||||
target_os = "android",
|
||||
target_os = "redox",
|
||||
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 = "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(unix)]
|
||||
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() }
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
@@ -0,0 +1,434 @@
|
||||
//! 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 = "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 = "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 = "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 = "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))
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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!("")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod auxv;
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod syscalls;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub(crate) mod syscalls;
|
||||
pub(crate) mod types;
|
||||
@@ -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) }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod syscalls;
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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) }))
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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};
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod syscalls;
|
||||
@@ -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))) }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub(crate) mod syscalls;
|
||||
pub(crate) mod types;
|
||||
@@ -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()))
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub(crate) mod syscalls;
|
||||
pub(crate) mod types;
|
||||
@@ -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))) }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#[cfg(not(windows))]
|
||||
pub(crate) mod syscalls;
|
||||
pub(crate) mod types;
|
||||
@@ -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)) }
|
||||
}
|
||||
@@ -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;
|
||||
@@ -0,0 +1,2 @@
|
||||
pub(crate) mod syscalls;
|
||||
pub(crate) mod types;
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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()),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -0,0 +1,3 @@
|
||||
#[cfg(not(windows))]
|
||||
pub(crate) mod syscalls;
|
||||
pub(crate) mod types;
|
||||
@@ -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(×pec),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod syscalls;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
Loaded 100 of 339 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user