Vendor dependencies for 0.3.0 release

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

File diff suppressed because one or more lines are too long

View File

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

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

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

29
vendor/rustix-0.38.44/COPYRIGHT vendored Normal file
View File

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

1012
vendor/rustix-0.38.44/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

308
vendor/rustix-0.38.44/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,308 @@
# 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 = "0.38.44"
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",
]
[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.compiler_builtins]
version = "0.1.49"
optional = true
[dependencies.core]
version = "1.0.0"
optional = true
package = "rustc-std-workspace-core"
[dependencies.itoa]
version = "1.0.13"
optional = true
default-features = false
[dependencies.rustc-std-workspace-alloc]
version = "1.0.0"
optional = true
[dev-dependencies.flate2]
version = "1.0"
[dev-dependencies.libc]
version = "0.2.161"
[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"
[features]
all-apis = [
"event",
"fs",
"io_uring",
"mm",
"mount",
"net",
"param",
"pipe",
"process",
"procfs",
"pty",
"rand",
"runtime",
"shm",
"stdio",
"system",
"termios",
"thread",
"time",
]
alloc = []
cc = []
default = [
"std",
"use-libc-auxv",
]
event = []
fs = []
io_uring = [
"event",
"fs",
"net",
"linux-raw-sys/io_uring",
]
libc-extra-traits = ["libc?/extra_traits"]
linux_4_11 = []
linux_latest = ["linux_4_11"]
mm = []
mount = []
net = [
"linux-raw-sys/net",
"linux-raw-sys/netlink",
"linux-raw-sys/if_ether",
"linux-raw-sys/xdp",
]
param = ["fs"]
pipe = []
process = ["linux-raw-sys/prctl"]
procfs = [
"once_cell",
"itoa",
"fs",
]
pty = [
"itoa",
"fs",
]
rand = []
runtime = ["linux-raw-sys/prctl"]
rustc-dep-of-std = [
"core",
"rustc-std-workspace-alloc",
"compiler_builtins",
"linux-raw-sys/rustc-dep-of-std",
"bitflags/rustc-dep-of-std",
"compiler_builtins?/rustc-dep-of-std",
]
shm = ["fs"]
std = [
"bitflags/std",
"alloc",
"libc?/std",
"libc_errno?/std",
"libc-extra-traits",
]
stdio = []
system = ["linux-raw-sys/system"]
termios = []
thread = ["linux-raw-sys/prctl"]
time = []
try_close = []
use-explicitly-provided-auxv = []
use-libc = [
"libc_errno",
"libc",
"libc-extra-traits",
]
use-libc-auxv = []
[target.'cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", 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.4.14"
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", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", 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.161"
optional = true
default-features = false
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_endian = "little", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", 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", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", 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.4.14"
features = [
"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", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", 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.161"
default-features = false
[target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", 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(any(target_os = "android", target_os = "linux"))'.dependencies.once_cell]
version = "1.5.2"
optional = true
[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.59"
features = [
"Win32_Foundation",
"Win32_Networking_WinSock",
"Win32_NetworkManagement_IpHelper",
"Win32_System_Threading",
]
[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(fix_y2038)",
"cfg(freebsdlike)",
"cfg(libc)",
"cfg(linux_kernel)",
"cfg(linux_like)",
"cfg(linux_raw)",
"cfg(netbsdlike)",
"cfg(rustc_attrs)",
"cfg(solarish)",
"cfg(staged_api)",
"cfg(static_assertions)",
"cfg(thumb_mode)",
"cfg(wasi)",
"cfg(wasi_ext)",
'cfg(target_arch, values("xtensa"))',
]

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

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

View File

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

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

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

View File

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

196
vendor/rustix-0.38.44/README.md vendored Normal file
View File

@@ -0,0 +1,196 @@
<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::fd`], and [`rustix::ffi`] are enabled by
default. The rest of the API is 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. |
| `procfs` | [`rustix::procfs`]—Utilities for reading `/proc` on Linux. |
| `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. |
[`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::procfs`]: https://docs.rs/rustix/*/rustix/procfs/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
## 64-bit Large File Support (LFS) and Year 2038 (y2038) support
`rustix` automatically uses 64-bit APIs when available, and avoids exposing
32-bit APIs that would have the year-2038 problem or fail to support large
files. For instance, `rustix::fstatvfs` calls `fstatvfs64`, and returns a
struct that's 64-bit even on 32-bit platforms.
## Similar crates
`rustix` is similar to [`nix`], [`simple_libc`], [`unix`], [`nc`], [`uapi`],
and [`rusl`]. `rustix` is architected for [I/O safety] with most APIs using
[`OwnedFd`] and [`AsFd`] to manipulate file descriptors rather than `File` or
even `c_int`, and supporting multiple backends so that it can use direct
syscalls while still being usable on all platforms `libc` supports. Like `nix`,
`rustix` has an optimized and flexible filename argument mechanism that allows
users to use a variety of string types, including non-UTF-8 string types.
[`relibc`] is a similar project which aims to be a full "libc", including
C-compatible interfaces and higher-level C/POSIX standard-library
functionality; `rustix` just aims to provide safe and idiomatic Rust interfaces
to low-level syscalls. `relibc` also doesn't tend to support features not
supported on Redox, such as `*at` functions like `openat`, which are important
features for `rustix`.
`rustix` has its own code for making direct syscalls, similar to the
[`syscall`], [`sc`], and [`scall`] crates, using the Rust `asm!` macro.
`rustix` can also use Linux's vDSO mechanism to optimize Linux `clock_gettime`
on all architectures, and all Linux system calls on x86. And `rustix`'s
syscalls report errors using an optimized `Errno` type.
`rustix`'s `*at` functions are similar to the [`openat`] crate, but `rustix`
provides them as free functions rather than associated functions of a `Dir`
type. `rustix`'s `CWD` constant exposes the special `AT_FDCWD` value in a safe
way, so users don't need to open `.` to get a current-directory handle.
`rustix`'s `openat2` function is similar to the [`openat2`] crate, but uses I/O
safety types rather than `RawFd`. `rustix` does not provide dynamic feature
detection, so users must handle the [`NOSYS`] error themselves.
`rustix`'s `termios` module is similar to the [`termios`] crate, but uses I/O
safety types rather than `RawFd`, and the flags parameters to functions such as
`tcsetattr` are `enum`s rather than bare integers. And, rustix calls its
`tcgetattr` function `tcgetattr`, rather than `Termios::from_fd`.
## Minimum Supported Rust Version (MSRV)
This crate currently works on the version of [Rust on Debian stable], which is
currently [Rust 1.63]. This policy may change in the future, in minor version
releases, so users using a fixed version of Rust should pin to a specific
version of this crate.
## Minimum Linux Version
On Linux platforms, rustix requires at least Linux 3.2. This is at most the
oldest Linux version supported by:
- [any current Rust target], or
- [kernel.org] at the time of rustix's [MSRV] release.
The specifics of this policy may change in the future, but we intend it to
always reflect “very old” Linux versions.
[MSRV]: #minimum-supported-rust-version-msrv
[Rust 1.63]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html
[any current Rust target]: https://doc.rust-lang.org/nightly/rustc/platform-support.html
[kernel.org]: https://www.kernel.org/releases.html
[Rust on Debian stable]: https://packages.debian.org/stable/rust/rustc
[Windows Sockets 2]: https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-start-page-2
[`nix`]: https://crates.io/crates/nix
[`unix`]: https://crates.io/crates/unix
[`nc`]: https://crates.io/crates/nc
[`simple_libc`]: https://crates.io/crates/simple_libc
[`uapi`]: https://crates.io/crates/uapi
[`rusl`]: https://lib.rs/crates/rusl
[`relibc`]: https://gitlab.redox-os.org/redox-os/relibc
[`syscall`]: https://crates.io/crates/syscall
[`sc`]: https://crates.io/crates/sc
[`scall`]: https://crates.io/crates/scall
[`openat`]: https://crates.io/crates/openat
[`openat2`]: https://crates.io/crates/openat2
[I/O safety types]: https://doc.rust-lang.org/stable/std/os/fd/index.html#structs
[`termios`]: https://crates.io/crates/termios
[`libc`]: https://crates.io/crates/libc
[`windows-sys`]: https://crates.io/crates/windows-sys
[`cap-std`]: https://crates.io/crates/cap-std
[`memfd`]: https://crates.io/crates/memfd
[`timerfd`]: https://crates.io/crates/timerfd
[`io-streams`]: https://crates.io/crates/io-streams
[`bitflags`]: https://crates.io/crates/bitflags
[`Arg`]: https://docs.rs/rustix/*/rustix/path/trait.Arg.html
[I/O-safe]: https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md
[I/O safety]: https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md
[provenance]: https://github.com/rust-lang/rust/issues/95228
[`OwnedFd`]: https://doc.rust-lang.org/stable/std/os/fd/struct.OwnedFd.html
[`AsFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.AsFd.html
[`NOSYS`]: https://docs.rs/rustix/*/rustix/io/struct.Errno.html#associatedconstant.NOSYS

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

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

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

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

254
vendor/rustix-0.38.44/build.rs vendored Normal file
View File

@@ -0,0 +1,254 @@
use std::env::var;
use std::io::Write;
/// 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 `--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");
}
// Feature needed for testing.
if use_static_assertions() {
use_feature("static_assertions");
}
// WASI support can utilize wasi_ext if present.
if os == "wasi" {
use_feature_or_nothing("wasi_ext");
}
// 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 == "powerpc64" || arch == "s390x" || arch.starts_with("mips"))
&& !rustix_use_experimental_asm);
if libc {
// Use the libc backend.
use_feature("libc");
} else {
// Use the linux_raw backend.
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 == "mips"
|| arch == "sparc"
|| arch == "x86"
|| (arch == "wasm32" && os == "emscripten")
|| (arch == "aarch64" && os == "linux" && abi == Ok("ilp32".to_string())))
&& (apple
|| os == "android"
|| os == "emscripten"
|| 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 use_feature_or_nothing(feature: &str) {
if has_feature(feature) {
use_feature(feature);
}
}
fn use_feature(feature: &str) {
println!("cargo:rustc-cfg={}", feature);
}
/// Test whether the rustc at `var("RUSTC")` supports the given feature.
fn has_feature(feature: &str) -> bool {
can_compile(format!(
"#![allow(stable_features)]\n#![feature({})]",
feature
))
}
/// Test whether the rustc at `var("RUSTC")` can compile the given code.
fn can_compile<T: AsRef<str>>(test: T) -> bool {
use std::process::Stdio;
let rustc = var("RUSTC").unwrap();
let target = var("TARGET").unwrap();
// Use `RUSTC_WRAPPER` if it's set, unless it's set to an empty string, as
// documented [here].
// [here]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads
let wrapper = var("RUSTC_WRAPPER")
.ok()
.and_then(|w| if w.is_empty() { None } else { Some(w) });
let mut cmd = if let Some(wrapper) = wrapper {
let mut cmd = std::process::Command::new(wrapper);
// The wrapper's first argument is supposed to be the path to rustc.
cmd.arg(rustc);
cmd
} else {
std::process::Command::new(rustc)
};
cmd.arg("--crate-type=rlib") // Don't require `main`.
.arg("--emit=metadata") // Do as little as possible but still parse.
.arg("--target")
.arg(target)
.arg("-o")
.arg("-")
.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()
}

View File

@@ -0,0 +1,485 @@
//! 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_kernel, feature = "net"))]
pub(crate) const ETH_P_TSN: c_int = linux_raw_sys::if_ether::ETH_P_TSN as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_ERSPAN2: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN2 as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_ERSPAN: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_PROFINET: c_int = linux_raw_sys::if_ether::ETH_P_PROFINET as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_REALTEK: c_int = linux_raw_sys::if_ether::ETH_P_REALTEK as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_ETHERCAT: c_int = linux_raw_sys::if_ether::ETH_P_ETHERCAT as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_PREAUTH: c_int = linux_raw_sys::if_ether::ETH_P_PREAUTH as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_LLDP: c_int = linux_raw_sys::if_ether::ETH_P_LLDP as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_MRP: c_int = linux_raw_sys::if_ether::ETH_P_MRP as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_NCSI: c_int = linux_raw_sys::if_ether::ETH_P_NCSI as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_CFM: c_int = linux_raw_sys::if_ether::ETH_P_CFM as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_IBOE: c_int = linux_raw_sys::if_ether::ETH_P_IBOE as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_HSR: c_int = linux_raw_sys::if_ether::ETH_P_HSR as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_NSH: c_int = linux_raw_sys::if_ether::ETH_P_NSH as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_DSA_8021Q: c_int = linux_raw_sys::if_ether::ETH_P_DSA_8021Q as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_DSA_A5PSW: c_int = linux_raw_sys::if_ether::ETH_P_DSA_A5PSW as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_IFE: c_int = linux_raw_sys::if_ether::ETH_P_IFE as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_CAN: c_int = linux_raw_sys::if_ether::ETH_P_CAN as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_CANXL: c_int = linux_raw_sys::if_ether::ETH_P_CANXL as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_XDSA: c_int = linux_raw_sys::if_ether::ETH_P_XDSA as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_MAP: c_int = linux_raw_sys::if_ether::ETH_P_MAP as _;
#[cfg(all(linux_kernel, feature = "net"))]
pub(crate) const ETH_P_MCTP: c_int = linux_raw_sys::if_ether::ETH_P_MCTP as _;
#[cfg(all(
linux_kernel,
any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
)
))]
pub(crate) const SIGEMT: c_int = linux_raw_sys::general::SIGEMT as _;
// TODO: Upstream these.
#[cfg(all(linux_kernel, feature = "termios"))]
pub(crate) const IUCLC: tcflag_t = linux_raw_sys::general::IUCLC as _;
#[cfg(all(linux_kernel, 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 = libc::MSG_NONBLOCK;
// `O_LARGEFILE` can be automatically set by the kernel on Linux:
// <https://github.com/torvalds/linux/blob/v6.7/fs/open.c#L1458-L1459>
// so libc implementations may leave it undefined or defined to zero.
#[cfg(linux_kernel)]
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;
// TODO: This is new in Linux 6.11; remove when linux-raw-sys is updated.
#[cfg(linux_kernel)]
pub(crate) const MAP_DROPPABLE: u32 = 0x8;
// 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 libc::{
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 = libc::CBAUD << libc::IBSHIFT;
// Automatically enable “large file” support (LFS) features.
#[cfg(target_os = "vxworks")]
pub(super) use libc::_Vx_ticks64_t as _Vx_ticks_t;
#[cfg(linux_kernel)]
pub(super) use libc::fallocate64 as fallocate;
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
#[cfg(any(linux_like, target_os = "aix"))]
pub(super) use libc::open64 as open;
#[cfg(any(
linux_kernel,
target_os = "aix",
target_os = "hurd",
target_os = "l4re"
))]
pub(super) use libc::posix_fallocate64 as posix_fallocate;
#[cfg(any(all(linux_like, not(target_os = "android")), target_os = "aix"))]
pub(super) use libc::{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 libc::{
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 libc::{
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 libc::{
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 libc::{lstat64 as lstat, stat64 as stat};
#[cfg(any(
linux_kernel,
target_os = "aix",
target_os = "hurd",
target_os = "emscripten"
))]
pub(super) use libc::{pread64 as pread, pwrite64 as pwrite};
#[cfg(any(target_os = "linux", target_os = "hurd", target_os = "emscripten"))]
pub(super) use libc::{preadv64 as preadv, pwritev64 as pwritev};
#[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]
pub(super) unsafe fn prlimit(
pid: libc::pid_t,
resource: libc::__rlimit_resource_t,
new_limit: *const libc::rlimit64,
old_limit: *mut libc::rlimit64,
) -> libc::c_int {
// `prlimit64` wasn't supported in glibc until 2.13.
weak_or_syscall! {
fn prlimit64(
pid: libc::pid_t,
resource: libc::__rlimit_resource_t,
new_limit: *const libc::rlimit64,
old_limit: *mut libc::rlimit64
) via SYS_prlimit64 -> libc::c_int
}
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub(super) unsafe fn prlimit(
pid: libc::pid_t,
resource: libc::c_int,
new_limit: *const libc::rlimit64,
old_limit: *mut libc::rlimit64,
) -> libc::c_int {
weak_or_syscall! {
fn prlimit64(
pid: libc::pid_t,
resource: libc::c_int,
new_limit: *const libc::rlimit64,
old_limit: *mut libc::rlimit64
) via SYS_prlimit64 -> libc::c_int
}
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(target_os = "android")]
pub(super) unsafe fn prlimit(
pid: libc::pid_t,
resource: libc::c_int,
new_limit: *const libc::rlimit64,
old_limit: *mut libc::rlimit64,
) -> libc::c_int {
weak_or_syscall! {
fn prlimit64(
pid: libc::pid_t,
resource: libc::c_int,
new_limit: *const libc::rlimit64,
old_limit: *mut libc::rlimit64
) via SYS_prlimit64 -> libc::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: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off64_t,
) -> libc::ssize_t {
// Older Android libc lacks `preadv64`, so use the `weak!` mechanism to
// test for it, and call back to `libc::syscall`. We don't use
// `weak_or_syscall` here because we need to pass the 64-bit offset
// specially.
weak! {
fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, libc::off64_t) -> libc::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: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize
) via SYS_preadv -> libc::ssize_t
}
preadv(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize)
}
}
pub(in super::super) unsafe fn pwritev64(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off64_t,
) -> libc::ssize_t {
// See the comments in `preadv64`.
weak! {
fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, libc::off64_t) -> libc::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: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize
) via SYS_pwritev -> libc::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 {
weakcall! {
pub(in super::super) fn preadv(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off_t
) -> libc::ssize_t
}
weakcall! {
pub(in super::super) fn pwritev(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int, offset: libc::off_t
) -> libc::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: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off64_t,
flags: libc::c_int,
) -> libc::ssize_t {
// Older glibc lacks `preadv64v2`, so use the `weak!` mechanism to
// test for it, and call back to `libc::syscall`. We don't use
// `weak_or_syscall` here because we need to pass the 64-bit offset
// specially.
weak! {
fn preadv64v2(libc::c_int, *const libc::iovec, libc::c_int, libc::off64_t, libc::c_int) -> libc::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: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize,
flags: libc::c_int
) via SYS_preadv2 -> libc::ssize_t
}
preadv2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
}
pub(in super::super) unsafe fn pwritev64v2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off64_t,
flags: libc::c_int,
) -> libc::ssize_t {
// See the comments in `preadv64v2`.
weak! {
fn pwritev64v2(libc::c_int, *const libc::iovec, libc::c_int, libc::off64_t, libc::c_int) -> libc::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: libc::c_int,
iov: *const libc::iovec,
iovec: libc::c_int,
offset_lo: usize,
offset_hi: usize,
flags: libc::c_int
) via SYS_pwritev2 -> libc::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: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off64_t,
flags: libc::c_int,
) -> libc::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: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize,
flags: libc::c_int
) via SYS_preadv2 -> libc::ssize_t
}
preadv2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
pub(in super::super) unsafe fn pwritev64v2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off64_t,
flags: libc::c_int,
) -> libc::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: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize,
flags: libc::c_int
) via SYS_pwritev2 -> libc::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};

View File

@@ -0,0 +1,261 @@
//! 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;
use super::fd::{AsRawFd, BorrowedFd, FromRawFd, 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 = "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 = "redox", 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 = "redox", 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)
}
/// Convert the value to the `msg_iovlen` field of a `msghdr` struct.
#[cfg(all(
not(any(windows, target_os = "espidf", target_os = "redox", target_os = "wasi")),
any(
target_os = "android",
all(
target_os = "linux",
not(target_env = "musl"),
not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips")))
)
)
))]
#[inline]
pub(super) fn msg_iov_len(len: usize) -> c::size_t {
len
}
/// Convert the value to the `msg_iovlen` field of a `msghdr` struct.
#[cfg(all(
not(any(
windows,
target_os = "espidf",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)),
not(any(
target_os = "android",
all(
target_os = "linux",
not(target_env = "musl"),
not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips")))
)
))
))]
#[inline]
pub(crate) 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 = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
))]
#[inline]
pub(crate) 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 = "emscripten",
target_os = "espidf",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
pub(crate) fn msg_control_len(len: usize) -> c::size_t {
len
}

View File

@@ -0,0 +1,75 @@
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`
#[cfg(not(target_os = "android"))]
const EXCLUSIVE = bitcast!(c::EPOLLEXCLUSIVE);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

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

View File

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

View File

@@ -0,0 +1,453 @@
//! 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(feature = "alloc")]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
use crate::backend::conv::ret_u32;
#[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;
use crate::io;
#[cfg(solarish)]
use crate::utils::as_mut_ptr;
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
use crate::utils::as_ptr;
#[cfg(any(
all(feature = "alloc", bsd),
solarish,
all(feature = "alloc", any(linux_kernel, target_os = "redox")),
))]
use core::mem::MaybeUninit;
#[cfg(any(bsd, linux_kernel, 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(
linux_kernel,
solarish,
target_os = "redox",
all(feature = "alloc", bsd)
))]
use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd};
#[cfg(any(
linux_kernel,
solarish,
target_os = "freebsd",
target_os = "illumos",
target_os = "espidf",
target_os = "redox",
all(feature = "alloc", bsd)
))]
use {crate::backend::conv::ret_owned_fd, crate::fd::OwnedFd};
#[cfg(all(feature = "alloc", bsd))]
use {crate::event::kqueue::Event, crate::utils::as_ptr};
#[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(all(feature = "alloc", bsd))]
pub(crate) fn kqueue() -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::kqueue()) }
}
#[cfg(all(feature = "alloc", bsd))]
pub(crate) unsafe fn kevent(
kq: BorrowedFd<'_>,
changelist: &[Event],
eventlist: &mut [MaybeUninit<Event>],
timeout: Option<&c::timespec>,
) -> io::Result<c::c_int> {
ret_c_int(c::kevent(
borrowed_fd(kq),
changelist.as_ptr().cast(),
changelist
.len()
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
eventlist.as_mut_ptr().cast(),
eventlist
.len()
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
timeout.map_or(null(), as_ptr),
))
}
#[inline]
pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> {
let nfds = fds
.len()
.try_into()
.map_err(|_convert_err| 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<&crate::timespec::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::OVERFLOW)?,
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<&crate::timespec::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::OVERFLOW)?,
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<&mut c::timespec>,
) -> io::Result<Event> {
let mut event = MaybeUninit::<c::port_event>::uninit();
let timeout = timeout.map_or(null_mut(), as_mut_ptr);
unsafe {
ret(c::port_get(borrowed_fd(port), event.as_mut_ptr(), timeout))?;
}
// If we're done, initialize the event and return it.
Ok(Event(unsafe { event.assume_init() }))
}
#[cfg(all(feature = "alloc", solarish))]
pub(crate) fn port_getn(
port: BorrowedFd<'_>,
timeout: Option<&mut c::timespec>,
events: &mut Vec<Event>,
mut nget: u32,
) -> io::Result<()> {
// `port_getn` special-cases a max value of 0 to be a query that returns
// the number of events. We don't want to do the `set_len` in that case, so
// so bail out early if needed.
if events.capacity() == 0 {
return Ok(());
}
let timeout = timeout.map_or(null_mut(), as_mut_ptr);
unsafe {
ret(c::port_getn(
borrowed_fd(port),
events.as_mut_ptr().cast(),
events.capacity().try_into().unwrap(),
&mut nget,
timeout,
))?;
}
// Update the vector length.
unsafe {
events.set_len(nget.try_into().unwrap());
}
Ok(())
}
#[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(feature = "alloc")]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) fn epoll_wait(
epoll: BorrowedFd<'_>,
events: &mut [MaybeUninit<crate::event::epoll::Event>],
timeout: c::c_int,
) -> io::Result<usize> {
unsafe {
ret_u32(c::epoll_wait(
borrowed_fd(epoll),
events.as_mut_ptr().cast::<c::epoll_event>(),
events.len().try_into().unwrap_or(i32::MAX),
timeout,
))
.map(|i| i.try_into().unwrap_or(usize::MAX))
}
}

View File

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

View File

@@ -0,0 +1,74 @@
//! Windows system calls in the `event` module.
use crate::backend::c;
use crate::backend::conv::ret_c_int;
use crate::event::{FdSetElement, PollFd};
use crate::io;
pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> {
let nfds = fds
.len()
.try_into()
.map_err(|_convert_err| 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,
))
}
}

View File

@@ -0,0 +1,424 @@
#[cfg(not(any(solarish, 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 = "netbsd",
target_os = "nto",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
use crate::fs::{fstatfs, StatFs};
#[cfg(not(any(
solarish,
target_os = "haiku",
target_os = "redox",
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;
#[cfg(not(any(linux_like, target_os = "hurd")))]
use c::readdir as libc_readdir;
#[cfg(any(linux_like, target_os = "hurd"))]
use c::readdir64 as libc_readdir;
use core::fmt;
use core::ptr::NonNull;
use libc_errno::{errno, set_errno, Errno};
/// `DIR*`
pub struct Dir {
/// The `libc` `DIR` pointer.
libc_dir: NonNull<c::DIR>,
/// Have we seen any errors in this iteration?
any_errors: bool,
}
impl Dir {
/// Take ownership of `fd` and construct a `Dir` that reads entries from
/// the given directory file descriptor.
#[inline]
pub fn new<Fd: Into<OwnedFd>>(fd: Fd) -> io::Result<Self> {
Self::_new(fd.into())
}
#[inline]
fn _new(fd: OwnedFd) -> io::Result<Self> {
let raw = owned_fd(fd);
unsafe {
let libc_dir = c::fdopendir(raw);
if let Some(libc_dir) = NonNull::new(libc_dir) {
Ok(Self {
libc_dir,
any_errors: false,
})
} else {
let err = io::Errno::last_os_error();
let _ = c::close(raw);
Err(err)
}
}
}
/// Borrow `fd` and construct a `Dir` that reads entries from the given
/// directory file descriptor.
#[inline]
pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> {
Self::_read_from(fd.as_fd())
}
#[inline]
#[allow(unused_mut)]
fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> {
let mut any_errors = false;
// Given an arbitrary `OwnedFd`, it's impossible to know whether the
// user holds a `dup`'d copy which could continue to modify the
// file description state, which would cause Undefined Behavior after
// our call to `fdopendir`. To prevent this, we obtain an independent
// `OwnedFd`.
let flags = fcntl_getfl(fd)?;
let fd_for_dir = match openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty()) {
Ok(fd) => fd,
#[cfg(not(target_os = "wasi"))]
Err(io::Errno::NOENT) => {
// If "." doesn't exist, it means the directory was removed.
// We treat that as iterating through a directory with no
// entries.
any_errors = true;
crate::io::dup(fd)?
}
Err(err) => return Err(err),
};
let raw = owned_fd(fd_for_dir);
unsafe {
let libc_dir = c::fdopendir(raw);
if let Some(libc_dir) = NonNull::new(libc_dir) {
Ok(Self {
libc_dir,
any_errors,
})
} else {
let err = io::Errno::last_os_error();
let _ = c::close(raw);
Err(err)
}
}
}
/// `rewinddir(self)`
#[inline]
pub fn rewind(&mut self) {
self.any_errors = false;
unsafe { c::rewinddir(self.libc_dir.as_ptr()) }
}
/// `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(freebsdlike, netbsdlike))]
d_fileno: dirent.d_fileno,
name: CStr::from_ptr(dirent.d_name.as_ptr()).to_owned(),
};
Some(Ok(result))
}
}
}
/// `fstat(self)`
#[cfg(not(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 = "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 = "haiku",
target_os = "redox",
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 = "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(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,
}
impl DirEntry {
/// Returns the file name of this directory entry.
#[inline]
pub fn file_name(&self) -> &CStr {
&self.name
}
/// 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,
)
}
);
}
#[test]
fn dir_iterator_handles_io_errors() {
// create a dir, keep the FD, then delete the dir
let tmp = tempfile::tempdir().unwrap();
let fd = crate::fs::openat(
crate::fs::CWD,
tmp.path(),
crate::fs::OFlags::RDONLY | crate::fs::OFlags::CLOEXEC,
crate::fs::Mode::empty(),
)
.unwrap();
let file_fd = crate::fs::openat(
&fd,
tmp.path().join("test.txt"),
crate::fs::OFlags::WRONLY | crate::fs::OFlags::CREATE,
crate::fs::Mode::RWXU,
)
.unwrap();
let mut dir = Dir::read_from(&fd).unwrap();
// Reach inside the `Dir` and replace its directory with a file, which
// will cause the subsequent `readdir` to fail.
unsafe {
let raw_fd = c::dirfd(dir.libc_dir.as_ptr());
let mut owned_fd: crate::fd::OwnedFd = crate::fd::FromRawFd::from_raw_fd(raw_fd);
crate::io::dup2(&file_fd, &mut owned_fd).unwrap();
core::mem::forget(owned_fd);
}
// FreeBSD and macOS seem to read some directory entries before we call
// `.next()`.
#[cfg(any(apple, freebsdlike))]
{
dir.rewind();
}
assert!(matches!(dir.next(), Some(Err(_))));
assert!(dir.next().is_none());
}

View File

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

View File

@@ -0,0 +1,138 @@
#[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
use crate::backend::c;
use crate::fs::Dev;
#[cfg(not(any(
apple,
solarish,
target_os = "aix",
target_os = "android",
target_os = "emscripten",
)))]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
c::makedev(maj, min)
}
#[cfg(solarish)]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// SAFETY: Solarish's `makedev` is marked unsafe but it isn't doing
// anything unsafe.
unsafe { c::makedev(maj, min) }
}
#[cfg(all(target_os = "android", not(target_pointer_width = "32")))]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
c::makedev(maj, min)
}
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do
// it ourselves.
((u64::from(maj) & 0xffff_f000_u64) << 32)
| ((u64::from(maj) & 0x0000_0fff_u64) << 8)
| ((u64::from(min) & 0xffff_ff00_u64) << 12)
| (u64::from(min) & 0x0000_00ff_u64)
}
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// Emscripten's `makedev` has a 32-bit return value.
Dev::from(c::makedev(maj, min))
}
#[cfg(apple)]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// Apple's `makedev` oddly has signed argument types and is `unsafe`.
unsafe { c::makedev(maj as i32, min as i32) }
}
#[cfg(target_os = "aix")]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// AIX's `makedev` oddly is `unsafe`.
unsafe { c::makedev(maj, min) }
}
#[cfg(not(any(
apple,
freebsdlike,
target_os = "android",
target_os = "emscripten",
target_os = "netbsd"
)))]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
unsafe { c::major(dev) }
}
#[cfg(any(
apple,
freebsdlike,
target_os = "netbsd",
all(target_os = "android", not(target_pointer_width = "32")),
))]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
// On some platforms `major` oddly has signed return types.
(unsafe { c::major(dev) }) as u32
}
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
// 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do
// it ourselves.
(((dev >> 31 >> 1) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)) as u32
}
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
// Emscripten's `major` has a 32-bit argument value.
unsafe { c::major(dev as u32) }
}
#[cfg(not(any(
apple,
freebsdlike,
target_os = "android",
target_os = "emscripten",
target_os = "netbsd"
)))]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
unsafe { c::minor(dev) }
}
#[cfg(any(
apple,
freebsdlike,
target_os = "netbsd",
all(target_os = "android", not(target_pointer_width = "32"))
))]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
// On some platforms, `minor` oddly has signed return types.
(unsafe { c::minor(dev) }) as u32
}
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
// 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do
// it ourselves.
(((dev >> 12) & 0xffff_ff00) | (dev & 0x0000_00ff)) as u32
}
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
// Emscripten's `minor` has a 32-bit argument value.
unsafe { c::minor(dev as u32) }
}

View File

@@ -0,0 +1,23 @@
#[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 = "redox",
target_os = "vita",
target_os = "wasi"
)))]
pub(crate) mod makedev;
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;
// TODO: Fix linux-raw-sys to define ioctl codes for sparc.
#[cfg(all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")))]
pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::RawOpcode = 0x8008_6610;
#[cfg(all(linux_kernel, not(any(target_arch = "sparc", target_arch = "sparc64"))))]
pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::RawOpcode =
linux_raw_sys::ioctl::EXT4_IOC_RESIZE_FS as crate::ioctl::RawOpcode;

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

View File

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

View File

@@ -0,0 +1,348 @@
//! 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, 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(linux_kernel)]
use crate::io::ReadWriteFlags;
use crate::io::{self, FdFlags};
use crate::ioctl::{IoctlOutput, RawOpcode};
use core::cmp::min;
#[cfg(all(feature = "fs", feature = "net"))]
use libc_errno::errno;
#[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, len: usize) -> io::Result<usize> {
ret_usize(c::read(borrowed_fd(fd), buf.cast(), min(len, 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,
len: usize,
offset: u64,
) -> io::Result<usize> {
let len = min(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.
#[cfg(any(target_os = "espidf", target_os = "vita"))]
let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
ret_usize(c::pread(borrowed_fd(fd), buf.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.
#[cfg(any(target_os = "espidf", target_os = "vita"))]
let offset: i32 = 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 = "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;
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 = "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;
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(linux_kernel)]
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(linux_kernel)]
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: RawOpcode,
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: RawOpcode,
arg: *mut c::c_void,
) -> io::Result<IoctlOutput> {
ioctl(fd, request, arg)
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
#[cfg(all(feature = "fs", feature = "net"))]
pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
use core::mem::MaybeUninit;
let (mut read, mut write) = crate::fs::fd::_is_file_read_write(fd)?;
let mut not_socket = false;
if read {
// Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates
// the read side is shut down; an `EWOULDBLOCK` indicates the read
// side is still open.
match unsafe {
c::recv(
borrowed_fd(fd),
MaybeUninit::<[u8; 1]>::uninit()
.as_mut_ptr()
.cast::<c::c_void>(),
1,
c::MSG_PEEK | c::MSG_DONTWAIT,
)
} {
0 => read = false,
-1 => {
#[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
match errno().0 {
c::EAGAIN | c::EWOULDBLOCK => (),
c::ENOTSOCK => not_socket = true,
err => return Err(io::Errno(err)),
}
}
_ => (),
}
}
if write && !not_socket {
// Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates
// the write side is shut down.
if unsafe { c::send(borrowed_fd(fd), [].as_ptr(), 0, c::MSG_DONTWAIT) } == -1 {
#[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
match errno().0 {
c::EAGAIN | c::EWOULDBLOCK | c::ENOTSOCK => (),
c::EPIPE => write = false,
err => return Err(io::Errno(err)),
}
}
}
Ok((read, write))
}
#[cfg(target_os = "wasi")]
#[cfg(all(feature = "fs", feature = "net"))]
pub(crate) fn is_read_write(_fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
todo!("Implement is_read_write for WASI in terms of fd_fdstat_get");
}
pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> {
let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFD))? };
Ok(FdFlags::from_bits_retain(bitcast!(flags)))
}
pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> {
unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFD, flags.bits())) }
}
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD_CLOEXEC, min)) }
}
#[cfg(target_os = "espidf")]
pub(crate) fn fcntl_dupfd(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD, min)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::dup(borrowed_fd(fd))) }
}
#[allow(clippy::needless_pass_by_ref_mut)]
#[cfg(not(target_os = "wasi"))]
pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> {
unsafe { ret_discarded_fd(c::dup2(borrowed_fd(fd), borrowed_fd(new.as_fd()))) }
}
#[allow(clippy::needless_pass_by_ref_mut)]
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "android",
target_os = "dragonfly",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> {
unsafe {
ret_discarded_fd(c::dup3(
borrowed_fd(fd),
borrowed_fd(new.as_fd()),
bitflags_bits!(flags),
))
}
}
#[cfg(any(
apple,
target_os = "android",
target_os = "dragonfly",
target_os = "haiku",
target_os = "redox",
))]
pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, _flags: DupFlags) -> io::Result<()> {
// Android 5.0 has `dup3`, but libc doesn't have bindings. Emulate it
// using `dup2`. We don't need to worry about the difference between
// `dup2` and `dup3` when the file descriptors are equal because we
// have an `&mut OwnedFd` which means `fd` doesn't alias it.
dup2(fd, new)
}

View File

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

View File

@@ -0,0 +1,37 @@
//! 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};
use crate::backend::fd::LibcFd;
use crate::fd::{BorrowedFd, RawFd};
use crate::io;
use crate::ioctl::{IoctlOutput, RawOpcode};
pub(crate) unsafe fn close(raw_fd: RawFd) {
let _ = c::close(raw_fd as LibcFd);
}
#[cfg(feature = "try_close")]
pub(crate) unsafe fn try_close(raw_fd: RawFd) -> io::Result<()> {
ret(c::close(raw_fd as LibcFd))
}
#[inline]
pub(crate) unsafe fn ioctl(
fd: BorrowedFd<'_>,
request: RawOpcode,
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: RawOpcode,
arg: *mut c::c_void,
) -> io::Result<IoctlOutput> {
ioctl(fd, request, arg)
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,495 @@
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_kernel)]
const SEM = linux_raw_sys::general::PROT_SEM;
/// `PROT_BTI`
#[cfg(all(linux_kernel, target_arch = "aarch64"))]
const BTI = linux_raw_sys::general::PROT_BTI;
/// `PROT_MTE`
#[cfg(all(linux_kernel, target_arch = "aarch64"))]
const MTE = linux_raw_sys::general::PROT_MTE;
/// `PROT_SAO`
#[cfg(all(linux_kernel, any(target_arch = "powerpc", target_arch = "powerpc64")))]
const SAO = linux_raw_sys::general::PROT_SAO;
/// `PROT_ADI`
#[cfg(all(linux_kernel, 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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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(linux_kernel)]
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!(libc::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!(libc::MCL_FUTURE);
/// Lock all pages which are currently mapped into the address space of
/// the process.
const CURRENT = bitcast!(libc::MCL_CURRENT);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,215 @@
//! 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 {
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.FromRawFd.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.FromRawFd.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::{
AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd,
};
#[allow(unused_imports)]
pub(crate) use RawFd as LibcFd;
}
// On Windows we emulate selected libc-compatible interfaces. On non-Windows,
// we just use libc here, since this is the libc backend.
#[cfg_attr(windows, path = "winsock_c.rs")]
pub(crate) mod c;
#[cfg(feature = "event")]
pub(crate) mod event;
#[cfg(not(windows))]
#[cfg(feature = "fs")]
pub(crate) mod fs;
pub(crate) mod io;
#[cfg(linux_kernel)]
#[cfg(feature = "io_uring")]
pub(crate) mod io_uring;
#[cfg(not(any(windows, target_os = "espidf", target_os = "vita", target_os = "wasi")))]
#[cfg(feature = "mm")]
pub(crate) mod mm;
#[cfg(linux_kernel)]
#[cfg(feature = "mount")]
pub(crate) mod mount;
#[cfg(linux_kernel)]
#[cfg(all(feature = "fs", not(feature = "mount")))]
pub(crate) mod mount; // for deprecated mount functions in "fs"
#[cfg(not(any(target_os = "redox", 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 = "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 = "procfs", 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 = "vita",
target_os = "wasi"
)))]
#[cfg(feature = "shm")]
pub(crate) mod shm;
#[cfg(any(feature = "fs", feature = "thread", feature = "process"))]
#[cfg(not(any(windows, target_os = "wasi")))]
pub(crate) mod ugid;
#[cfg(bsd)]
const MAX_IOV: usize = c::IOV_MAX as usize;
#[cfg(any(linux_kernel, target_os = "emscripten", target_os = "nto"))]
const MAX_IOV: usize = c::UIO_MAXIOV as usize;
#[cfg(not(any(
bsd,
linux_kernel,
windows,
target_os = "emscripten",
target_os = "espidf",
target_os = "nto",
target_os = "horizon",
)))]
const MAX_IOV: usize = 16; // The minimum value required by POSIX.

View File

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

View File

@@ -0,0 +1,272 @@
use crate::backend::c;
use crate::backend::conv::ret;
#[cfg(feature = "mount")]
use crate::backend::conv::{borrowed_fd, c_str, ret_owned_fd};
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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(feature = "mount")]
#[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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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)]
#[cfg(feature = "mount")]
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,
))
}
}

View File

@@ -0,0 +1,339 @@
use crate::backend::c;
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: c::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;
/// <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(feature = "mount")]
#[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: c::c_uint {
/// `FSOPEN_CLOEXEC`
const FSOPEN_CLOEXEC = 0x0000_0001;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(feature = "mount")]
#[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: c::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(feature = "mount")]
#[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,
}
#[cfg(feature = "mount")]
#[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: c::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(feature = "mount")]
#[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: c::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(feature = "mount")]
#[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: c::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(feature = "mount")]
#[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: c::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: c::c_ulong {
/// `MS_SILENT`
const SILENT = c::MS_SILENT;
/// `MS_SHARED`
const SHARED = c::MS_SHARED;
/// `MS_PRIVATE`
const PRIVATE = c::MS_PRIVATE;
/// `MS_SLAVE`
const SLAVE = c::MS_SLAVE;
/// `MS_UNBINDABLE`
const UNBINDABLE = c::MS_UNBINDABLE;
/// `MS_REC`
const REC = c::MS_REC;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub(crate) struct InternalMountFlags: c::c_ulong {
const REMOUNT = c::MS_REMOUNT;
const MOVE = c::MS_MOVE;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[cfg(linux_kernel)]
pub(crate) struct MountFlagsArg(pub(crate) c::c_ulong);

View File

@@ -0,0 +1,259 @@
//! Socket address utilities.
use crate::backend::c;
#[cfg(unix)]
use {
crate::ffi::CStr,
crate::io,
crate::path,
core::cmp::Ordering,
core::fmt,
core::hash::{Hash, Hasher},
core::slice,
};
/// `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 bytes = path.to_bytes_with_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,
})
}
const fn init() -> c::sockaddr_un {
c::sockaddr_un {
#[cfg(any(
bsd,
target_os = "aix",
target_os = "haiku",
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 = "nto"))]
sun_path: [0; 104],
#[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", 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]
pub fn path(&self) -> Option<&CStr> {
let len = self.len();
if len != 0 && self.unix.sun_path[0] != 0 {
let end = len as usize - offsetof_sun_path();
let bytes = &self.unix.sun_path[..end];
// SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
// And `from_bytes_with_nul_unchecked` since the string is
// NUL-terminated.
unsafe {
Some(CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
bytes.as_ptr().cast(),
bytes.len(),
)))
}
} else {
None
}
}
/// For an abstract address, return the identifier.
#[cfg(linux_kernel)]
#[inline]
pub fn abstract_name(&self) -> Option<&[u8]> {
let len = self.len();
if len != 0 && self.unix.sun_path[0] == 0 {
let end = len as usize - offsetof_sun_path();
let bytes = &self.unix.sun_path[1..end];
// SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
unsafe { Some(slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len())) }
} else {
None
}
}
#[inline]
pub(crate) fn addr_len(&self) -> c::socklen_t {
#[cfg(not(any(bsd, target_os = "haiku")))]
{
self.len
}
#[cfg(any(bsd, target_os = "haiku"))]
{
c::socklen_t::from(self.unix.sun_len)
}
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.addr_len() as usize
}
}
#[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 {
if let Some(path) = self.path() {
path.fmt(f)
} else {
#[cfg(linux_kernel)]
if let Some(name) = self.abstract_name() {
return name.fmt(f);
}
"(unnamed)".fmt(f)
}
}
}
/// `struct sockaddr_storage` as a raw struct.
pub type SocketAddrStorage = c::sockaddr_storage;
/// 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 = "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 = "nto"))]
sun_path: [0; 104],
#[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto")))]
sun_path: [0; 108],
#[cfg(target_os = "haiku")]
sun_path: [0; 126],
#[cfg(target_os = "aix")]
sun_path: [0; 1023],
};
(crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize)
}

View File

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

View File

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

View File

@@ -0,0 +1,160 @@
//! 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::backend::conv::{msg_control_len, msg_iov_len};
#[cfg(target_os = "linux")]
use crate::backend::net::write_sockaddr::encode_sockaddr_xdp;
use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
use crate::io::{self, IoSlice, IoSliceMut};
#[cfg(target_os = "linux")]
use crate::net::xdp::SocketAddrXdp;
use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrV4, SocketAddrV6};
use crate::utils::as_ptr;
use core::mem::{size_of, zeroed, MaybeUninit};
/// Create a message header intended to receive a datagram.
pub(crate) fn with_recv_msghdr<R>(
name: &mut MaybeUninit<c::sockaddr_storage>,
iov: &mut [IoSliceMut<'_>],
control: &mut RecvAncillaryBuffer<'_>,
f: impl FnOnce(&mut c::msghdr) -> io::Result<R>,
) -> io::Result<R> {
control.clear();
let namelen = size_of::<c::sockaddr_storage>() as c::socklen_t;
let mut msghdr = {
let mut h = zero_msghdr();
h.msg_name = name.as_mut_ptr().cast();
h.msg_namelen = namelen;
h.msg_iov = iov.as_mut_ptr().cast();
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
};
let res = f(&mut msghdr);
// Reset the control length.
if res.is_ok() {
unsafe {
control.set_control_len(msghdr.msg_controllen.try_into().unwrap_or(usize::MAX));
}
}
res
}
/// Create a message header intended to send without an address.
pub(crate) fn with_noaddr_msghdr<R>(
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
f({
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 an IPv4 address.
pub(crate) fn with_v4_msghdr<R>(
addr: &SocketAddrV4,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
let encoded = encode_sockaddr_v4(addr);
f({
let mut h = zero_msghdr();
h.msg_name = as_ptr(&encoded) as _;
h.msg_namelen = size_of::<SocketAddrV4>() as _;
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 an IPv6 address.
pub(crate) fn with_v6_msghdr<R>(
addr: &SocketAddrV6,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
let encoded = encode_sockaddr_v6(addr);
f({
let mut h = zero_msghdr();
h.msg_name = as_ptr(&encoded) as _;
h.msg_namelen = size_of::<SocketAddrV6>() as _;
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 a Unix address.
#[cfg(all(unix, not(target_os = "redox")))]
pub(crate) fn with_unix_msghdr<R>(
addr: &crate::net::SocketAddrUnix,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
f({
let mut h = zero_msghdr();
h.msg_name = as_ptr(&addr.unix) as _;
h.msg_namelen = addr.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());
h
})
}
/// Create a message header intended to send with an IPv6 address.
#[cfg(target_os = "linux")]
pub(crate) fn with_xdp_msghdr<R>(
addr: &SocketAddrXdp,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
let encoded = encode_sockaddr_xdp(addr);
f({
let mut h = zero_msghdr();
h.msg_name = as_ptr(&encoded) as _;
h.msg_namelen = size_of::<SocketAddrXdp>() as _;
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 zero-initialized message header struct value.
#[cfg(all(unix, not(target_os = "redox")))]
pub(crate) fn zero_msghdr() -> c::msghdr {
// SAFETY: We can't initialize all the fields by value because on some
// platforms the `msghdr` struct in the libc crate contains private padding
// fields. But it is still a C type that's meant to be zero-initializable.
unsafe { zeroed() }
}

View File

@@ -0,0 +1,54 @@
//! Wrappers for netdevice ioctls.
#![allow(unsafe_code)]
#[cfg(feature = "alloc")]
use crate::alloc::string::String;
use crate::backend::io::syscalls::ioctl;
use crate::fd::AsFd;
use crate::io;
#[cfg(feature = "alloc")]
use libc::SIOCGIFNAME;
use libc::{__c_anonymous_ifr_ifru, c_char, ifreq, IFNAMSIZ, SIOCGIFINDEX};
pub(crate) fn name_to_index(fd: impl AsFd, 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.as_fd(), SIOCGIFINDEX as _, &mut ifreq as *mut ifreq as _) }?;
let index = unsafe { ifreq.ifr_ifru.ifru_ifindex };
Ok(index as u32)
}
#[cfg(feature = "alloc")]
pub(crate) fn index_to_name(fd: impl AsFd, index: u32) -> io::Result<String> {
let mut ifreq = ifreq {
ifr_name: [0; 16],
ifr_ifru: __c_anonymous_ifr_ifru {
ifru_ifindex: index as _,
},
};
unsafe { ioctl(fd.as_fd(), SIOCGIFNAME as _, &mut ifreq as *mut ifreq as _) }?;
if let Some(nul_byte) = ifreq.ifr_name.iter().position(|char| *char == 0) {
let name: String = ifreq.ifr_name[..nul_byte]
.iter()
.map(|v| *v as u8 as char)
.collect();
Ok(name)
} else {
Err(io::Errno::INVAL)
}
}

View File

@@ -0,0 +1,337 @@
//! The BSD sockets API requires us to read the `ss_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;
#[cfg(target_os = "linux")]
use crate::net::xdp::{SockaddrXdpFlags, SocketAddrXdp};
use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
use core::mem::size_of;
// This must match the header of `sockaddr`.
#[repr(C)]
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"
))]
ss_family: u8,
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
)))]
ss_family: u16,
}
/// Read the `ss_family` field from a socket address returned from the OS.
///
/// # Safety
///
/// `storage` must point to a valid socket address returned from the OS.
#[inline]
unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> 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(target_os = "haiku"))]
sa_data: [0; 14],
#[cfg(target_os = "haiku")]
sa_data: [0; 30],
};
(*storage.cast::<sockaddr_header>()).ss_family.into()
}
/// Set the `ss_family` field of a socket address to `AF_UNSPEC`, so that we
/// can test for `AF_UNSPEC` to test whether it was stored to.
pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr_storage) {
(*storage.cast::<sockaddr_header>()).ss_family = c::AF_UNSPEC as _;
}
/// Read a socket address encoded in a platform-specific format.
///
/// # Safety
///
/// `storage` must point to valid socket address storage.
pub(crate) unsafe fn read_sockaddr(
storage: *const c::sockaddr_storage,
len: usize,
) -> io::Result<SocketAddrAny> {
#[cfg(unix)]
let offsetof_sun_path = super::addr::offsetof_sun_path();
if len < size_of::<c::sa_family_t>() {
return Err(io::Errno::INVAL);
}
match read_ss_family(storage).into() {
c::AF_INET => {
if len < size_of::<c::sockaddr_in>() {
return Err(io::Errno::INVAL);
}
let decode = &*storage.cast::<c::sockaddr_in>();
Ok(SocketAddrAny::V4(SocketAddrV4::new(
Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
u16::from_be(decode.sin_port),
)))
}
c::AF_INET6 => {
if len < size_of::<c::sockaddr_in6>() {
return Err(io::Errno::INVAL);
}
let decode = &*storage.cast::<c::sockaddr_in6>();
#[cfg(not(windows))]
let s6_addr = decode.sin6_addr.s6_addr;
#[cfg(windows)]
let s6_addr = decode.sin6_addr.u.Byte;
#[cfg(not(windows))]
let sin6_scope_id = decode.sin6_scope_id;
#[cfg(windows)]
let sin6_scope_id = decode.Anonymous.sin6_scope_id;
Ok(SocketAddrAny::V6(SocketAddrV6::new(
Ipv6Addr::from(s6_addr),
u16::from_be(decode.sin6_port),
u32::from_be(decode.sin6_flowinfo),
sin6_scope_id,
)))
}
#[cfg(unix)]
c::AF_UNIX => {
if len < offsetof_sun_path {
return Err(io::Errno::INVAL);
}
if len == offsetof_sun_path {
SocketAddrUnix::new(&[][..]).map(SocketAddrAny::Unix)
} else {
let decode = &*storage.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 {
return SocketAddrUnix::new_abstract_name(core::mem::transmute::<
&[c::c_char],
&[u8],
>(
&decode.sun_path[1..len - offsetof_sun_path],
))
.map(SocketAddrAny::Unix);
}
// 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(io::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(io::Errno::INVAL);
}
debug_assert_eq!(
CStr::from_ptr(decode.sun_path.as_ptr().cast())
.to_bytes()
.len(),
provided_len
);
&decode.sun_path[..provided_len]
};
SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
.map(SocketAddrAny::Unix)
}
}
#[cfg(target_os = "linux")]
c::AF_XDP => {
if len < size_of::<c::sockaddr_xdp>() {
return Err(io::Errno::INVAL);
}
let decode = &*storage.cast::<c::sockaddr_xdp>();
Ok(SocketAddrAny::Xdp(SocketAddrXdp::new(
SockaddrXdpFlags::from_bits_retain(decode.sxdp_flags),
u32::from_be(decode.sxdp_ifindex),
u32::from_be(decode.sxdp_queue_id),
u32::from_be(decode.sxdp_shared_umem_fd),
)))
}
_ => Err(io::Errno::INVAL),
}
}
/// Read an optional socket address returned from the OS.
///
/// # Safety
///
/// `storage` must point to a valid socket address returned from the OS.
pub(crate) unsafe fn maybe_read_sockaddr_os(
storage: *const c::sockaddr_storage,
len: usize,
) -> Option<SocketAddrAny> {
if len == 0 {
return None;
}
assert!(len >= size_of::<c::sa_family_t>());
let family = read_ss_family(storage).into();
if family == c::AF_UNSPEC {
None
} else {
Some(inner_read_sockaddr_os(family, storage, len))
}
}
/// Read a socket address returned from the OS.
///
/// # Safety
///
/// `storage` must point to a valid socket address returned from the OS.
pub(crate) unsafe fn read_sockaddr_os(
storage: *const c::sockaddr_storage,
len: usize,
) -> SocketAddrAny {
assert!(len >= size_of::<c::sa_family_t>());
let family = read_ss_family(storage).into();
inner_read_sockaddr_os(family, storage, len)
}
unsafe fn inner_read_sockaddr_os(
family: c::c_int,
storage: *const c::sockaddr_storage,
len: usize,
) -> SocketAddrAny {
#[cfg(unix)]
let offsetof_sun_path = super::addr::offsetof_sun_path();
assert!(len >= size_of::<c::sa_family_t>());
match family {
c::AF_INET => {
assert!(len >= size_of::<c::sockaddr_in>());
let decode = &*storage.cast::<c::sockaddr_in>();
SocketAddrAny::V4(SocketAddrV4::new(
Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
u16::from_be(decode.sin_port),
))
}
c::AF_INET6 => {
assert!(len >= size_of::<c::sockaddr_in6>());
let decode = &*storage.cast::<c::sockaddr_in6>();
SocketAddrAny::V6(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)]
c::AF_UNIX => {
assert!(len >= offsetof_sun_path);
if len == offsetof_sun_path {
SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap())
} else {
let decode = &*storage.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 {
return SocketAddrAny::Unix(
SocketAddrUnix::new_abstract_name(core::mem::transmute::<
&[c::c_char],
&[u8],
>(
&decode.sun_path[1..len - offsetof_sun_path],
))
.unwrap(),
);
}
// Otherwise we expect a NUL-terminated filesystem path.
assert_eq!(decode.sun_path[len - 1 - offsetof_sun_path], 0);
let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
// 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.
#[cfg(any(solarish, target_os = "freebsd"))]
let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()];
SocketAddrAny::Unix(
SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
.unwrap(),
)
}
}
#[cfg(target_os = "linux")]
c::AF_XDP => {
assert!(len >= size_of::<c::sockaddr_xdp>());
let decode = &*storage.cast::<c::sockaddr_xdp>();
SocketAddrAny::Xdp(SocketAddrXdp::new(
SockaddrXdpFlags::from_bits_retain(decode.sxdp_flags),
u32::from_be(decode.sxdp_ifindex),
u32::from_be(decode.sxdp_queue_id),
u32::from_be(decode.sxdp_shared_umem_fd),
))
}
other => unimplemented!("{:?}", other),
}
}

View File

@@ -0,0 +1,112 @@
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 = "espidf",
target_os = "nto",
target_os = "haiku",
target_os = "hurd",
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);
/// Deprecated alias for [`EOR`].
///
/// [`EOR`]: Self::EOR
#[cfg(not(windows))]
#[deprecated(note = "`rustix::net::SendFlags::EOT` is renamed to `rustix::net::SendFlags::EOR`.")]
const EOT = bitcast!(c::MSG_EOR);
/// `MSG_EOR`
#[cfg(not(windows))]
const EOR = bitcast!(c::MSG_EOR);
/// `MSG_MORE`
#[cfg(not(any(
bsd,
solarish,
windows,
target_os = "aix",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "vita",
)))]
const MORE = bitcast!(c::MSG_MORE);
#[cfg(not(any(apple, windows, 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 {
#[cfg(not(any(
apple,
solarish,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "vita",
)))]
/// `MSG_CMSG_CLOEXEC`
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 = "espidf",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
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`
const TRUNC = bitcast!(c::MSG_TRUNC);
/// `MSG_WAITALL`
const WAITALL = bitcast!(c::MSG_WAITALL);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,621 @@
//! libc syscalls supporting `rustix::net`.
#[cfg(unix)]
use super::addr::SocketAddrUnix;
#[cfg(target_os = "linux")]
use super::msghdr::with_xdp_msghdr;
#[cfg(target_os = "linux")]
use super::write_sockaddr::encode_sockaddr_xdp;
use crate::backend::c;
use crate::backend::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len};
use crate::fd::{BorrowedFd, OwnedFd};
use crate::io;
#[cfg(target_os = "linux")]
use crate::net::xdp::SocketAddrXdp;
use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6};
use crate::utils::as_ptr;
use core::mem::{size_of, MaybeUninit};
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use {
super::msghdr::{with_noaddr_msghdr, with_recv_msghdr, with_v4_msghdr, with_v6_msghdr},
crate::io::{IoSlice, IoSliceMut},
crate::net::{RecvAncillaryBuffer, RecvMsgReturn, SendAncillaryBuffer},
};
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
use {
super::read_sockaddr::{initialize_family_to_unspec, maybe_read_sockaddr_os, read_sockaddr_os},
super::send_recv::{RecvFlags, SendFlags},
super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6},
crate::net::{AddressFamily, Protocol, Shutdown, SocketFlags, SocketType},
core::ptr::null_mut,
};
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) unsafe fn recv(
fd: BorrowedFd<'_>,
buf: *mut u8,
len: usize,
flags: RecvFlags,
) -> io::Result<usize> {
ret_send_recv(c::recv(
borrowed_fd(fd),
buf.cast(),
send_recv_len(len),
bitflags_bits!(flags),
))
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
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),
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) unsafe fn recvfrom(
fd: BorrowedFd<'_>,
buf: *mut u8,
buf_len: usize,
flags: RecvFlags,
) -> io::Result<(usize, Option<SocketAddrAny>)> {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
// `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(storage.as_mut_ptr());
ret_send_recv(c::recvfrom(
borrowed_fd(fd),
buf.cast(),
send_recv_len(buf_len),
bitflags_bits!(flags),
storage.as_mut_ptr().cast(),
&mut len,
))
.map(|nread| {
(
nread,
maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
)
})
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn sendto_v4(
fd: BorrowedFd<'_>,
buf: &[u8],
flags: SendFlags,
addr: &SocketAddrV4,
) -> io::Result<usize> {
unsafe {
ret_send_recv(c::sendto(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
bitflags_bits!(flags),
as_ptr(&encode_sockaddr_v4(addr)).cast::<c::sockaddr>(),
size_of::<c::sockaddr_in>() as c::socklen_t,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn sendto_v6(
fd: BorrowedFd<'_>,
buf: &[u8],
flags: SendFlags,
addr: &SocketAddrV6,
) -> io::Result<usize> {
unsafe {
ret_send_recv(c::sendto(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
bitflags_bits!(flags),
as_ptr(&encode_sockaddr_v6(addr)).cast::<c::sockaddr>(),
size_of::<c::sockaddr_in6>() as c::socklen_t,
))
}
}
#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn sendto_unix(
fd: BorrowedFd<'_>,
buf: &[u8],
flags: SendFlags,
addr: &SocketAddrUnix,
) -> io::Result<usize> {
unsafe {
ret_send_recv(c::sendto(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
bitflags_bits!(flags),
as_ptr(&addr.unix).cast(),
addr.addr_len(),
))
}
}
#[cfg(target_os = "linux")]
pub(crate) fn sendto_xdp(
fd: BorrowedFd<'_>,
buf: &[u8],
flags: SendFlags,
addr: &SocketAddrXdp,
) -> io::Result<usize> {
unsafe {
ret_send_recv(c::sendto(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
bitflags_bits!(flags),
as_ptr(&encode_sockaddr_xdp(addr)).cast::<c::sockaddr>(),
size_of::<c::sockaddr_xdp>() as _,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
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,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
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,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn bind_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
unsafe {
ret(c::bind(
borrowed_fd(sockfd),
as_ptr(&encode_sockaddr_v4(addr)).cast(),
size_of::<c::sockaddr_in>() as c::socklen_t,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn bind_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> {
unsafe {
ret(c::bind(
borrowed_fd(sockfd),
as_ptr(&encode_sockaddr_v6(addr)).cast(),
size_of::<c::sockaddr_in6>() as c::socklen_t,
))
}
}
#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> {
unsafe {
ret(c::bind(
borrowed_fd(sockfd),
as_ptr(&addr.unix).cast(),
addr.addr_len(),
))
}
}
#[cfg(target_os = "linux")]
pub(crate) fn bind_xdp(sockfd: BorrowedFd<'_>, addr: &SocketAddrXdp) -> io::Result<()> {
unsafe {
ret(c::bind(
borrowed_fd(sockfd),
as_ptr(&encode_sockaddr_xdp(addr)).cast(),
size_of::<c::sockaddr_xdp>() as c::socklen_t,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
unsafe {
ret(c::connect(
borrowed_fd(sockfd),
as_ptr(&encode_sockaddr_v4(addr)).cast(),
size_of::<c::sockaddr_in>() as c::socklen_t,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn connect_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> {
unsafe {
ret(c::connect(
borrowed_fd(sockfd),
as_ptr(&encode_sockaddr_v6(addr)).cast(),
size_of::<c::sockaddr_in6>() as c::socklen_t,
))
}
}
#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> {
unsafe {
ret(c::connect(
borrowed_fd(sockfd),
as_ptr(&addr.unix).cast(),
addr.addr_len(),
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
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,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> {
unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) }
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
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 = "redox",
target_os = "vita",
target_os = "wasi"
)))]
pub(crate) fn recvmsg(
sockfd: BorrowedFd<'_>,
iov: &mut [IoSliceMut<'_>],
control: &mut RecvAncillaryBuffer<'_>,
msg_flags: RecvFlags,
) -> io::Result<RecvMsgReturn> {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
with_recv_msghdr(&mut storage, iov, control, |msghdr| {
let result = unsafe {
ret_send_recv(c::recvmsg(
borrowed_fd(sockfd),
msghdr,
bitflags_bits!(msg_flags),
))
};
result.map(|bytes| {
// Get the address of the sender, if any.
let addr =
unsafe { maybe_read_sockaddr_os(msghdr.msg_name as _, msghdr.msg_namelen as _) };
RecvMsgReturn {
bytes,
address: addr,
flags: RecvFlags::from_bits_retain(bitcast!(msghdr.msg_flags)),
}
})
})
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
pub(crate) fn sendmsg(
sockfd: BorrowedFd<'_>,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
with_noaddr_msghdr(iov, control, |msghdr| unsafe {
ret_send_recv(c::sendmsg(
borrowed_fd(sockfd),
&msghdr,
bitflags_bits!(msg_flags),
))
})
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
pub(crate) fn sendmsg_v4(
sockfd: BorrowedFd<'_>,
addr: &SocketAddrV4,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
with_v4_msghdr(addr, iov, control, |msghdr| unsafe {
ret_send_recv(c::sendmsg(
borrowed_fd(sockfd),
&msghdr,
bitflags_bits!(msg_flags),
))
})
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
pub(crate) fn sendmsg_v6(
sockfd: BorrowedFd<'_>,
addr: &SocketAddrV6,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
with_v6_msghdr(addr, iov, control, |msghdr| unsafe {
ret_send_recv(c::sendmsg(
borrowed_fd(sockfd),
&msghdr,
bitflags_bits!(msg_flags),
))
})
}
#[cfg(all(
unix,
not(any(target_os = "espidf", target_os = "redox", target_os = "vita"))
))]
pub(crate) fn sendmsg_unix(
sockfd: BorrowedFd<'_>,
addr: &SocketAddrUnix,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
super::msghdr::with_unix_msghdr(addr, iov, control, |msghdr| unsafe {
ret_send_recv(c::sendmsg(
borrowed_fd(sockfd),
&msghdr,
bitflags_bits!(msg_flags),
))
})
}
#[cfg(target_os = "linux")]
pub(crate) fn sendmsg_xdp(
sockfd: BorrowedFd<'_>,
addr: &SocketAddrXdp,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
with_xdp_msghdr(addr, iov, control, |msghdr| unsafe {
ret_send_recv(c::sendmsg(
borrowed_fd(sockfd),
&msghdr,
bitflags_bits!(msg_flags),
))
})
}
#[cfg(not(any(
apple,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "redox",
target_os = "nto",
target_os = "vita",
target_os = "wasi",
)))]
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)
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
unsafe {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
let owned_fd = ret_owned_fd(c::accept(
borrowed_fd(sockfd),
storage.as_mut_ptr().cast(),
&mut len,
))?;
Ok((
owned_fd,
maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
))
}
}
#[cfg(not(any(
apple,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
pub(crate) fn acceptfrom_with(
sockfd: BorrowedFd<'_>,
flags: SocketFlags,
) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
unsafe {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
let owned_fd = ret_owned_fd(c::accept4(
borrowed_fd(sockfd),
storage.as_mut_ptr().cast(),
&mut len,
flags.bits() as c::c_int,
))?;
Ok((
owned_fd,
maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
))
}
}
/// 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 = "nto",
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 = "nto",
target_os = "vita",
))]
pub(crate) fn acceptfrom_with(
sockfd: BorrowedFd<'_>,
_flags: SocketFlags,
) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
acceptfrom(sockfd)
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> {
unsafe { ret(c::shutdown(borrowed_fd(sockfd), how as c::c_int)) }
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny> {
unsafe {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
ret(c::getsockname(
borrowed_fd(sockfd),
storage.as_mut_ptr().cast(),
&mut len,
))?;
Ok(read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>> {
unsafe {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
ret(c::getpeername(
borrowed_fd(sockfd),
storage.as_mut_ptr().cast(),
&mut len,
))?;
Ok(maybe_read_sockaddr_os(
storage.as_ptr(),
len.try_into().unwrap(),
))
}
}
#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn socketpair(
domain: AddressFamily,
type_: SocketType,
flags: SocketFlags,
protocol: Option<Protocol>,
) -> io::Result<(OwnedFd, OwnedFd)> {
let raw_protocol = match protocol {
Some(p) => p.0.get(),
None => 0,
};
unsafe {
let mut fds = MaybeUninit::<[OwnedFd; 2]>::uninit();
ret(c::socketpair(
c::c_int::from(domain.0),
(type_.0 | flags.bits()) as c::c_int,
raw_protocol as c::c_int,
fds.as_mut_ptr().cast::<c::c_int>(),
))?;
let [fd0, fd1] = fds.assume_init();
Ok((fd0, fd1))
}
}

View File

@@ -0,0 +1,128 @@
//! The BSD sockets API requires us to read the `ss_family` field before we can
//! interpret the rest of a `sockaddr` produced by the kernel.
use super::addr::SocketAddrStorage;
#[cfg(unix)]
use super::addr::SocketAddrUnix;
use super::ext::{in6_addr_new, in_addr_new, sockaddr_in6_new};
use crate::backend::c;
#[cfg(target_os = "linux")]
use crate::net::xdp::SocketAddrXdp;
use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6};
use core::mem::size_of;
pub(crate) unsafe fn write_sockaddr(
addr: &SocketAddrAny,
storage: *mut SocketAddrStorage,
) -> usize {
match addr {
SocketAddrAny::V4(v4) => write_sockaddr_v4(v4, storage),
SocketAddrAny::V6(v6) => write_sockaddr_v6(v6, storage),
#[cfg(unix)]
SocketAddrAny::Unix(unix) => write_sockaddr_unix(unix, storage),
#[cfg(target_os = "linux")]
SocketAddrAny::Xdp(xdp) => write_sockaddr_xdp(xdp, storage),
}
}
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: 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,
}
}
unsafe fn write_sockaddr_v4(v4: &SocketAddrV4, storage: *mut SocketAddrStorage) -> usize {
let encoded = encode_sockaddr_v4(v4);
core::ptr::write(storage.cast(), encoded);
size_of::<c::sockaddr_in>()
}
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(
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(),
)
}
}
unsafe fn write_sockaddr_v6(v6: &SocketAddrV6, storage: *mut SocketAddrStorage) -> usize {
let encoded = encode_sockaddr_v6(v6);
core::ptr::write(storage.cast(), encoded);
size_of::<c::sockaddr_in6>()
}
#[cfg(unix)]
unsafe fn write_sockaddr_unix(unix: &SocketAddrUnix, storage: *mut SocketAddrStorage) -> usize {
core::ptr::write(storage.cast(), unix.unix);
unix.len()
}
#[cfg(target_os = "linux")]
pub(crate) fn encode_sockaddr_xdp(xdp: &SocketAddrXdp) -> c::sockaddr_xdp {
c::sockaddr_xdp {
sxdp_family: c::AF_XDP as _,
sxdp_flags: xdp.flags().bits(),
sxdp_ifindex: xdp.interface_index(),
sxdp_queue_id: xdp.queue_id(),
sxdp_shared_umem_fd: xdp.shared_umem_fd(),
}
}
#[cfg(target_os = "linux")]
unsafe fn write_sockaddr_xdp(xdp: &SocketAddrXdp, storage: *mut SocketAddrStorage) -> usize {
let encoded = encode_sockaddr_xdp(xdp);
core::ptr::write(storage.cast(), encoded);
size_of::<c::sockaddr_xdp>()
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,128 @@
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 = "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<()> {
let size: c::c_int = size.try_into().map_err(|_| io::Errno::PERM)?;
unsafe {
let _ = ret_c_int(c::fcntl(borrowed_fd(fd), c::F_SETPIPE_SZ, size))?;
}
Ok(())
}

View File

@@ -0,0 +1,108 @@
#[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 = "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: c::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 as *mut 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() as *mut c::c_void,
iov_len: buf.len() as _,
},
_lifetime: PhantomData,
}
}
}
#[cfg(not(any(apple, target_os = "wasi")))]
#[test]
fn test_types() {
assert_eq_size!(PipeFlags, c::c_int);
#[cfg(linux_kernel)]
assert_eq_size!(SpliceFlags, c::c_int);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,744 @@
//! libc syscalls supporting `rustix::process`.
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
use super::types::RawCpuSet;
use crate::backend::c;
#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
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(linux_kernel)]
use crate::backend::conv::ret_u32;
#[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 = "wasi", target_os = "fuchsia")))]
use crate::fd::BorrowedFd;
#[cfg(target_os = "linux")]
use crate::fd::{AsRawFd, OwnedFd, RawFd};
#[cfg(any(target_os = "linux", feature = "fs"))]
use crate::ffi::CStr;
#[cfg(feature = "fs")]
use crate::fs::Mode;
use crate::io;
#[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(linux_kernel)]
use crate::process::{Cpuid, MembarrierCommand, MembarrierQuery};
#[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 = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use crate::process::{Resource, Rlimit};
#[cfg(not(any(
target_os = "espidf",
target_os = "redox",
target_os = "openbsd",
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(any(linux_kernel, target_os = "dragonfly"))]
#[inline]
pub(crate) fn sched_getcpu() -> usize {
let r = unsafe { libc::sched_getcpu() };
debug_assert!(r >= 0);
r as usize
}
#[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())) }
}
// 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()),
))
}
}
#[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(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();
}
}
#[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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "espidf",
target_os = "redox",
target_os = "openbsd",
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 = "espidf",
target_os = "redox",
target_os = "openbsd",
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 = "espidf",
target_os = "redox",
target_os = "openbsd",
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 = "espidf",
target_os = "redox",
target_os = "openbsd",
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 = "espidf",
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 i32)) }
}
#[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 i32,
))
}
}
#[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 i32)) }
}
#[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 c::c_int,
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) }
}

View File

@@ -0,0 +1,172 @@
#[cfg(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::process::membarrier
/// [`membarrier_cpu`]: crate::process::membarrier_cpu
/// [`membarrier_query`]: crate::process::membarrier_query
#[cfg(linux_kernel)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u32)]
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 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 = "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))]
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 = "nto", target_os = "haiku")))]
Rss = bitcast!(c::RLIMIT_RSS),
/// `RLIMIT_NPROC`
#[cfg(not(any(solarish, 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 = "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 = "haiku",
target_os = "hurd",
target_os = "nto"
)))]
Locks = bitcast!(c::RLIMIT_LOCKS),
/// `RLIMIT_SIGPENDING`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
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 = "haiku",
target_os = "hurd",
target_os = "nto"
)))]
Msgqueue = bitcast!(c::RLIMIT_MSGQUEUE),
/// `RLIMIT_NICE`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
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 = "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 = "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;
}
/// A CPU identifier as a raw integer.
#[cfg(linux_kernel)]
pub type RawCpuid = u32;
#[cfg(freebsdlike)]
pub type RawId = c::id_t;
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
pub(crate) type RawCpuSet = c::cpu_set_t;
#[cfg(freebsdlike)]
pub(crate) type RawCpuSet = c::cpuset_t;
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
#[inline]
pub(crate) fn raw_cpu_set_new() -> RawCpuSet {
let mut set = unsafe { core::mem::zeroed() };
super::cpu_set::CPU_ZERO(&mut set);
set
}
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
pub(crate) const CPU_SETSIZE: usize = c::CPU_SETSIZE as usize;

View File

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

View File

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

View File

@@ -0,0 +1,118 @@
//! libc syscalls supporting `rustix::pty`.
use crate::backend::c;
use crate::backend::conv::{borrowed_fd, ret};
use crate::fd::BorrowedFd;
use crate::io;
#[cfg(all(
feature = "alloc",
any(
apple,
linux_like,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos"
)
))]
use {
crate::ffi::{CStr, CString},
crate::path::SMALL_PATH_BUFFER_SIZE,
alloc::borrow::ToOwned,
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 {
libc::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();
std::ptr::copy_nonoverlapping(name.as_ptr(), buffer.as_mut_ptr(), len + 1);
0
}
_ => libc_errno::errno().0,
}
}
};
if r == 0 {
return Ok(unsafe { CStr::from_ptr(buffer.as_ptr().cast()).to_owned() });
}
if r != c::ERANGE {
return Err(io::Errno::from_raw_os_error(r));
}
// Use `Vec` reallocation strategy to grow capacity exponentially.
buffer.reserve(1);
buffer.resize(buffer.capacity(), 0_u8);
}
}
#[inline]
pub(crate) fn unlockpt(fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe { ret(c::unlockpt(borrowed_fd(fd))) }
}
#[cfg(not(linux_kernel))]
#[inline]
pub(crate) fn grantpt(fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe { ret(c::grantpt(borrowed_fd(fd))) }
}

View File

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

View File

@@ -0,0 +1,18 @@
//! 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,
cap: 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.cast(), cap, flags.bits()))
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,159 @@
//! 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 = "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 = "emscripten",
target_os = "espidf",
target_os = "illumos",
target_os = "haiku",
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;
syscall! {
fn finit_module(
fd: c::c_int,
param_values: *const c::c_char,
flags: c::c_int
) via SYS_finit_module -> c::c_int
}
unsafe { ret(finit_module(fd.as_raw_fd(), c_str(param_values), flags)) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn delete_module(name: &CStr, flags: c::c_int) -> io::Result<()> {
syscall! {
fn delete_module(
name: *const c::c_char,
flags: c::c_int
) via SYS_delete_module -> c::c_int
}
unsafe { ret(delete_module(c_str(name), flags)) }
}

View File

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

View File

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

View File

@@ -0,0 +1,521 @@
//! 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(all(feature = "alloc", feature = "procfs"))]
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
use crate::ffi::CStr;
#[cfg(any(
not(target_os = "espidf"),
all(
feature = "procfs",
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
// `libc::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 = "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 = "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 { 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(all(feature = "alloc", feature = "procfs"))]
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
pub(crate) fn ttyname(dirfd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
unsafe {
// `ttyname_r` returns its error status rather than using `errno`.
match c::ttyname_r(borrowed_fd(dirfd), buf.as_mut_ptr().cast(), buf.len()) {
0 => Ok(CStr::from_ptr(buf.as_ptr().cast()).to_bytes().len()),
err => Err(io::Errno::from_raw_os_error(err)),
}
}
}

View File

@@ -0,0 +1,93 @@
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);
}
}
/// `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_*` operations for use with the [`futex`] function.
///
/// [`futex`]: fn@crate::thread::futex
// TODO: Deprecate this now that we have a new typed API.
/*
#[deprecated(
since = "0.38.35",
note = "
The `futex` function and `FutexOperation` enum are deprecated. There are
individual functions available to perform futex operations with improved
type safety. See the `rustix::thread::futex` module."
)]
*/
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
pub enum FutexOperation {
/// `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_WAITERS`
pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS;
/// `FUTEX_OWNER_DIED`
pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED;

View File

@@ -0,0 +1,4 @@
#[cfg(linux_kernel)]
pub(crate) mod futex;
#[cfg(not(windows))]
pub(crate) mod syscalls;

View File

@@ -0,0 +1,648 @@
//! libc syscalls supporting `rustix::thread`.
use crate::backend::c;
use crate::backend::conv::ret;
use crate::io;
#[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(not(target_os = "redox"))]
use crate::thread::{NanosleepRelativeResult, Timespec};
#[cfg(all(target_env = "gnu", fix_y2038))]
use crate::timespec::LibcTimespec;
#[cfg(all(
linux_kernel,
target_pointer_width = "32",
not(any(target_arch = "aarch64", target_arch = "x86_64"))
))]
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_usize},
crate::fd::BorrowedFd,
crate::pid::Pid,
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 = "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, request, remain.as_mut_ptr()) {
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 = "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 = "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 _,
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 = "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(request, remain.as_mut_ptr())) {
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) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> {
unsafe { 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: crate::ugid::Uid,
euid: crate::ugid::Uid,
suid: 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.as_raw(), euid.as_raw(), suid.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: crate::ugid::Gid,
egid: crate::ugid::Gid,
sgid: 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.as_raw(), egid.as_raw(), sgid.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 linux_raw_sys::general::__kernel_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: *const 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,
timeout,
uaddr2,
val3,
))
.or_else(|err| {
// See the comments in `rustix_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 linux_raw_sys::general::__kernel_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)]
#[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: *const 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 timeout.is_null() {
None
} else {
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)?,
})
};
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)]
#[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()) })
}

View File

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

View File

@@ -0,0 +1,452 @@
//! libc syscalls supporting `rustix::time`.
use crate::backend::c;
use crate::backend::conv::ret;
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(feature = "time")]
#[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(all(target_env = "gnu", fix_y2038))]
use crate::timespec::LibcTimespec;
use crate::timespec::Timespec;
use core::mem::MaybeUninit;
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(feature = "time")]
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 = "fuchsia"))]
#[cfg(all(target_env = "gnu", fix_y2038))]
#[cfg(feature = "time")]
weak!(fn __timerfd_gettime64(c::c_int, *mut LibcItimerspec) -> c::c_int);
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(all(target_env = "gnu", fix_y2038))]
#[cfg(feature = "time")]
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, timespec.as_mut_ptr());
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, timespec.as_mut_ptr())).unwrap();
timespec.assume_init()
}
}
#[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;
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(linux_kernel)]
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, timespec.as_mut_ptr()))?;
Ok(timespec.assume_init())
}
}
#[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, &timespec))
}
}
#[cfg(not(any(
target_os = "redox",
target_os = "wasi",
all(apple, not(target_os = "macos"))
)))]
#[cfg(fix_y2038)]
fn clock_settime_old(id: ClockId, timespec: Timespec) -> io::Result<()> {
let old_timespec = c::timespec {
tv_sec: timespec
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timespec.tv_nsec as _,
};
unsafe { ret(c::clock_settime(id as c::clockid_t, &old_timespec)) }
}
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(feature = "time")]
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 = "fuchsia"))]
#[cfg(feature = "time")]
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 {
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
ret(c::timerfd_settime(
borrowed_fd(fd),
bitflags_bits!(flags),
new_value,
result.as_mut_ptr(),
))?;
Ok(result.assume_init())
}
}
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(fix_y2038)]
#[cfg(feature = "time")]
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 = "fuchsia"))]
#[cfg(feature = "time")]
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 {
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
ret(c::timerfd_gettime(borrowed_fd(fd), result.as_mut_ptr()))?;
Ok(result.assume_init())
}
}
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(fix_y2038)]
#[cfg(feature = "time")]
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 _,
},
})
}

View File

@@ -0,0 +1,177 @@
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
use crate::backend::c;
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(fix_y2038)]
use crate::timespec::LibcTimespec;
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(fix_y2038)]
use crate::timespec::Timespec;
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
use bitflags::bitflags;
/// `struct itimerspec` for use with [`timerfd_gettime`] and
/// [`timerfd_settime`].
///
/// [`timerfd_gettime`]: crate::time::timerfd_gettime
/// [`timerfd_settime`]: crate::time::timerfd_settime
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(not(fix_y2038))]
pub type Itimerspec = c::itimerspec;
/// `struct itimerspec` for use with [`timerfd_gettime`] and
/// [`timerfd_settime`].
///
/// [`timerfd_gettime`]: crate::time::timerfd_gettime
/// [`timerfd_settime`]: crate::time::timerfd_settime
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[cfg(fix_y2038)]
#[repr(C)]
#[derive(Debug, Clone)]
pub struct Itimerspec {
/// The interval of an interval timer.
pub it_interval: Timespec,
/// Time remaining in the current interval.
pub it_value: Timespec,
}
/// On most platforms, `LibcItimerspec` is just `Itimerspec`.
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[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 = "fuchsia"))]
#[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 = "fuchsia"))]
#[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 = "fuchsia"))]
#[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 = "fuchsia"))]
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 = "fuchsia"))]
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(linux_kernel)]
#[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 = "fuchsia"))]
#[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")]
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.
#[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.
#[doc(alias = "CLOCK_BOOTTIME_ALARM")]
BoottimeAlarm = bitcast!(c::CLOCK_BOOTTIME_ALARM),
}
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[test]
fn test_types() {
assert_eq_size!(TimerfdFlags, c::c_int);
assert_eq_size!(TimerfdTimerFlags, c::c_int);
}

View File

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

View File

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

View File

@@ -0,0 +1,67 @@
//! Adapt the Winsock API to resemble a POSIX-style libc API.
#![allow(unused_imports)]
#![allow(non_camel_case_types)]
#![allow(dead_code)]
use windows_sys::Win32::Networking::WinSock;
// Define the basic C types. With Rust 1.64, we can use these from `core::ffi`.
pub(crate) type c_schar = i8;
pub(crate) type c_uchar = u8;
pub(crate) type c_short = i16;
pub(crate) type c_ushort = u16;
pub(crate) type c_int = i32;
pub(crate) type c_uint = u32;
pub(crate) type c_longlong = i64;
pub(crate) type c_ulonglong = u64;
pub(crate) type ssize_t = isize;
pub(crate) type c_char = i8;
pub(crate) type c_long = i32;
pub(crate) type c_ulong = u32;
pub(crate) use core::ffi::c_void;
// windows-sys declares these constants as u16. For better compatibility
// with Unix-family APIs, redeclare them as u32.
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: i64,
}
pub type time_t = i64;

View File

@@ -0,0 +1,268 @@
//! aarch64 Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, 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",
in("x8") nr.to_asm(),
in("x0") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
in("x5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
in("x5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

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

View File

@@ -0,0 +1,543 @@
//! 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, 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",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall7_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"sw {}, 24($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
in(reg) a6.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,265 @@
//! riscv64 Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
lateout("a0") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") 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!(
"ecall",
in("a7") nr.to_asm(),
in("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 r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
in("a5") 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!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
in("a5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,287 @@
//! s390x Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
lateout("r2") 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("r1") nr.to_asm(),
inlateout("r2") 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("r1") nr.to_asm(),
inlateout("r2") 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",
in("r1") nr.to_asm(),
in("r2") a0.to_asm(),
options(nostack, preserves_flags, 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("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") 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("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") 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("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") 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("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") 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("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") 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("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") 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("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
in("r6") 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("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
in("r6") 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> {
if nr.nr == linux_raw_sys::general::__NR_mmap as usize {
let mut a = [
a0.to_asm(),
a1.to_asm(),
a2.to_asm(),
a3.to_asm(),
a4.to_asm(),
a5.to_asm(),
];
return syscall1(nr, a.as_mut_ptr().into());
}
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
in("r6") a4.to_asm(),
in("r7") 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> {
if nr.nr == linux_raw_sys::general::__NR_mmap as usize {
let a = [
a0.to_asm(),
a1.to_asm(),
a2.to_asm(),
a3.to_asm(),
a4.to_asm(),
a5.to_asm(),
];
return syscall1_readonly(nr, a.as_ptr().into());
}
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
in("r6") a4.to_asm(),
in("r7") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,322 @@
//! arm Linux system calls, using thumb-mode.
//!
//! In thumb-mode, r7 is the frame pointer and is not permitted to be used in
//! an inline asm operand, so we have to use a different register and copy it
//! into r7 inside the inline asm.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov r7, {nr}",
"svc 0",
nr = in(reg) 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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
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!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,490 @@
//! 32-bit x86 Linux system calls.
//!
//! There are two forms; `indirect_*` which take a callee, which allow calling
//! through the vDSO when possible, and plain forms, which use the `int 0x80`
//! instruction.
//!
//! Most `rustix` syscalls use the vsyscall mechanism rather than going using
//! `int 0x80` sequences, as vsyscall is much faster.
//!
//! Syscalls made with `int 0x80` preserve the flags register, while syscalls
//! made using vsyscall do not.
#![allow(dead_code)]
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use crate::backend::vdso_wrappers::SyscallType;
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall0(
callee: SyscallType,
nr: SyscallNumber<'_>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall1(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall1_noreturn(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> ! {
asm!(
"call {callee}",
callee = in(reg) callee,
in("eax") nr.to_asm(),
in("ebx") a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall2(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall3(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall4(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
// a3 should go in esi, but `asm!` won't let us use it as an operand.
// Temporarily swap it into place, and then swap it back afterward.
//
// We hard-code the callee operand to use edi instead of `in(reg)` because
// even though we can't name esi as an operand, the compiler can use esi to
// satisfy `in(reg)`.
asm!(
"xchg esi, {a3}",
"call edi",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
in("edi") callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall5(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
// Oof. a3 should go in esi, and `asm!` won't let us use that register as
// an operand. And we can't request stack slots. And there are no other
// registers free. Use eax as a temporary pointer to a slice, since it gets
// clobbered as the return value anyway.
asm!(
"push esi",
"push [eax + 0]",
"mov esi, [eax + 4]",
"mov eax, [eax + 8]",
"call [esp]",
"pop esi",
"pop esi",
inout("eax") &[callee as _, a3.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall6(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
// Oof again. a3 should go in esi, and a5 should go in ebp, and `asm!`
// won't let us use either of those registers as operands. And we can't
// request stack slots. And there are no other registers free. Use eax as a
// temporary pointer to a slice, since it gets clobbered as the return
// value anyway.
//
// This is another reason that syscalls should be compiler intrinsics
// rather than inline asm.
asm!(
"push ebp",
"push esi",
"push [eax + 0]",
"mov esi, [eax + 4]",
"mov ebp, [eax + 8]",
"mov eax, [eax + 12]",
"call [esp]",
"pop esi",
"pop esi",
"pop ebp",
inout("eax") &[callee as _, a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => 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!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
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!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"int $$0x80",
in("eax") nr.to_asm(),
in("ebx") 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!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") 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!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") 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!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") 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!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") 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;
// a3 should go in esi, but `asm!` won't let us use it as an operand.
// Temporarily swap it into place, and then swap it back afterward.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.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;
// See the comments in `syscall4`.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.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;
// As in `syscall4`, use xchg to handle a3. a4 should go in edi, and we can
// use that register as an operand. Unlike in `indirect_syscall5`, we don't
// have a `callee` operand taking up a register, so we have enough
// registers and don't need to use a slice.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") 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;
// See the comments in `syscall5`.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") 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;
// See the comments in `indirect_syscall6`.
asm!(
"push ebp",
"push esi",
"mov esi, [eax + 0]",
"mov ebp, [eax + 4]",
"mov eax, [eax + 8]",
"int $$0x80",
"pop esi",
"pop ebp",
inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(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;
// See the comments in `indirect_syscall6`.
asm!(
"push ebp",
"push esi",
"mov esi, [eax + 0]",
"mov ebp, [eax + 4]",
"mov eax, [eax + 8]",
"int $$0x80",
"pop esi",
"pop ebp",
inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

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