Initial vendor packages

Signed-off-by: Valentin Popov <valentin@popov.link>
This commit is contained in:
2024-01-08 01:21:28 +04:00
parent 5ecd8cf2cb
commit 1b6a04ca55
7309 changed files with 2160054 additions and 0 deletions

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

File diff suppressed because one or more lines are too long

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

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

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

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

29
vendor/rustix/COPYRIGHT vendored Normal file
View File

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

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

@ -0,0 +1,271 @@
# 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.28"
authors = [
"Dan Gohman <dev@sunfishcode.online>",
"Jakub Konka <kubkon@jakubkonka.com>",
]
include = [
"src",
"build.rs",
"Cargo.toml",
"COPYRIGHT",
"LICENSE*",
"/*.md",
"benches",
]
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"]
rustdoc-args = [
"--cfg",
"doc_cfg",
]
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-wasi",
]
[[bench]]
name = "mod"
harness = false
[dependencies.alloc]
version = "1.0.0"
optional = true
package = "rustc-std-workspace-alloc"
[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.1"
optional = true
default-features = false
[dev-dependencies.flate2]
version = "1.0"
[dev-dependencies.libc]
version = "0.2.150"
[dev-dependencies.libc_errno]
version = "0.3.8"
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",
]
linux_4_11 = []
linux_latest = ["linux_4_11"]
mm = []
mount = []
net = [
"linux-raw-sys/net",
"linux-raw-sys/netlink",
"linux-raw-sys/if_ether",
]
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 = [
"dep:core",
"dep:alloc",
"dep: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",
]
stdio = []
system = ["linux-raw-sys/system"]
termios = []
thread = ["linux-raw-sys/prctl"]
time = []
use-explicitly-provided-auxv = []
use-libc = [
"libc_errno",
"libc",
]
use-libc-auxv = []
[target."cfg(all(any(target_os = \"android\", target_os = \"linux\"), any(rustix_use_libc, miri, not(all(target_os = \"linux\", target_endian = \"little\", 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 = \"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.11"
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\", target_endian = \"little\", 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 = \"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.150"
features = ["extra_traits"]
optional = true
default-features = false
[target."cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", target_endian = \"little\", 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 = \"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.8"
optional = true
default-features = false
package = "errno"
[target."cfg(all(not(rustix_use_libc), not(miri), target_os = \"linux\", target_endian = \"little\", 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 = \"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.11"
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\", target_endian = \"little\", 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 = \"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.150"
features = ["extra_traits"]
default-features = false
[target."cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \"linux\", target_endian = \"little\", 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 = \"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.8"
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.8"
default-features = false
package = "errno"
[target."cfg(windows)".dependencies.windows-sys]
version = "0.52.0"
features = [
"Win32_Foundation",
"Win32_Networking_WinSock",
"Win32_NetworkManagement_IpHelper",
"Win32_System_Threading",
]
[target."cfg(windows)".dev-dependencies.ctor]
version = "0.2.0"

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

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

View File

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

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

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

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

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

196
vendor/rustix/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/SECURITY.md vendored Normal file
View File

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

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

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

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

@ -0,0 +1,250 @@
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 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.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 == "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"))
&& (apple
|| os == "android"
|| os == "emscripten"
|| os == "haiku"
|| env == "gnu"
|| (env == "musl" && arch == "x86"))
{
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 out_dir = var("OUT_DIR").unwrap();
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("--out-dir")
.arg(out_dir); // Put the output somewhere inconsequential.
// 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()
}

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

@ -0,0 +1,468 @@
//! 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;
// TODO: Remove once https://github.com/rust-lang/libc/pull/3377 is merged and released.
#[cfg(target_os = "netbsd")]
#[cfg(feature = "net")]
pub(crate) const SO_NOSIGPIPE: c_int = 0x0800;
// On PowerPC, the regular `termios` has the `termios2` fields and there is no
// `termios2`. linux-raw-sys has aliases `termios2` to `termios` to cover this
// difference, but we still need to manually import it since `libc` doesn't
// have this.
#[cfg(all(
linux_kernel,
feature = "termios",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
pub(crate) use {
linux_raw_sys::general::{termios2, CIBAUD},
linux_raw_sys::ioctl::{TCGETS2, TCSETS2, TCSETSF2, TCSETSW2},
};
// 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", target_env = "gnu"))]
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};

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

@ -0,0 +1,247 @@
//! 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()
}
#[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(linux_kernel)]
#[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"))
)
))]
#[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"))
))
))]
#[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 = "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 = "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,496 @@
//! Linux `epoll` support.
//!
//! # Examples
//!
//! ```no_run
//! # #[cfg(feature = "net")]
//! # fn main() -> std::io::Result<()> {
//! use rustix::event::epoll;
//! use rustix::fd::AsFd;
//! use rustix::io::{ioctl_fionbio, read, write};
//! use rustix::net::{
//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType,
//! };
//! use std::collections::HashMap;
//! use std::os::unix::io::AsRawFd;
//!
//! // Create a socket and listen on it.
//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?;
//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?;
//! listen(&listen_sock, 1)?;
//!
//! // Create an epoll object. Using `Owning` here means the epoll object will
//! // take ownership of the file descriptors registered with it.
//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?;
//!
//! // Register the socket with the epoll object.
//! epoll::add(
//! &epoll,
//! &listen_sock,
//! epoll::EventData::new_u64(1),
//! epoll::EventFlags::IN,
//! )?;
//!
//! // Keep track of the sockets we've opened.
//! let mut next_id = epoll::EventData::new_u64(2);
//! let mut sockets = HashMap::new();
//!
//! // Process events.
//! let mut event_list = epoll::EventVec::with_capacity(4);
//! loop {
//! epoll::wait(&epoll, &mut event_list, -1)?;
//! for event in &event_list {
//! let target = event.data;
//! if target.u64() == 1 {
//! // Accept a new connection, set it to non-blocking, and
//! // register to be notified when it's ready to write to.
//! let conn_sock = accept(&listen_sock)?;
//! ioctl_fionbio(&conn_sock, true)?;
//! epoll::add(
//! &epoll,
//! &conn_sock,
//! next_id,
//! epoll::EventFlags::OUT | epoll::EventFlags::ET,
//! )?;
//!
//! // Keep track of the socket.
//! sockets.insert(next_id, conn_sock);
//! next_id = epoll::EventData::new_u64(next_id.u64() + 1);
//! } else {
//! // Write a message to the stream and then unregister it.
//! let target = sockets.remove(&target).unwrap();
//! write(&target, b"hello\n")?;
//! let _ = epoll::delete(&epoll, &target)?;
//! }
//! }
//! }
//! # }
//! # #[cfg(not(feature = "net"))]
//! # fn main() {}
//! ```
use crate::backend::c;
#[cfg(feature = "alloc")]
use crate::backend::conv::ret_u32;
use crate::backend::conv::{ret, ret_owned_fd};
use crate::fd::{AsFd, AsRawFd, OwnedFd};
use crate::io;
use crate::utils::as_mut_ptr;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use bitflags::bitflags;
use core::ffi::c_void;
use core::hash::{Hash, Hasher};
use core::ptr::null_mut;
use core::slice;
bitflags! {
/// `EPOLL_*` for use with [`new`].
#[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 [`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;
}
}
/// `epoll_create1(flags)`—Creates a new epoll object.
///
/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file
/// descriptor from being implicitly passed across `exec` boundaries.
#[inline]
#[doc(alias = "epoll_create1")]
pub fn create(flags: CreateFlags) -> io::Result<OwnedFd> {
// SAFETY: We're calling `epoll_create1` via FFI and we know how it
// behaves.
unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) }
}
/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll
/// object.
///
/// This registers interest in any of the events set in `events` occurring on
/// the file descriptor associated with `data`.
///
/// If [`delete`] is not called on the I/O source passed into this function
/// before the I/O source is `close`d, then the `epoll` will act as if the I/O
/// source is still registered with it. This can lead to spurious events being
/// returned from [`wait`]. If a file descriptor is an
/// `Arc<dyn SystemResource>`, then `epoll` can be thought to maintain a
/// `Weak<dyn SystemResource>` to the file descriptor.
#[doc(alias = "epoll_ctl")]
pub fn add(
epoll: impl AsFd,
source: impl AsFd,
data: EventData,
event_flags: EventFlags,
) -> io::Result<()> {
// SAFETY: We're calling `epoll_ctl` via FFI and we know how it
// behaves. 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 elsehwere for layout equivalence.
unsafe {
let raw_fd = source.as_fd().as_raw_fd();
ret(c::epoll_ctl(
epoll.as_fd().as_raw_fd(),
c::EPOLL_CTL_ADD,
raw_fd,
as_mut_ptr(&mut Event {
flags: event_flags,
data,
})
.cast(),
))
}
}
/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a
/// given epoll object.
///
/// This sets the events of interest with `target` to `events`.
#[doc(alias = "epoll_ctl")]
pub fn modify(
epoll: impl AsFd,
source: impl AsFd,
data: EventData,
event_flags: EventFlags,
) -> io::Result<()> {
let raw_fd = source.as_fd().as_raw_fd();
// SAFETY: We're calling `epoll_ctl` via FFI and we know how it
// behaves. 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 elsehwere for layout equivalence.
unsafe {
ret(c::epoll_ctl(
epoll.as_fd().as_raw_fd(),
c::EPOLL_CTL_MOD,
raw_fd,
as_mut_ptr(&mut Event {
flags: event_flags,
data,
})
.cast(),
))
}
}
/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a
/// given epoll object.
#[doc(alias = "epoll_ctl")]
pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> {
// SAFETY: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
let raw_fd = source.as_fd().as_raw_fd();
ret(c::epoll_ctl(
epoll.as_fd().as_raw_fd(),
c::EPOLL_CTL_DEL,
raw_fd,
null_mut(),
))
}
}
/// `epoll_wait(self, events, timeout)`—Waits for registered events of
/// interest.
///
/// For each event of interest, an element is written to `events`. On
/// success, this returns the number of written elements.
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> {
// SAFETY: We're calling `epoll_wait` via FFI and we know how it
// behaves.
unsafe {
event_list.events.set_len(0);
let nfds = ret_u32(c::epoll_wait(
epoll.as_fd().as_raw_fd(),
event_list.events.as_mut_ptr().cast::<c::epoll_event>(),
event_list.events.capacity().try_into().unwrap_or(i32::MAX),
timeout,
))?;
event_list.events.set_len(nfds as usize);
}
Ok(())
}
/// An iterator over the `Event`s in an `EventVec`.
pub struct Iter<'a> {
/// Use `Copied` to copy the struct, since `Event` is `packed` on some
/// platforms, and it's common for users to directly destructure it, which
/// would lead to errors about forming references to packed fields.
iter: core::iter::Copied<slice::Iter<'a, Event>>,
}
impl<'a> Iterator for Iter<'a> {
type Item = Event;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
/// A record of an event that occurred.
#[repr(C)]
#[cfg_attr(
any(
all(
target_arch = "x86",
not(target_env = "musl"),
not(target_os = "android"),
),
target_arch = "x86_64",
),
repr(packed)
)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Event {
/// Which specific event(s) occurred.
pub flags: EventFlags,
/// User data.
pub data: EventData,
}
/// Data associated with an [`Event`]. This can either be a 64-bit integer
/// value or a pointer which preserves pointer provenance.
#[repr(C)]
#[derive(Copy, Clone)]
pub union EventData {
/// A 64-bit integer value.
as_u64: u64,
/// A `*mut c_void` which preserves pointer provenance, extended to be
/// 64-bit so that if we read the value as a `u64` union field, we don't
/// get uninitialized memory.
sixty_four_bit_pointer: SixtyFourBitPointer,
}
impl EventData {
/// Construct a new value containing a `u64`.
#[inline]
pub const fn new_u64(value: u64) -> Self {
Self { as_u64: value }
}
/// Construct a new value containing a `*mut c_void`.
#[inline]
pub const fn new_ptr(value: *mut c_void) -> Self {
Self {
sixty_four_bit_pointer: SixtyFourBitPointer {
pointer: value,
#[cfg(target_pointer_width = "32")]
_padding: 0,
},
}
}
/// Return the value as a `u64`.
///
/// If the stored value was a pointer, the pointer is zero-extended to a
/// `u64`.
#[inline]
pub fn u64(self) -> u64 {
unsafe { self.as_u64 }
}
/// Return the value as a `*mut c_void`.
///
/// If the stored value was a `u64`, the least-significant bits of the
/// `u64` are returned as a pointer value.
#[inline]
pub fn ptr(self) -> *mut c_void {
unsafe { self.sixty_four_bit_pointer.pointer }
}
}
impl PartialEq for EventData {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.u64() == other.u64()
}
}
impl Eq for EventData {}
impl Hash for EventData {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.u64().hash(state)
}
}
#[repr(C)]
#[derive(Copy, Clone)]
struct SixtyFourBitPointer {
#[cfg(target_endian = "big")]
#[cfg(target_pointer_width = "32")]
_padding: u32,
pointer: *mut c_void,
#[cfg(target_endian = "little")]
#[cfg(target_pointer_width = "32")]
_padding: u32,
}
/// A vector of `Event`s, plus context for interpreting them.
#[cfg(feature = "alloc")]
pub struct EventVec {
events: Vec<Event>,
}
#[cfg(feature = "alloc")]
impl EventVec {
/// Constructs an `EventVec` from raw pointer, length, and capacity.
///
/// # Safety
///
/// This function calls [`Vec::from_raw_parts`] with its arguments.
///
/// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts
#[inline]
pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self {
Self {
events: Vec::from_raw_parts(ptr, len, capacity),
}
}
/// Constructs an `EventVec` with memory for `capacity` `Event`s.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
events: Vec::with_capacity(capacity),
}
}
/// Returns the current `Event` capacity of this `EventVec`.
#[inline]
pub fn capacity(&self) -> usize {
self.events.capacity()
}
/// Reserves enough memory for at least `additional` more `Event`s.
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.events.reserve(additional);
}
/// Reserves enough memory for exactly `additional` more `Event`s.
#[inline]
pub fn reserve_exact(&mut self, additional: usize) {
self.events.reserve_exact(additional);
}
/// Clears all the `Events` out of this `EventVec`.
#[inline]
pub fn clear(&mut self) {
self.events.clear();
}
/// Shrinks the capacity of this `EventVec` as much as possible.
#[inline]
pub fn shrink_to_fit(&mut self) {
self.events.shrink_to_fit();
}
/// Returns an iterator over the `Event`s in this `EventVec`.
#[inline]
pub fn iter(&self) -> Iter<'_> {
Iter {
iter: self.events.iter().copied(),
}
}
/// Returns the number of `Event`s logically contained in this `EventVec`.
#[inline]
pub fn len(&mut self) -> usize {
self.events.len()
}
/// Tests whether this `EventVec` is logically empty.
#[inline]
pub fn is_empty(&mut self) -> bool {
self.events.is_empty()
}
}
#[cfg(feature = "alloc")]
impl<'a> IntoIterator for &'a EventVec {
type IntoIter = Iter<'a>;
type Item = Event;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[test]
fn test_epoll_layouts() {
check_renamed_type!(Event, epoll_event);
check_renamed_type!(Event, epoll_event);
check_renamed_struct_renamed_field!(Event, epoll_event, flags, events);
check_renamed_struct_renamed_field!(Event, epoll_event, data, u64);
}

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(linux_kernel)]
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};
use bitflags::bitflags;
use core::marker::PhantomData;
#[cfg(windows)]
use {
crate::backend::fd::{AsSocket, RawFd},
core::fmt,
};
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(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)]
#[cfg_attr(not(windows), derive(Debug))]
#[repr(transparent)]
pub struct PollFd<'fd> {
pollfd: c::pollfd,
_phantom: PhantomData<BorrowedFd<'fd>>,
}
#[cfg(windows)]
impl<'fd> fmt::Debug for PollFd<'fd> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.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,191 @@
//! libc syscalls supporting `rustix::event`.
use crate::backend::c;
use crate::backend::conv::ret_c_int;
#[cfg(any(apple, netbsdlike, target_os = "dragonfly", target_os = "solaris"))]
use crate::backend::conv::ret_owned_fd;
use crate::event::PollFd;
#[cfg(any(linux_kernel, bsd, solarish, target_os = "espidf"))]
use crate::fd::OwnedFd;
use crate::io;
#[cfg(any(bsd, solarish))]
use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd, core::mem::MaybeUninit};
#[cfg(solarish)]
use {
crate::backend::conv::ret, crate::event::port::Event, crate::utils::as_mut_ptr,
core::ptr::null_mut,
};
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "illumos",
target_os = "espidf"
))]
use {crate::backend::conv::ret_owned_fd, crate::event::EventfdFlags};
#[cfg(all(feature = "alloc", bsd))]
use {crate::event::kqueue::Event, crate::utils::as_ptr, core::ptr::null};
#[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(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<()> {
let timeout = timeout.map_or(null_mut(), as_mut_ptr);
unsafe {
ret(c::port_getn(
borrowed_fd(port),
events.as_mut_ptr().cast(),
events.len().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_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(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn pause() {
let r = unsafe { libc::pause() };
let errno = libc_errno::errno().0;
debug_assert_eq!(r, -1);
debug_assert_eq!(errno, libc::EINTR);
}

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,16 @@
//! Windows system calls in the `event` module.
use crate::backend::c;
use crate::backend::conv::ret_c_int;
use crate::event::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)
}

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

@ -0,0 +1,423 @@
#[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 anyting 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(doc_cfg, doc(cfg(feature = "process")))]
#[inline]
pub fn chdir(&self) -> io::Result<()> {
fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
}
/// `Dir` implements `Send` but not `Sync`, because we use `readdir` which is
/// not guaranteed to be thread-safe. Users can wrap this in a `Mutex` if they
/// need `Sync`, which is effectively what'd need to do to implement `Sync`
/// ourselves.
unsafe impl Send 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,131 @@
//! inotify support for working with inotifies
use crate::backend::c;
use crate::backend::conv::{borrowed_fd, c_str, ret, ret_c_int, ret_owned_fd};
use crate::fd::{BorrowedFd, OwnedFd};
use crate::io;
use bitflags::bitflags;
bitflags! {
/// `IN_*` for use with [`inotify_init`].
///
/// [`inotify_init`]: crate::fs::inotify::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::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;
}
}
/// `inotify_init1(flags)`—Creates a new inotify object.
///
/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file
/// descriptor from being implicitly passed across `exec` boundaries.
#[doc(alias = "inotify_init1")]
pub fn inotify_init(flags: CreateFlags) -> io::Result<OwnedFd> {
// SAFETY: `inotify_init1` has no safety preconditions.
unsafe { ret_owned_fd(c::inotify_init1(bitflags_bits!(flags))) }
}
/// `inotify_add_watch(self, path, flags)`—Adds a watch to inotify.
///
/// This registers or updates a watch for the filesystem path `path` and
/// returns a watch descriptor corresponding to this watch.
///
/// Note: Due to the existence of hardlinks, providing two different paths to
/// this method may result in it returning the same watch descriptor. An
/// application should keep track of this externally to avoid logic errors.
pub fn inotify_add_watch<P: crate::path::Arg>(
inot: BorrowedFd<'_>,
path: P,
flags: WatchFlags,
) -> io::Result<i32> {
path.into_with_c_str(|path| {
// SAFETY: The fd and path we are passing is guaranteed valid by the
// type system.
unsafe {
ret_c_int(c::inotify_add_watch(
borrowed_fd(inot),
c_str(path),
flags.bits(),
))
}
})
}
/// `inotify_rm_watch(self, wd)`—Removes a watch from this inotify.
///
/// The watch descriptor provided should have previously been returned by
/// [`inotify_add_watch`] and not previously have been removed.
#[doc(alias = "inotify_rm_watch")]
pub fn inotify_remove_watch(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> {
// Android's `inotify_rm_watch` takes `u32` despite that
// `inotify_add_watch` expects a `i32`.
#[cfg(target_os = "android")]
let wd = wd as u32;
// SAFETY: The fd is valid and closing an arbitrary wd is valid.
unsafe { ret(c::inotify_rm_watch(borrowed_fd(inot), wd)) }
}

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

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,340 @@
//! 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(target_os = "espidf"))]
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(target_os = "espidf"))]
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(target_os = "espidf"))]
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 = "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 = "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);
}
#[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 = "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,30 @@
//! Windows system calls in the `io` module.
use crate::backend::c;
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);
}
#[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,70 @@
//! 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, 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_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,477 @@
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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "nto",
target_os = "redox",
)))]
const POPULATE = bitcast!(c::MAP_POPULATE);
/// `MAP_STACK`
#[cfg(not(any(
apple,
solarish,
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "netbsd",
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 = "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);
/// <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")))]
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;
}
}

215
vendor/rustix/src/backend/libc/mod.rs vendored Normal file
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 a `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,340 @@
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;
// TODO: add when Linux 6.5 is released
// /// `MOVE_MOUNT_BENEATH`
// 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,245 @@
//! 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,
})
}
fn init() -> c::sockaddr_un {
c::sockaddr_un {
#[cfg(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto"))]
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, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(path) = self.path() {
path.fmt(fmt)
} else {
#[cfg(linux_kernel)]
if let Some(name) = self.abstract_name() {
return name.fmt(fmt);
}
"(unnamed)".fmt(fmt)
}
}
}
/// `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 = "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 = "nto",
target_os = "vita"
))]
sun_family: 0_u8,
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
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,135 @@
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 = "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 = "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,16 @@
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;
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,134 @@
//! 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};
use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
use crate::io::{self, IoSlice, IoSliceMut};
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 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,306 @@
//! 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;
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 = "nto",
target_os = "vita"
))]
sa_len: 0_u8,
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
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 = "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()).to_bytes().len(),
provided_len
);
&decode.sun_path[..provided_len]
};
SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
.map(SocketAddrAny::Unix)
}
}
_ => 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(),
)
}
}
other => unimplemented!("{:?}", other),
}
}

View File

@ -0,0 +1,103 @@
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `MSG_*` flags for use with [`send`], [`send_to`], 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 = "vita",
)))]
const CONFIRM = bitcast!(c::MSG_CONFIRM);
/// `MSG_DONTROUTE`
const DONTROUTE = bitcast!(c::MSG_DONTROUTE);
/// `MSG_DONTWAIT`
#[cfg(not(windows))]
const DONTWAIT = bitcast!(c::MSG_DONTWAIT);
/// `MSG_EOR`
#[cfg(not(windows))]
const EOT = bitcast!(c::MSG_EOR);
/// `MSG_MORE`
#[cfg(not(any(
bsd,
solarish,
windows,
target_os = "aix",
target_os = "haiku",
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 = "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,568 @@
//! libc syscalls supporting `rustix::net`.
#[cfg(unix)]
use super::addr::SocketAddrUnix;
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;
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(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(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(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,103 @@
//! 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;
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),
}
}
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 = "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 = "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 = "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()
}

View File

@ -0,0 +1,54 @@
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_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,125 @@
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 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 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 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_sz(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_sz(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
let size: c::c_int = size.try_into().map_err(|_| io::Errno::PERM)?;
unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETPIPE_SZ, size)) }
}

View File

@ -0,0 +1,103 @@
#[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 = "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`].
#[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,50 @@
//! 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() }
}

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,713 @@
//! 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(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(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 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_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 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_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 manpage 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_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(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,106 @@
//! 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")
))]
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")
))]
#[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"))]
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::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,67 @@
//! 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;
#[cfg(linux_kernel)]
use crate::system::Sysinfo;
use core::mem::MaybeUninit;
#[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(target_os = "linux")]
pub(crate) fn reboot(cmd: RebootCommand) -> io::Result<()> {
unsafe { ret(c::reboot(cmd as i32)) }
}

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,403 @@
//! 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> {
// If we have `TCGETS2`, use it, so that we fill in the `c_ispeed` and
// `c_ospeed` fields.
#[cfg(linux_kernel)]
{
use crate::termios::{ControlModes, InputModes, LocalModes, OutputModes, SpecialCodes};
use crate::utils::default_array;
let termios2 = unsafe {
let mut termios2 = MaybeUninit::<c::termios2>::uninit();
// QEMU's `TCGETS2` doesn't currently set `input_speed` or
// `output_speed` on PowerPC, so zero out the fields ourselves.
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
{
termios2.write(core::mem::zeroed());
}
ret(c::ioctl(
borrowed_fd(fd),
c::TCGETS2 as _,
termios2.as_mut_ptr(),
))?;
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_array()),
input_speed: termios2.c_ispeed,
output_speed: termios2.c_ospeed,
};
// QEMU's `TCGETS2` doesn't currently set `input_speed` or
// `output_speed` on PowerPC, so set them manually if we can.
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
{
use crate::termios::speed;
if result.output_speed == 0 && (termios2.c_cflag & c::CBAUD) != c::BOTHER {
if let Some(output_speed) = speed::decode(termios2.c_cflag & c::CBAUD) {
result.output_speed = output_speed;
}
}
if result.input_speed == 0
&& ((termios2.c_cflag & c::CIBAUD) >> c::IBSHIFT) != c::BOTHER
{
// For input speeds, `B0` is special-cased to mean the input
// speed is the same as the output speed.
if ((termios2.c_cflag & c::CIBAUD) >> c::IBSHIFT) == c::B0 {
result.input_speed = result.output_speed;
} else if let Some(input_speed) =
speed::decode((termios2.c_cflag & c::CIBAUD) >> c::IBSHIFT)
{
result.input_speed = input_speed;
}
}
}
result.special_codes.0[..termios2.c_cc.len()].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())
}
}
#[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 succceed 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<()> {
// If we have `TCSETS2`, use it, so that we use the `c_ispeed` and
// `c_ospeed` fields.
#[cfg(linux_kernel)]
{
use crate::termios::speed;
use crate::utils::default_array;
use linux_raw_sys::general::{termios2, BOTHER, CBAUD, IBSHIFT};
#[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))]
use linux_raw_sys::ioctl::{TCSETS, TCSETS2};
// linux-raw-sys' ioctl-generation script for sparc isn't working yet,
// so as a temporary workaround, declare these manually.
#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
const TCSETS: u32 = 0x8024_5409;
#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
const TCSETS2: u32 = 0x802c_540d;
// Translate from `optional_actions` into an ioctl request code. On
// MIPS, `optional_actions` already has `TCGETS` added to it.
let request = TCSETS2
+ if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
)) {
optional_actions as u32 - TCSETS
} else {
optional_actions as u32
};
let input_speed = termios.input_speed();
let output_speed = termios.output_speed();
let mut termios2 = 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_array(),
c_ispeed: input_speed,
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 &= !CBAUD;
termios2.c_cflag |= speed::encode(output_speed).unwrap_or(BOTHER);
termios2.c_cflag &= !(CBAUD << IBSHIFT);
termios2.c_cflag |= speed::encode(input_speed).unwrap_or(BOTHER) << IBSHIFT;
let nccs = termios2.c_cc.len();
termios2
.c_cc
.copy_from_slice(&termios.special_codes.0[..nccs]);
unsafe { ret(c::ioctl(borrowed_fd(fd), request as _, &termios2)) }
}
#[cfg(not(linux_kernel))]
unsafe {
ret(c::tcsetattr(
borrowed_fd(fd),
optional_actions as _,
crate::utils::as_ptr(termios).cast(),
))
}
}
#[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,43 @@
use crate::backend::c;
bitflags::bitflags! {
/// `FUTEX_*` flags for use with [`futex`].
///
/// [`futex`]: crate::thread::futex
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FutexFlags: 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 [`futex`].
///
/// [`futex`]: crate::thread::futex
#[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),
}

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,523 @@
//! libc syscalls supporting `rustix::thread`.
use crate::backend::c;
use crate::backend::conv::ret;
use crate::io;
#[cfg(not(target_os = "redox"))]
use crate::thread::{NanosleepRelativeResult, Timespec};
#[cfg(all(target_env = "gnu", fix_y2038))]
use crate::timespec::LibcTimespec;
use core::mem::MaybeUninit;
#[cfg(linux_kernel)]
use {
crate::backend::conv::{borrowed_fd, ret_c_int, ret_usize},
crate::fd::BorrowedFd,
crate::pid::Pid,
crate::thread::{FutexFlags, FutexOperation},
crate::utils::as_mut_ptr,
};
#[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, core::ptr::null_mut};
#[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: 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(),
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, 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: 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, 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())) }
}
// TODO: This could be de-multiplexed.
#[cfg(linux_kernel)]
pub(crate) unsafe fn futex(
uaddr: *mut u32,
op: FutexOperation,
flags: FutexFlags,
val: u32,
utime: *const Timespec,
uaddr2: *mut u32,
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: *mut u32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *mut u32,
val3: u32
) via SYS_futex_time64 -> c::ssize_t
}
ret_usize(futex_time64(
uaddr,
op as i32 | flags.bits() as i32,
val,
utime,
uaddr2,
val3,
))
.or_else(|err| {
// See the comments in `rustix_clock_gettime_via_syscall` about
// emulation.
if err == io::Errno::NOSYS {
futex_old(uaddr, op, flags, val, utime, uaddr2, val3)
} else {
Err(err)
}
})
}
#[cfg(any(
target_pointer_width = "64",
target_arch = "aarch64",
target_arch = "x86_64"
))]
{
syscall! {
fn futex(
uaddr: *mut u32,
futex_op: c::c_int,
val: u32,
timeout: *const linux_raw_sys::general::__kernel_timespec,
uaddr2: *mut u32,
val3: u32
) via SYS_futex -> c::c_long
}
ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
utime.cast(),
uaddr2,
val3,
) as isize)
}
}
#[cfg(linux_kernel)]
#[cfg(all(
target_pointer_width = "32",
not(any(target_arch = "aarch64", target_arch = "x86_64"))
))]
unsafe fn futex_old(
uaddr: *mut u32,
op: FutexOperation,
flags: FutexFlags,
val: u32,
utime: *const Timespec,
uaddr2: *mut u32,
val3: u32,
) -> io::Result<usize> {
syscall! {
fn futex(
uaddr: *mut u32,
futex_op: c::c_int,
val: u32,
timeout: *const linux_raw_sys::general::__kernel_old_timespec,
uaddr2: *mut u32,
val3: u32
) via SYS_futex -> c::c_long
}
let old_utime = linux_raw_sys::general::__kernel_old_timespec {
tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
};
ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
&old_utime,
uaddr2,
val3,
) as isize)
}

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(
freebsdlike,
linux_kernel,
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,59 @@
//! 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, *,
};

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,317 @@
//! 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 = "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 = "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,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,489 @@
//! 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;
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)
}

View File

@ -0,0 +1,293 @@
//! x86-64 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!("x32 is not yet supported");
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
in("rax") nr.to_asm(),
in("rdi") 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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
in("r9") a5.to_asm(),
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
in("r9") a5.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

304
vendor/rustix/src/backend/linux_raw/c.rs vendored Normal file
View File

@ -0,0 +1,304 @@
//! Adapt the Linux API to resemble a POSIX-style libc API.
//!
//! The linux_raw backend doesn't use actual libc; this just defines certain
//! types that are convenient to have defined.
#![allow(unused_imports)]
#![allow(non_camel_case_types)]
pub(crate) type size_t = usize;
pub(crate) use linux_raw_sys::ctypes::*;
pub(crate) use linux_raw_sys::errno::EINVAL;
pub(crate) use linux_raw_sys::ioctl::{FIONBIO, FIONREAD};
// Import the kernel's `uid_t` and `gid_t` if they're 32-bit.
#[cfg(not(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86")))]
pub(crate) use linux_raw_sys::general::{__kernel_gid_t as gid_t, __kernel_uid_t as uid_t};
pub(crate) use linux_raw_sys::general::{
__kernel_pid_t as pid_t, __kernel_time64_t as time_t, __kernel_timespec as timespec, iovec,
O_CLOEXEC, O_NOCTTY, O_NONBLOCK, O_RDWR,
};
#[cfg(feature = "event")]
#[cfg(test)]
pub(crate) use linux_raw_sys::general::epoll_event;
#[cfg(any(
feature = "fs",
all(
not(feature = "use-libc-auxv"),
not(feature = "use-explicitly-provided-auxv"),
any(
feature = "param",
feature = "process",
feature = "runtime",
feature = "time",
target_arch = "x86",
)
)
))]
pub(crate) use linux_raw_sys::general::{
AT_FDCWD, NFS_SUPER_MAGIC, O_LARGEFILE, PROC_SUPER_MAGIC, UTIME_NOW, UTIME_OMIT, XATTR_CREATE,
XATTR_REPLACE,
};
pub(crate) use linux_raw_sys::ioctl::{BLKPBSZGET, BLKSSZGET, FICLONE};
#[cfg(feature = "io_uring")]
pub(crate) use linux_raw_sys::{general::open_how, io_uring::*};
#[cfg(feature = "net")]
pub(crate) use linux_raw_sys::{
cmsg_macros::*,
general::{O_CLOEXEC as SOCK_CLOEXEC, O_NONBLOCK as SOCK_NONBLOCK},
if_ether::*,
net::{
linger, msghdr, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_un, socklen_t, AF_DECnet,
__kernel_sa_family_t as sa_family_t, __kernel_sockaddr_storage as sockaddr_storage,
cmsghdr, in6_addr, in_addr, ip_mreq, ip_mreq_source, ip_mreqn, ipv6_mreq, AF_APPLETALK,
AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BLUETOOTH, AF_BRIDGE, AF_CAN, AF_ECONET,
AF_IEEE802154, AF_INET, AF_INET6, AF_IPX, AF_IRDA, AF_ISDN, AF_IUCV, AF_KEY, AF_LLC,
AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX, AF_RDS, AF_ROSE,
AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_WANPIPE, AF_X25,
IP6T_SO_ORIGINAL_DST, IPPROTO_FRAGMENT, IPPROTO_ICMPV6, IPPROTO_MH, IPPROTO_ROUTING,
IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_FREEBIND, IPV6_MULTICAST_HOPS,
IPV6_MULTICAST_LOOP, IPV6_RECVTCLASS, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY,
IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP,
IP_FREEBIND, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_RECVTOS, IP_TOS, IP_TTL,
MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_ERRQUEUE,
MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, SCM_CREDENTIALS,
SCM_RIGHTS, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET,
SOCK_STREAM, SOL_SOCKET, SO_ACCEPTCONN, SO_BROADCAST, SO_COOKIE, SO_DOMAIN, SO_ERROR,
SO_INCOMING_CPU, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_ORIGINAL_DST, SO_PASSCRED,
SO_PROTOCOL, SO_RCVBUF, SO_RCVTIMEO_NEW, SO_RCVTIMEO_NEW as SO_RCVTIMEO, SO_RCVTIMEO_OLD,
SO_REUSEADDR, SO_REUSEPORT, SO_SNDBUF, SO_SNDTIMEO_NEW, SO_SNDTIMEO_NEW as SO_SNDTIMEO,
SO_SNDTIMEO_OLD, SO_TYPE, TCP_CONGESTION, TCP_CORK, TCP_KEEPCNT, TCP_KEEPIDLE,
TCP_KEEPINTVL, TCP_NODELAY, TCP_QUICKACK, TCP_THIN_LINEAR_TIMEOUTS, TCP_USER_TIMEOUT,
},
netlink::*,
};
// Cast away bindgen's `enum` type to make these consistent with the other
// `setsockopt`/`getsockopt` level values.
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IP: u32 = linux_raw_sys::net::IPPROTO_IP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_ICMP: u32 = linux_raw_sys::net::IPPROTO_ICMP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IGMP: u32 = linux_raw_sys::net::IPPROTO_IGMP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IPIP: u32 = linux_raw_sys::net::IPPROTO_IPIP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_TCP: u32 = linux_raw_sys::net::IPPROTO_TCP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_EGP: u32 = linux_raw_sys::net::IPPROTO_EGP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_PUP: u32 = linux_raw_sys::net::IPPROTO_PUP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_UDP: u32 = linux_raw_sys::net::IPPROTO_UDP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IDP: u32 = linux_raw_sys::net::IPPROTO_IDP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_TP: u32 = linux_raw_sys::net::IPPROTO_TP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_DCCP: u32 = linux_raw_sys::net::IPPROTO_DCCP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IPV6: u32 = linux_raw_sys::net::IPPROTO_IPV6 as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_RSVP: u32 = linux_raw_sys::net::IPPROTO_RSVP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_GRE: u32 = linux_raw_sys::net::IPPROTO_GRE as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_ESP: u32 = linux_raw_sys::net::IPPROTO_ESP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_AH: u32 = linux_raw_sys::net::IPPROTO_AH as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_MTP: u32 = linux_raw_sys::net::IPPROTO_MTP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_BEETPH: u32 = linux_raw_sys::net::IPPROTO_BEETPH as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_ENCAP: u32 = linux_raw_sys::net::IPPROTO_ENCAP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_PIM: u32 = linux_raw_sys::net::IPPROTO_PIM as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_COMP: u32 = linux_raw_sys::net::IPPROTO_COMP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_SCTP: u32 = linux_raw_sys::net::IPPROTO_SCTP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_UDPLITE: u32 = linux_raw_sys::net::IPPROTO_UDPLITE as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_MPLS: u32 = linux_raw_sys::net::IPPROTO_MPLS as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_ETHERNET: u32 = linux_raw_sys::net::IPPROTO_ETHERNET as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_RAW: u32 = linux_raw_sys::net::IPPROTO_RAW as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_MPTCP: u32 = linux_raw_sys::net::IPPROTO_MPTCP as _;
#[cfg(any(feature = "process", feature = "runtime"))]
pub(crate) use linux_raw_sys::general::siginfo_t;
#[cfg(any(feature = "process", feature = "runtime"))]
pub(crate) const EXIT_SUCCESS: c_int = 0;
#[cfg(any(feature = "process", feature = "runtime"))]
pub(crate) const EXIT_FAILURE: c_int = 1;
#[cfg(feature = "process")]
pub(crate) const EXIT_SIGNALED_SIGABRT: c_int = 128 + linux_raw_sys::general::SIGABRT as c_int;
#[cfg(feature = "runtime")]
pub(crate) const CLONE_CHILD_SETTID: c_int = linux_raw_sys::general::CLONE_CHILD_SETTID as c_int;
#[cfg(feature = "process")]
pub(crate) use linux_raw_sys::{
general::{
CLD_CONTINUED, CLD_DUMPED, CLD_EXITED, CLD_KILLED, CLD_STOPPED, CLD_TRAPPED,
O_NONBLOCK as PIDFD_NONBLOCK, P_ALL, P_PGID, P_PID, P_PIDFD,
},
ioctl::TIOCSCTTY,
};
#[cfg(feature = "pty")]
pub(crate) use linux_raw_sys::ioctl::TIOCGPTPEER;
#[cfg(feature = "termios")]
pub(crate) use linux_raw_sys::{
general::{
cc_t, speed_t, tcflag_t, termios, termios2, winsize, B0, B1000000, B110, B115200, B1152000,
B1200, B134, B150, B1500000, B1800, B19200, B200, B2000000, B230400, B2400, B2500000, B300,
B3000000, B3500000, B38400, B4000000, B460800, B4800, B50, B500000, B57600, B576000, B600,
B75, B921600, B9600, BOTHER, BRKINT, BS0, BS1, BSDLY, CBAUD, CBAUDEX, CIBAUD, CLOCAL,
CMSPAR, CR0, CR1, CR2, CR3, CRDLY, CREAD, CRTSCTS, CS5, CS6, CS7, CS8, CSIZE, CSTOPB, ECHO,
ECHOCTL, ECHOE, ECHOK, ECHOKE, ECHONL, ECHOPRT, EXTA, EXTB, EXTPROC, FF0, FF1, FFDLY,
FLUSHO, HUPCL, IBSHIFT, ICANON, ICRNL, IEXTEN, IGNBRK, IGNCR, IGNPAR, IMAXBEL, INLCR,
INPCK, ISIG, ISTRIP, IUCLC, IUTF8, IXANY, IXOFF, IXON, NCCS, NL0, NL1, NLDLY, NOFLSH,
OCRNL, OFDEL, OFILL, OLCUC, ONLCR, ONLRET, ONOCR, OPOST, PARENB, PARMRK, PARODD, PENDIN,
TAB0, TAB1, TAB2, TAB3, TABDLY, TCIFLUSH, TCIOFF, TCIOFLUSH, TCION, TCOFLUSH, TCOOFF,
TCOON, TCSADRAIN, TCSAFLUSH, TCSANOW, TOSTOP, VDISCARD, VEOF, VEOL, VEOL2, VERASE, VINTR,
VKILL, VLNEXT, VMIN, VQUIT, VREPRINT, VSTART, VSTOP, VSUSP, VSWTC, VT0, VT1, VTDLY, VTIME,
VWERASE, XCASE, XTABS,
},
ioctl::{TCGETS2, TCSETS2, TCSETSF2, TCSETSW2, TIOCEXCL, TIOCNXCL},
};
// On MIPS, `TCSANOW` et al have `TCSETS` added to them, so we need it to
// subtract it out.
#[cfg(all(
feature = "termios",
any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
)
))]
pub(crate) use linux_raw_sys::ioctl::TCSETS;
// Define our own `uid_t` and `gid_t` if the kernel's versions are not 32-bit.
#[cfg(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86"))]
pub(crate) type uid_t = u32;
#[cfg(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86"))]
pub(crate) type gid_t = u32;
// Bindgen infers `u32` for many of these macro types which meant to be
// used with `c_int` in the C APIs, so cast them to `c_int`.
// Convert the signal constants from `u32` to `c_int`.
pub(crate) const SIGHUP: c_int = linux_raw_sys::general::SIGHUP as _;
pub(crate) const SIGINT: c_int = linux_raw_sys::general::SIGINT as _;
pub(crate) const SIGQUIT: c_int = linux_raw_sys::general::SIGQUIT as _;
pub(crate) const SIGILL: c_int = linux_raw_sys::general::SIGILL as _;
pub(crate) const SIGTRAP: c_int = linux_raw_sys::general::SIGTRAP as _;
pub(crate) const SIGABRT: c_int = linux_raw_sys::general::SIGABRT as _;
pub(crate) const SIGBUS: c_int = linux_raw_sys::general::SIGBUS as _;
pub(crate) const SIGFPE: c_int = linux_raw_sys::general::SIGFPE as _;
pub(crate) const SIGKILL: c_int = linux_raw_sys::general::SIGKILL as _;
pub(crate) const SIGUSR1: c_int = linux_raw_sys::general::SIGUSR1 as _;
pub(crate) const SIGSEGV: c_int = linux_raw_sys::general::SIGSEGV as _;
pub(crate) const SIGUSR2: c_int = linux_raw_sys::general::SIGUSR2 as _;
pub(crate) const SIGPIPE: c_int = linux_raw_sys::general::SIGPIPE as _;
pub(crate) const SIGALRM: c_int = linux_raw_sys::general::SIGALRM as _;
pub(crate) const SIGTERM: c_int = linux_raw_sys::general::SIGTERM as _;
#[cfg(not(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
)))]
pub(crate) const SIGSTKFLT: c_int = linux_raw_sys::general::SIGSTKFLT as _;
pub(crate) const SIGCHLD: c_int = linux_raw_sys::general::SIGCHLD as _;
pub(crate) const SIGCONT: c_int = linux_raw_sys::general::SIGCONT as _;
pub(crate) const SIGSTOP: c_int = linux_raw_sys::general::SIGSTOP as _;
pub(crate) const SIGTSTP: c_int = linux_raw_sys::general::SIGTSTP as _;
pub(crate) const SIGTTIN: c_int = linux_raw_sys::general::SIGTTIN as _;
pub(crate) const SIGTTOU: c_int = linux_raw_sys::general::SIGTTOU as _;
pub(crate) const SIGURG: c_int = linux_raw_sys::general::SIGURG as _;
pub(crate) const SIGXCPU: c_int = linux_raw_sys::general::SIGXCPU as _;
pub(crate) const SIGXFSZ: c_int = linux_raw_sys::general::SIGXFSZ as _;
pub(crate) const SIGVTALRM: c_int = linux_raw_sys::general::SIGVTALRM as _;
pub(crate) const SIGPROF: c_int = linux_raw_sys::general::SIGPROF as _;
pub(crate) const SIGWINCH: c_int = linux_raw_sys::general::SIGWINCH as _;
pub(crate) const SIGIO: c_int = linux_raw_sys::general::SIGIO as _;
pub(crate) const SIGPWR: c_int = linux_raw_sys::general::SIGPWR as _;
pub(crate) const SIGSYS: c_int = linux_raw_sys::general::SIGSYS as _;
#[cfg(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 _;
#[cfg(feature = "stdio")]
pub(crate) const STDIN_FILENO: c_int = linux_raw_sys::general::STDIN_FILENO as _;
#[cfg(feature = "stdio")]
pub(crate) const STDOUT_FILENO: c_int = linux_raw_sys::general::STDOUT_FILENO as _;
#[cfg(feature = "stdio")]
pub(crate) const STDERR_FILENO: c_int = linux_raw_sys::general::STDERR_FILENO as _;
pub(crate) const PIPE_BUF: usize = linux_raw_sys::general::PIPE_BUF as _;
pub(crate) const CLOCK_MONOTONIC: c_int = linux_raw_sys::general::CLOCK_MONOTONIC as _;
pub(crate) const CLOCK_REALTIME: c_int = linux_raw_sys::general::CLOCK_REALTIME as _;
pub(crate) const CLOCK_MONOTONIC_RAW: c_int = linux_raw_sys::general::CLOCK_MONOTONIC_RAW as _;
pub(crate) const CLOCK_MONOTONIC_COARSE: c_int =
linux_raw_sys::general::CLOCK_MONOTONIC_COARSE as _;
pub(crate) const CLOCK_REALTIME_COARSE: c_int = linux_raw_sys::general::CLOCK_REALTIME_COARSE as _;
pub(crate) const CLOCK_THREAD_CPUTIME_ID: c_int =
linux_raw_sys::general::CLOCK_THREAD_CPUTIME_ID as _;
pub(crate) const CLOCK_PROCESS_CPUTIME_ID: c_int =
linux_raw_sys::general::CLOCK_PROCESS_CPUTIME_ID as _;
#[cfg(any(feature = "thread", feature = "time", target_arch = "x86"))]
pub(crate) const CLOCK_BOOTTIME: c_int = linux_raw_sys::general::CLOCK_BOOTTIME as _;
#[cfg(any(feature = "thread", feature = "time", target_arch = "x86"))]
pub(crate) const CLOCK_BOOTTIME_ALARM: c_int = linux_raw_sys::general::CLOCK_BOOTTIME_ALARM as _;
#[cfg(any(feature = "thread", feature = "time", target_arch = "x86"))]
pub(crate) const CLOCK_TAI: c_int = linux_raw_sys::general::CLOCK_TAI as _;
#[cfg(any(feature = "thread", feature = "time", target_arch = "x86"))]
pub(crate) const CLOCK_REALTIME_ALARM: c_int = linux_raw_sys::general::CLOCK_REALTIME_ALARM as _;
#[cfg(feature = "system")]
mod reboot_symbols {
use super::c_int;
pub(crate) const LINUX_REBOOT_MAGIC1: c_int = linux_raw_sys::general::LINUX_REBOOT_MAGIC1 as _;
pub(crate) const LINUX_REBOOT_MAGIC2: c_int = linux_raw_sys::general::LINUX_REBOOT_MAGIC2 as _;
pub(crate) const LINUX_REBOOT_CMD_RESTART: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_RESTART as _;
pub(crate) const LINUX_REBOOT_CMD_HALT: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_HALT as _;
pub(crate) const LINUX_REBOOT_CMD_CAD_ON: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_CAD_ON as _;
pub(crate) const LINUX_REBOOT_CMD_CAD_OFF: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_CAD_OFF as _;
pub(crate) const LINUX_REBOOT_CMD_POWER_OFF: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_POWER_OFF as _;
pub(crate) const LINUX_REBOOT_CMD_SW_SUSPEND: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_SW_SUSPEND as _;
pub(crate) const LINUX_REBOOT_CMD_KEXEC: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_KEXEC as _;
}
#[cfg(feature = "system")]
pub(crate) use reboot_symbols::*;

File diff suppressed because it is too large Load Diff

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