Initial checkin.

This commit is contained in:
Dan Gohman
2020-09-14 13:26:29 -07:00
commit d37320c3d8
39 changed files with 3123 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
# install-rust
Author: Alex Crichton <alex@alexcrichton.com>
A small github action to install `rustup` and a Rust toolchain. This is
generally expressed inline, but it was repeated enough in this repository it
seemed worthwhile to extract.
Some gotchas:
* Can't `--self-update` on Windows due to permission errors (a bug in Github
Actions)
* `rustup` isn't installed on macOS (a bug in Github Actions)
When the above are fixed we should delete this action and just use this inline:
```yml
- run: rustup update $toolchain && rustup default $toolchain
shell: bash
```
+13
View File
@@ -0,0 +1,13 @@
# Author: Alex Crichton <alex@alexcrichton.com>
name: 'Install Rust toolchain'
description: 'Install both `rustup` and a Rust toolchain'
inputs:
toolchain:
description: 'Default toolchan to install'
required: false
default: 'stable'
runs:
using: node12
main: 'main.js'
+28
View File
@@ -0,0 +1,28 @@
// Author: Alex Crichton <alex@alexcrichton.com>
const child_process = require('child_process');
const toolchain = process.env.INPUT_TOOLCHAIN;
if (process.platform === 'darwin') {
child_process.execSync(`curl https://sh.rustup.rs | sh -s -- -y --default-toolchain=none --profile=minimal`);
const bindir = `${process.env.HOME}/.cargo/bin`;
console.log(`::add-path::${bindir}`);
process.env.PATH = `${process.env.PATH}:${bindir}`;
}
child_process.execFileSync('rustup', ['set', 'profile', 'minimal']);
child_process.execFileSync('rustup', ['update', toolchain, '--no-self-update']);
child_process.execFileSync('rustup', ['default', toolchain]);
// Deny warnings on CI to keep our code warning-free as it lands in-tree. Don't
// do this on nightly though since there's a fair amount of warning churn there.
if (!toolchain.startsWith('nightly')) {
console.log(`::set-env name=RUSTFLAGS::-D warnings`);
}
// Save disk space by avoiding incremental compilation, and also we don't use
// any caching so incremental wouldn't help anyway.
console.log(`::set-env name=CARGO_INCREMENTAL::0`);
// Turn down debuginfo from 2 to 1 to help save disk space
console.log(`::set-env name=CARGO_PROFILE_DEV_DEBUG::1`);
console.log(`::set-env name=CARGO_PROFILE_TEST_DEBUG::1`);
+68
View File
@@ -0,0 +1,68 @@
name: CI
on:
push:
branches:
- main
pull_request:
jobs:
rustfmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: ./.github/actions/install-rust
with:
toolchain: stable
- run: cargo fmt --all -- --check
check:
name: Check
runs-on: ${{ matrix.os }}
strategy:
matrix:
build: [stable, beta, nightly]
include:
- build: stable
os: ubuntu-latest
rust: stable
- build: beta
os: ubuntu-latest
rust: beta
- build: nightly
os: ubuntu-latest
rust: nightly
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: ./.github/actions/install-rust
with:
toolchain: ${{ matrix.rust }}
- run: >
rustup target add
x86_64-unknown-linux-musl
x86_64-unknown-linux-gnux32
x86_64-linux-android
x86_64-apple-darwin
x86_64-unknown-freebsd
x86_64-unknown-netbsd
x86_64-fuchsia
i686-unknown-linux-gnu
i686-unknown-linux-musl
wasm32-unknown-emscripten
- run: cargo check --workspace --release -vv
- run: cargo check --workspace --release -vv --target=x86_64-unknown-linux-musl
- run: cargo check --workspace --release -vv --target=x86_64-unknown-linux-gnux32
- run: cargo check --workspace --release -vv --target=x86_64-linux-android
- run: cargo check --workspace --release -vv --target=x86_64-apple-darwin
- run: cargo check --workspace --release -vv --target=x86_64-unknown-freebsd
- run: cargo check --workspace --release -vv --target=x86_64-unknown-netbsd
- run: cargo check --workspace --release -vv --target=x86_64-fuchsia
- run: cargo check --workspace --release -vv --target=i686-unknown-linux-gnu
- run: cargo check --workspace --release -vv --target=i686-unknown-linux-musl
- run: cargo check --workspace --release -vv --target=wasm32-unknown-emscripten
+2
View File
@@ -0,0 +1,2 @@
target
Cargo.lock
+1
View File
@@ -0,0 +1 @@
# This file tells tools we use rustfmt. We use the default settings.
+29
View File
@@ -0,0 +1,29 @@
Short version for non-lawyers:
`posish` is triple-licensed under Apache 2.0 with the LLVM Exception,
Apache 2.0, and MIT terms.
Longer version:
Copyrights in the `posish` project are retained by their contributors.
No copyright assignment is required to contribute to the `posish`
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), `posish`
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.
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "posish"
version = "0.0.0"
authors = [
"Dan Gohman <dev@sunfishcode.online>",
"Jakub Konka <kubkon@jakubkonka.com>",
]
description = "Safe Rust bindings to POSIX-ish libc APIs"
documentation = "https://docs.rs/posish"
license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
repository = "https://github.com/sunfishcode/posish"
edition = "2018"
keywords = ["api", "file", "network"]
categories = ["filesystem", "network-programming"]
readme = "README.md"
[dependencies]
bitflags = "1.2.1"
cfg-if = "0.1.10"
errno = "0.2.6"
libc = { version = "0.2.77", features = ["extra_traits"] }
[badges]
maintenance = { status = "actively-developed" }
+201
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.
+220
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
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.
+30
View File
@@ -0,0 +1,30 @@
<div align="center">
<h1><code>posish</code></h1>
<p>
<strong>Safe Rust bindings to POSIX-ish libc APIs</strong>
</p>
<p>
<a href="https://github.com/sunfishcode/posish/actions?query=workflow%3ACI"><img src="https://github.com/sunfishcode/posish/workflows/CI/badge.svg" alt="Github Actions CI Status" /></a>
<a href="https://crates.io/crates/posish"><img src="https://img.shields.io/crates/v/posish.svg" alt="crates.io page" /></a>
<a href="https://docs.rs/posish"><img src="https://docs.rs/posish/badge.svg" alt="docs.rs docs" /></a>
</p>
</div>
`posish` provides safe wrappers to POSIX-ish `libc` APIs.
`posish` is focused on functionality that isn't already provided by [`std`]
or [`getrandom`]. It has a philosophy of considering raw file descriptors to be
equivalent to pointers in terms of making APIs unsafe, in the spirit of
[`std::os::unix::io::FromRawFd::from_raw_fd`] being unsafe.
`posish` is relatively low-level and does not support Windows; for higher-level
and portable APIs to this functionality, see the [`system-interface`] and
[`cap-std`] crates.
[`std`]: https://doc.rust-lang.org/std/
[`getrandom`]: https://crates.io/crates/getrandom
[`std::os::unix::io::FromRawFd::from_raw_fd`]: https://doc.rust-lang.org/std/os/unix/io/trait.FromRawFd.html#tymethod.from_raw_fd
[`system-interface`]: https://crates.io/crates/system-interface
[`cap-std`]: https://crates.io/crates/cap-std
+23
View File
@@ -0,0 +1,23 @@
//! Work around Bionic (Android's libc) lacking `telldir` and `seekdir`. See
//!
//! https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/HEAD/android-compat/telldir.c
//! https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/7ca4e52401a5e3376ee285cbb973f84559c12572/android-compat/telldir.c
//!
//! for details.
use crate::zero_ok;
#[repr(C)]
struct DIR_INTERNALS {
fd: libc::c_int,
}
pub(crate) unsafe fn telldir(dir: *mut libc::DIR) -> libc::c_long {
let dir = &*(dir as *mut DIR_INTERNALS);
libc::lseek(dir.fd, 0, libc::SEEK_CUR)
}
pub(crate) unsafe fn seekdir(dir: *mut libc::DIR, loc: libc::c_long) {
let dir = &*(dir as *mut DIR_INTERNALS);
zero_ok(libc::lseek(dir.fd, loc, libc::SEEK_SET)).unwrap()
}
+348
View File
@@ -0,0 +1,348 @@
//! POSIX-style `*at` functions.
use crate::{
fs::{Access, AtFlags, LibcStat, Mode, OFlags, PathArg},
negone_err, zero_ok,
};
#[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "l4re")))]
use libc::{fstatat as libc_fstatat, openat as libc_openat};
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
use libc::{fstatat64 as libc_fstatat, openat64 as libc_openat};
#[cfg(unix)]
use std::os::unix::{
ffi::OsStrExt,
io::{AsRawFd, FromRawFd, RawFd},
};
#[cfg(target_os = "wasi")]
use std::os::wasi::{
ffi::OsStringExt,
io::{AsRawFd, FromRawFd, RawFd},
};
use std::{
convert::TryInto,
ffi::{CStr, OsString},
fs, io,
mem::MaybeUninit,
};
#[cfg(not(target_os = "wasi"))]
use std::{ffi::OsStr, mem::ManuallyDrop};
/// Return a "file" which holds a handle which refers to the process current
/// directory (`AT_FDCWD`). It is a `ManuallyDrop`, however the caller should
/// not drop it explicitly, as it refers to an ambient authority rather than
/// an open resource.
#[cfg(not(target_os = "wasi"))]
pub fn cwd() -> ManuallyDrop<fs::File> {
ManuallyDrop::new(unsafe { fs::File::from_raw_fd(libc::AT_FDCWD) })
}
/// `openat(dirfd, path, oflags, mode)`
#[inline]
pub fn openat<P: PathArg, Fd: AsRawFd>(
dirfd: &Fd,
path: P,
oflags: OFlags,
mode: Mode,
) -> io::Result<fs::File> {
let dirfd = dirfd.as_raw_fd();
let path = path.as_cstr()?;
unsafe { _openat(dirfd, &path, oflags, mode) }
}
unsafe fn _openat(dirfd: RawFd, path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<fs::File> {
#[allow(clippy::useless_conversion)]
let fd = negone_err(libc_openat(
dirfd as libc::c_int,
path.as_ptr(),
oflags.bits(),
libc::c_uint::from(mode.bits()),
))?;
Ok(fs::File::from_raw_fd(fd as RawFd))
}
/// `readlinkat(fd, path)`
#[inline]
pub fn readlinkat<P: PathArg, Fd: AsRawFd>(dirfd: &Fd, path: P) -> io::Result<OsString> {
let dirfd = dirfd.as_raw_fd();
let path = path.as_cstr()?;
unsafe { _readlinkat(dirfd, &path) }
}
/// Implement `readlinkat` on platforms which have `PATH_MAX` that we can rely on.
#[cfg(not(target_os = "wasi"))]
unsafe fn _readlinkat(dirfd: RawFd, path: &CStr) -> io::Result<OsString> {
let buffer = &mut [0_u8; libc::PATH_MAX as usize + 1];
let nread = negone_err(libc::readlinkat(
dirfd,
path.as_ptr(),
buffer.as_mut_ptr() as *mut libc::c_char,
buffer.len(),
))?;
let nread = nread.try_into().unwrap();
let link = OsStr::from_bytes(&buffer[0..nread]);
Ok(link.into())
}
/// Implemented `readlinkat` on platforms where we dyamically allocate the buffer instead.
#[cfg(target_os = "wasi")]
unsafe fn _readlinkat(dirfd: RawFd, path: &CStr) -> io::Result<OsString> {
// Start with a buffer big enough for the vast majority of paths.
let mut buffer = Vec::with_capacity(256);
loop {
let nread = negone_err(unsafe {
libc::readlinkat(
dirfd as libc::c_int,
path.as_ptr(),
buffer.as_mut_ptr() as *mut libc::c_char,
buffer.capacity(),
)
})?;
let nread = nread.try_into().unwrap();
buffer.set_len(nread);
if nread < buffer.capacity() {
return Ok(OsString::from_vec(buffer));
}
// This would be a good candidate for `try_reserve`.
// https://github.com/rust-lang/rust/issues/48043
buffer.reserve(1);
}
}
/// `mkdirat(fd, path, mode)`
#[inline]
pub fn mkdirat<P: PathArg, Fd: AsRawFd>(dirfd: &Fd, path: P, mode: Mode) -> io::Result<()> {
let dirfd = dirfd.as_raw_fd();
let path = path.as_cstr()?;
unsafe { _mkdirat(dirfd, &path, mode) }
}
unsafe fn _mkdirat(dirfd: RawFd, path: &CStr, mode: Mode) -> io::Result<()> {
zero_ok(libc::mkdirat(
dirfd as libc::c_int,
path.as_ptr(),
mode.bits(),
))
}
/// `linkat(old_dirfd, old_path, new_dirfd, new_path, flags)`
#[inline]
pub fn linkat<P: PathArg, Q: PathArg, PFd: AsRawFd, QFd: AsRawFd>(
old_dirfd: &PFd,
old_path: P,
new_dirfd: &QFd,
new_path: Q,
flags: AtFlags,
) -> io::Result<()> {
let old_dirfd = old_dirfd.as_raw_fd();
let new_dirfd = new_dirfd.as_raw_fd();
let old_path = old_path.as_cstr()?;
let new_path = new_path.as_cstr()?;
unsafe { _linkat(old_dirfd, &old_path, new_dirfd, &new_path, flags) }
}
unsafe fn _linkat(
old_dirfd: RawFd,
old_path: &CStr,
new_dirfd: RawFd,
new_path: &CStr,
flags: AtFlags,
) -> io::Result<()> {
zero_ok(libc::linkat(
old_dirfd as libc::c_int,
old_path.as_ptr(),
new_dirfd as libc::c_int,
new_path.as_ptr(),
flags.bits(),
))
}
/// `unlinkat(fd, path, flags)`
#[inline]
pub fn unlinkat<P: PathArg, Fd: AsRawFd>(dirfd: &Fd, path: P, flags: AtFlags) -> io::Result<()> {
let dirfd = dirfd.as_raw_fd();
let path = path.as_cstr()?;
unsafe { _unlinkat(dirfd, &path, flags) }
}
unsafe fn _unlinkat(dirfd: RawFd, path: &CStr, flags: AtFlags) -> io::Result<()> {
zero_ok(libc::unlinkat(
dirfd as libc::c_int,
path.as_ptr(),
flags.bits(),
))
}
/// `renameat(old_dirfd, old_path, new_dirfd, new_path)`
#[inline]
pub fn renameat<P: PathArg, Q: PathArg, PFd: AsRawFd, QFd: AsRawFd>(
old_dirfd: &PFd,
old_path: P,
new_dirfd: &QFd,
new_path: Q,
) -> io::Result<()> {
let old_dirfd = old_dirfd.as_raw_fd();
let new_dirfd = new_dirfd.as_raw_fd();
let old_path = old_path.as_cstr()?;
let new_path = new_path.as_cstr()?;
unsafe { _renameat(old_dirfd, &old_path, new_dirfd, &new_path) }
}
unsafe fn _renameat(
old_dirfd: RawFd,
old_path: &CStr,
new_dirfd: RawFd,
new_path: &CStr,
) -> io::Result<()> {
zero_ok(libc::renameat(
old_dirfd as libc::c_int,
old_path.as_ptr(),
new_dirfd as libc::c_int,
new_path.as_ptr(),
))
}
/// `symlinkat(old_dirfd, old_path, new_dirfd, new_path)`
#[inline]
pub fn symlinkat<P: PathArg, Q: PathArg, Fd: AsRawFd>(
old_path: P,
new_dirfd: &Fd,
new_path: Q,
) -> io::Result<()> {
let new_dirfd = new_dirfd.as_raw_fd();
let old_path = old_path.as_cstr()?;
let new_path = new_path.as_cstr()?;
unsafe { _symlinkat(&old_path, new_dirfd, &new_path) }
}
unsafe fn _symlinkat(old_path: &CStr, new_dirfd: RawFd, new_path: &CStr) -> io::Result<()> {
zero_ok(libc::symlinkat(
old_path.as_ptr(),
new_dirfd as libc::c_int,
new_path.as_ptr(),
))
}
/// `fstatat(dirfd, path, flags)`
#[inline]
pub fn statat<P: PathArg, Fd: AsRawFd>(
dirfd: &Fd,
path: P,
flags: AtFlags,
) -> io::Result<LibcStat> {
let dirfd = dirfd.as_raw_fd();
let path = path.as_cstr()?;
unsafe { _statat(dirfd, &path, flags) }
}
unsafe fn _statat(dirfd: RawFd, path: &CStr, flags: AtFlags) -> io::Result<LibcStat> {
let mut stat = MaybeUninit::<LibcStat>::uninit();
zero_ok(libc_fstatat(
dirfd as libc::c_int,
path.as_ptr(),
stat.as_mut_ptr(),
flags.bits(),
))?;
Ok(stat.assume_init())
}
/// `faccessat(dirfd, path, access, flags)`
#[inline]
pub fn accessat<P: PathArg, Fd: AsRawFd>(
dirfd: &Fd,
path: P,
access: Access,
flags: AtFlags,
) -> io::Result<()> {
let dirfd = dirfd.as_raw_fd();
let path = path.as_cstr()?;
unsafe { _accessat(dirfd, &path, access, flags) }
}
#[cfg(not(target_os = "emscripten"))]
unsafe fn _accessat(dirfd: RawFd, path: &CStr, access: Access, flags: AtFlags) -> io::Result<()> {
zero_ok(libc::faccessat(
dirfd as libc::c_int,
path.as_ptr(),
access.bits(),
flags.bits(),
))
}
// Temporarily disable on Emscripten until https://github.com/rust-lang/libc/pull/1836
// is available.
#[cfg(target_os = "emscripten")]
unsafe fn _accessat(
_dirfd: RawFd,
_path: &CStr,
_access: Access,
_flags: AtFlags,
) -> io::Result<()> {
Ok(())
}
/// `utimensat(dirfd, path, times, flags)`
pub fn utimensat<P: PathArg, Fd: AsRawFd>(
dirfd: &Fd,
path: P,
times: &[libc::timespec; 2],
flags: AtFlags,
) -> io::Result<()> {
let dirfd = dirfd.as_raw_fd();
let path = path.as_cstr()?;
unsafe { _utimensat(dirfd, &path, times, flags) }
}
unsafe fn _utimensat(
dirfd: RawFd,
path: &CStr,
times: &[libc::timespec; 2],
flags: AtFlags,
) -> io::Result<()> {
zero_ok(libc::utimensat(
dirfd as libc::c_int,
path.as_ptr(),
times.as_ptr(),
flags.bits(),
))
}
/// `fchmodat(dirfd, path, mode, 0)`
///
/// The flags argument is fixed to 0, so `AT_SYMLINK_NOFOLLOW` is not supported.
/// <details>
/// Platform support for this flag varies widely.
/// </details>
///
/// Note that this implementation does not support `O_PATH` file descriptors,
/// even on platforms where the host libc emulates it.
#[cfg(not(target_os = "wasi"))]
pub fn chmodat<P: PathArg, Fd: AsRawFd>(dirfd: &Fd, path: P, mode: Mode) -> io::Result<()> {
let dirfd = dirfd.as_raw_fd();
let path = path.as_cstr()?;
unsafe { _chmodat(dirfd, &path, mode) }
}
#[cfg(not(any(target_os = "linux", target_os = "wasi")))]
unsafe fn _chmodat(dirfd: RawFd, path: &CStr, mode: Mode) -> io::Result<()> {
zero_ok(libc::fchmodat(
dirfd as libc::c_int,
path.as_ptr(),
mode.bits(),
0,
))
}
#[cfg(target_os = "linux")]
unsafe fn _chmodat(dirfd: RawFd, path: &CStr, mode: Mode) -> io::Result<()> {
// Note that Linux's `fchmodat` does not have a flags argument.
zero_ok(libc::syscall(
libc::SYS_fchmodat,
dirfd as libc::c_int,
path.as_ptr(),
mode.bits(),
))
}
+245
View File
@@ -0,0 +1,245 @@
//! Libc filesystem constants, translated into `bitflags` constants.
use bitflags::bitflags;
use cfg_if::cfg_if;
bitflags! {
/// `FD_*` constants.
pub struct FdFlags: libc::c_int {
/// `FD_CLOEXEC`
const CLOEXEC = libc::FD_CLOEXEC;
}
}
bitflags! {
/// `*_OK` constants for use with [`accessat`].
///
/// [`accessat`]: fn.accessat.html
pub struct Access: libc::c_int {
/// `R_OK`
const READ_OK = libc::R_OK;
/// `W_OK`
const WRITE_OK = libc::W_OK;
/// `X_OK`
const EXEC_OK = libc::X_OK;
/// `F_OK`
const EXISTS = libc::F_OK;
}
}
bitflags! {
/// `AT_*` constants.
pub struct AtFlags: libc::c_int {
/// `AT_REMOVEDIR`
const REMOVEDIR = libc::AT_REMOVEDIR;
/// `AT_SYMLINK_FOLLOW`
const SYMLINK_FOLLOW = libc::AT_SYMLINK_FOLLOW;
/// `AT_SYMLINK_NOFOLLOW`
const SYMLINK_NOFOLLOW = libc::AT_SYMLINK_NOFOLLOW;
/// `AT_EMPTY_PATH`
#[cfg(any(target_os = "android",
target_os = "fuchsia",
target_os = "linux"))]
const EMPTY_PATH = libc::AT_EMPTY_PATH;
/// `AT_EACCESS`
// Temporarily disable on Emscripten until https://github.com/rust-lang/libc/pull/1836
// is available.
#[cfg(not(any(target_os = "emscripten", target_os = "android")))]
const EACCESS = libc::AT_EACCESS;
}
}
#[cfg(not(target_os = "wasi"))]
bitflags! {
/// `S_I*` constants.
pub struct Mode: libc::mode_t {
/// `S_IRWXU`
const IRWXU = libc::S_IRWXU;
/// `S_IRUSR`
const IRUSR = libc::S_IRUSR;
/// `S_IWUSR`
const IWUSR = libc::S_IWUSR;
/// `S_IXUSR`
const IXUSR = libc::S_IXUSR;
/// `S_IRWXG`
const IRWXG = libc::S_IRWXG;
/// `S_IRGRP`
const IRGRP = libc::S_IRGRP;
/// `S_IWGRP`
const IWGRP = libc::S_IWGRP;
/// `S_IXGRP`
const IXGRP = libc::S_IXGRP;
/// `S_IRWXO`
const IRWXO = libc::S_IRWXO;
/// `S_IROTH`
const IROTH = libc::S_IROTH;
/// `S_IWOTH`
const IWOTH = libc::S_IWOTH;
/// `S_IXOTH`
const IXOTH = libc::S_IXOTH;
/// `S_ISUID`
const ISUID = libc::S_ISUID as libc::mode_t;
/// `S_ISGID`
const ISGID = libc::S_ISGID as libc::mode_t;
/// `S_ISVTX`
const ISVTX = libc::S_ISVTX as libc::mode_t;
}
}
#[cfg(target_os = "wasi")]
pub struct Mode {}
#[cfg(target_os = "wasi")]
impl Mode {
pub fn bits(&self) -> u32 {
0
}
}
bitflags! {
/// `O_*` constants.
pub struct OFlags: libc::c_int {
/// `O_ACCMODE`
const ACCMODE = libc::O_ACCMODE;
/// Similar to `ACCMODE`, but just includes the read/write flags, and
/// no other flags.
///
/// Some libc implementations include `O_PATH` in `O_ACCMODE`, which
/// sometimes we really just want the read/write bits. Caution is
/// indicated, as the presence of `O_PATH` may mean that the read/write
/// bits don't have their usual meaning.
const RWMODE = libc::O_RDONLY | libc::O_WRONLY | libc::O_RDWR;
/// `O_APPEND`
const APPEND = libc::O_APPEND;
/// `O_CREAT`
const CREATE = libc::O_CREAT;
/// `O_DIRECTORY`
const DIRECTORY = libc::O_DIRECTORY;
/// `O_DSYNC`
const DSYNC = {
cfg_if! {
if #[cfg(any(target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "wasi"))] {
libc::O_DSYNC
} else if #[cfg(target_os = "freebsd")] {
// FreeBSD lacks `O_DSYNC`; emulate it with `O_SYNC`, which
// is correct, though conservative.
// https://reviews.freebsd.org/D19407#inline-118670
libc::O_SYNC
}
}
};
/// `O_EXCL`
const EXCL = libc::O_EXCL;
/// `O_FSYNC`
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
all(target_os = "linux", not(target_env = "musl")),
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
const FSYNC = libc::O_FSYNC;
/// `O_NOFOLLOW`
const NOFOLLOW = libc::O_NOFOLLOW;
/// `O_NONBLOCK`
const NONBLOCK = libc::O_NONBLOCK;
/// `O_RDONLY`
const RDONLY = libc::O_RDONLY;
/// `O_WRONLY`
const WRONLY = libc::O_WRONLY;
/// `O_RDWR`
const RDWR = libc::O_RDWR;
/// `O_NOCTTY`
const NOCTTY = libc::O_NOCTTY;
/// `O_RSYNC`
#[cfg(any(target_os = "emscripten",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "wasi",
))]
const RSYNC = libc::O_RSYNC;
/// `O_SYNC`
const SYNC = libc::O_SYNC;
/// `O_TRUNC`
const TRUNC = libc::O_TRUNC;
/// `O_PATH`
#[cfg(any(target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "linux",
target_os = "redox"))]
const PATH = libc::O_PATH;
/// `O_CLOEXEC`
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "freebsd",
target_os = "haiku",
target_os = "hermit",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
target_os = "vxworks"))]
const CLOEXEC = libc::O_CLOEXEC;
/// `O_TMPFILE`
#[cfg(any(target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "linux"))]
const TMPFILE = libc::O_TMPFILE;
}
}
+197
View File
@@ -0,0 +1,197 @@
//! `Dir`, `Entry`, and `SeekLoc`.
#[cfg(target_os = "android")]
use crate::fs::android::{seekdir as libc_seekdir, telldir as libc_telldir};
use crate::fs::FileType;
use errno::{set_errno, Errno};
#[cfg(not(any(
target_os = "android",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux"
)))]
use libc::{dirent as libc_dirent, readdir as libc_readdir};
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux"
))]
use libc::{dirent64 as libc_dirent, readdir64 as libc_readdir};
#[cfg(not(target_os = "android"))]
use libc::{seekdir as libc_seekdir, telldir as libc_telldir};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, IntoRawFd, RawFd};
use std::{convert::TryInto, ffi::CStr, io, ptr};
/// `DIR*`
pub struct Dir(ptr::NonNull<libc::DIR>);
impl Dir {
/// Construct a `Dir`, assuming ownership of the file descriptor.
#[inline]
pub fn from<F: IntoRawFd>(fd: F) -> io::Result<Self> {
let fd = fd.into_raw_fd();
unsafe { Self::_from(fd) }
}
unsafe fn _from(fd: RawFd) -> io::Result<Self> {
let d = libc::fdopendir(fd as libc::c_int);
if let Some(d) = ptr::NonNull::new(d) {
Ok(Self(d))
} else {
let e = io::Error::last_os_error();
libc::close(fd as libc::c_int);
Err(e)
}
}
/// `seekdir(self, loc)`
#[inline]
pub fn seek(&self, loc: SeekLoc) {
unsafe { libc_seekdir(self.0.as_ptr(), loc.0) }
}
/// `telldir(self)`
#[inline]
pub fn tell(&self) -> SeekLoc {
SeekLoc(unsafe { libc_telldir(self.0.as_ptr()) })
}
/// `rewinddir(self)`
#[inline]
pub fn rewind(&self) {
unsafe { libc::rewinddir(self.0.as_ptr()) }
}
/// `readdir(self)`, where `None` means the end of the directory.
pub fn read(&self) -> Option<io::Result<Entry>> {
set_errno(Errno(0));
let dirent = unsafe { libc_readdir(self.0.as_ptr()) };
if dirent.is_null() {
let curr_errno = io::Error::last_os_error();
if curr_errno.raw_os_error() == Some(0) {
// We successfully reached the end of the stream.
None
} else {
// `errno` is unknown or non-zero, so an error occurred.
Some(Err(curr_errno))
}
} else {
// We successfully read an entry.
Some(Ok(Entry {
dirent: unsafe { *dirent },
}))
}
}
}
unsafe impl Send for Dir {}
unsafe impl Sync for Dir {}
impl AsRawFd for Dir {
#[inline]
fn as_raw_fd(&self) -> RawFd {
unsafe { libc::dirfd(self.0.as_ptr()) as RawFd }
}
}
impl Drop for Dir {
#[inline]
fn drop(&mut self) {
unsafe { libc::closedir(self.0.as_ptr()) };
}
}
impl Iterator for Dir {
type Item = io::Result<Entry>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
Self::read(self)
}
}
/// `struct dirent`
#[derive(Debug)]
pub struct Entry {
dirent: libc_dirent,
}
impl Entry {
/// Returns the file name of this directory entry.
#[inline]
pub fn file_name(&self) -> &CStr {
unsafe { CStr::from_ptr(self.dirent.d_name.as_ptr()) }
}
/// Returns the type of this directory entry.
#[inline]
pub fn file_type(&self) -> FileType {
FileType::from_dirent_d_type(self.dirent.d_type)
}
/// Return the inode number of this directory entry.
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))]
#[inline]
pub fn ino(&self) -> u64 {
self.dirent.d_ino
}
/// Return the inode number of this directory entry.
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
#[inline]
pub fn ino(&self) -> u64 {
self.dirent.d_fileno.into()
}
/// Return a cookie indicating the location of this entry, for use with [`Dir::seek`].
///
/// [`Dir::seek`]: struct.Dir.html#method.seek
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
#[inline]
pub fn seek_loc(&self) -> io::Result<SeekLoc> {
let off_i64: i64 = self.dirent.d_off;
unsafe { SeekLoc::from_raw(off_i64 as u64) }
}
}
/// A location for use with [`Dir::seek`].
///
/// [`Dir::seek`]: struct.Dir.html#method.seek
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct SeekLoc(libc::c_long);
impl SeekLoc {
/// Return the location encoded as a `u64`. Note that this value is meant to
/// be opaque, and applications shouldn't do anything with it except call
/// `SeekLoc::from_raw`.
#[allow(clippy::useless_conversion)]
#[inline]
pub fn to_raw(&self) -> u64 {
i64::from(self.0) as u64
}
/// Construct a new `SeekLoc` from a value returned by `SeekLoc::to_raw`.
///
/// # Safety
///
/// The passed-in `loc` value must be a value returned from
/// `SeekLoc::to_raw`.
#[inline]
pub unsafe fn from_raw(loc: u64) -> io::Result<Self> {
Ok(Self(
loc.try_into()
.map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?,
))
}
}
+55
View File
@@ -0,0 +1,55 @@
use crate::zero_ok;
#[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "l4re")))]
use libc::posix_fadvise as libc_posix_fadvise;
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
use libc::posix_fadvise64 as libc_posix_fadvise;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, RawFd};
use std::{convert::TryInto, io};
/// `POSIX_FADV_*` constants.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(i32)]
pub enum Advice {
/// `POSIX_FADV_NORMAL`
Normal = libc::POSIX_FADV_NORMAL,
/// `POSIX_FADV_SEQUENTIAL`
Sequential = libc::POSIX_FADV_SEQUENTIAL,
/// `POSIX_FADV_RANDOM`
Random = libc::POSIX_FADV_RANDOM,
/// `POSIX_FADV_NOREUSE`
NoReuse = libc::POSIX_FADV_NOREUSE,
/// `POSIX_FADV_WILLNEED`
WillNeed = libc::POSIX_FADV_WILLNEED,
/// `POSIX_FADV_DONTNEED`
DontNeed = libc::POSIX_FADV_DONTNEED,
}
/// `posix_fadvise(fd, offset, len, advice)`
#[inline]
pub fn fadvise<Fd: AsRawFd>(fd: &Fd, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
let fd = fd.as_raw_fd();
unsafe { _fadvise(fd, offset, len, advice) }
}
unsafe fn _fadvise(fd: RawFd, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
if let (Ok(offset), Ok(len)) = (offset.try_into(), len.try_into()) {
zero_ok(libc_posix_fadvise(
fd as libc::c_int,
offset,
len,
advice as libc::c_int,
))?;
}
// If the offset or length can't be converted, ignore the advice, as it
// isn't likely to be useful in that case.
Ok(())
}
+53
View File
@@ -0,0 +1,53 @@
use crate::{
fs::{FdFlags, OFlags},
negone_err, zero_ok,
};
use std::io;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, RawFd};
/// `fcntl(fd, F_GETFD)`
#[inline]
pub fn getfd<Fd: AsRawFd>(fd: &Fd) -> io::Result<FdFlags> {
let fd = fd.as_raw_fd();
unsafe { _getfd(fd) }
}
unsafe fn _getfd(fd: RawFd) -> io::Result<FdFlags> {
negone_err(libc::fcntl(fd as libc::c_int, libc::F_GETFD)).map(FdFlags::from_bits_truncate)
}
/// `fcntl(fd, F_SETFD, flags)`
#[inline]
pub fn setfd<Fd: AsRawFd>(fd: &Fd, flags: FdFlags) -> io::Result<()> {
let fd = fd.as_raw_fd();
unsafe { _setfd(fd, flags) }
}
unsafe fn _setfd(fd: RawFd, flags: FdFlags) -> io::Result<()> {
zero_ok(libc::fcntl(fd as libc::c_int, libc::F_SETFD, flags.bits()))
}
/// `fcntl(fd, F_GETFL)`
#[inline]
pub fn getfl<Fd: AsRawFd>(fd: &Fd) -> io::Result<OFlags> {
let fd = fd.as_raw_fd();
unsafe { _getfl(fd) }
}
unsafe fn _getfl(fd: RawFd) -> io::Result<OFlags> {
negone_err(libc::fcntl(fd as libc::c_int, libc::F_GETFL)).map(OFlags::from_bits_truncate)
}
/// `fcntl(fd, F_SETFL, flags)`
#[inline]
pub fn setfl<Fd: AsRawFd>(fd: &Fd, flags: OFlags) -> io::Result<()> {
let fd = fd.as_raw_fd();
unsafe { _setfl(fd, flags) }
}
unsafe fn _setfl(fd: RawFd, flags: OFlags) -> io::Result<()> {
zero_ok(libc::fcntl(fd as libc::c_int, libc::F_SETFL, flags.bits()))
}
+134
View File
@@ -0,0 +1,134 @@
//! Functions which operate on file descriptors.
#[cfg(not(target_os = "wasi"))]
use crate::fs::Mode;
use crate::{negone_err, zero_ok};
#[cfg(not(any(
target_os = "linux",
target_os = "emscripten",
target_os = "l4re",
target_os = "netbsd",
target_os = "wasi"
)))]
use libc::fstatfs as libc_fstatfs;
#[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "l4re")))]
use libc::lseek as libc_lseek;
#[cfg(not(any(
target_os = "ios",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "redox",
target_os = "wasi",
)))]
use libc::off64_t as libc_off_t;
#[cfg(any(
target_os = "ios",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "redox",
target_os = "wasi",
))]
use libc::off_t as libc_off_t;
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
use libc::{fstatfs64 as libc_fstatfs, lseek64 as libc_lseek};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, RawFd};
use std::{
convert::TryInto,
io::{self, SeekFrom},
};
#[cfg(not(any(target_os = "netbsd", target_os = "wasi")))]
// not implemented in libc for netbsd yet
use {crate::fs::LibcStatFs, std::mem::MaybeUninit};
/// `lseek(fd, offset, whence)`
#[inline]
pub fn seek<Fd: AsRawFd>(fd: &Fd, pos: SeekFrom) -> io::Result<u64> {
let fd = fd.as_raw_fd();
unsafe { _seek(fd, pos) }
}
unsafe fn _seek(fd: RawFd, pos: SeekFrom) -> io::Result<u64> {
let (whence, offset): (libc::c_int, libc_off_t) = match pos {
SeekFrom::Start(pos) => (
libc::SEEK_SET,
pos.try_into()
.map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?,
),
SeekFrom::End(offset) => (libc::SEEK_END, offset),
SeekFrom::Current(offset) => (libc::SEEK_CUR, offset),
};
let offset = negone_err(libc_lseek(fd as libc::c_int, offset, whence))?;
Ok(offset.try_into().unwrap())
}
/// `lseek(fd, 0, SEEK_CUR)`
#[inline]
pub fn tell<Fd: AsRawFd>(fd: &Fd) -> io::Result<u64> {
let fd = fd.as_raw_fd();
unsafe { _tell(fd) }
}
unsafe fn _tell(fd: RawFd) -> io::Result<u64> {
let offset = negone_err(libc_lseek(fd as libc::c_int, 0, libc::SEEK_CUR))?;
Ok(offset.try_into().unwrap())
}
/// `fchmod(fd)`.
///
/// Note that this implementation does not support `O_PATH` file descriptors,
/// even on platforms where the host libc emulates it.
#[cfg(not(target_os = "wasi"))]
#[inline]
pub fn fchmod<Fd: AsRawFd>(fd: &Fd, mode: Mode) -> io::Result<()> {
let fd = fd.as_raw_fd();
unsafe { _fchmod(fd, mode) }
}
#[cfg(not(any(target_os = "linux", target_os = "wasi")))]
unsafe fn _fchmod(fd: RawFd, mode: Mode) -> io::Result<()> {
zero_ok(libc::fchmod(fd as libc::c_int, mode.bits()))
}
#[cfg(target_os = "linux")]
unsafe fn _fchmod(fd: RawFd, mode: Mode) -> io::Result<()> {
// Use `libc::syscall` rather than `libc::fchmod` because some libc
// implementations, such as musl, add extra logic to `fchmod` to emulate
// support for `O_PATH`, which uses `/proc` outside our control and
// interferes with our own use of `O_PATH`.
zero_ok(libc::syscall(
libc::SYS_fchmod,
fd as libc::c_int,
mode.bits(),
))
}
/// `fstatfs(fd)`
#[cfg(not(any(target_os = "netbsd", target_os = "wasi")))] // not implemented in libc for netbsd yet
#[inline]
pub fn fstatfs<Fd: AsRawFd>(fd: &Fd) -> io::Result<LibcStatFs> {
let fd = fd.as_raw_fd();
unsafe { _fstatfs(fd) }
}
#[cfg(not(any(target_os = "netbsd", target_os = "wasi")))] // not implemented in libc for netbsd yet
unsafe fn _fstatfs(fd: RawFd) -> io::Result<LibcStatFs> {
let mut statfs = MaybeUninit::<LibcStatFs>::uninit();
zero_ok(libc_fstatfs(fd as libc::c_int, statfs.as_mut_ptr()))?;
Ok(statfs.assume_init())
}
/// `futimens(fd, times)`
#[inline]
pub fn futimens<Fd: AsRawFd>(fd: &Fd, times: &[libc::timespec; 2]) -> io::Result<()> {
let fd = fd.as_raw_fd();
unsafe { _futimens(fd, times) }
}
unsafe fn _futimens(fd: RawFd, times: &[libc::timespec; 2]) -> io::Result<()> {
zero_ok(libc::futimens(fd as libc::c_int, times.as_ptr()))
}
+61
View File
@@ -0,0 +1,61 @@
/// `S_IF*` constants.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FileType {
/// `S_IFREG`
RegularFile,
/// `S_IFDIR`
Directory,
/// `S_IFLNK`
Symlink,
/// `S_IFIFO`
Fifo,
/// `S_IFSOCK`
Socket,
/// `S_IFCHR`
CharacterDevice,
/// `S_IFBLK`
BlockDevice,
/// An unknown filesystem object.
Unknown,
}
impl FileType {
/// Construct a `FileType` from the `st_mode` field of a `LibcStat`.
pub fn from_libc_stat_st_mode(st_mode: libc::mode_t) -> Self {
match st_mode & libc::S_IFMT {
libc::S_IFREG => Self::RegularFile,
libc::S_IFDIR => Self::Directory,
libc::S_IFLNK => Self::Symlink,
#[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFIFO`.
libc::S_IFIFO => Self::Fifo,
#[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFSOCK`.
libc::S_IFSOCK => Self::Socket,
libc::S_IFCHR => Self::CharacterDevice,
libc::S_IFBLK => Self::BlockDevice,
_ => Self::Unknown,
}
}
/// Construct a `FileType` from the `d_type` field of a `libc::dirent`.
pub(crate) fn from_dirent_d_type(d_type: u8) -> Self {
match d_type {
libc::DT_REG => Self::RegularFile,
libc::DT_DIR => Self::Directory,
libc::DT_LNK => Self::Symlink,
#[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `DT_SOCK`.
libc::DT_SOCK => Self::Socket,
#[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `DT_FIFO`.
libc::DT_FIFO => Self::Fifo,
libc::DT_CHR => Self::CharacterDevice,
libc::DT_BLK => Self::BlockDevice,
/* libc::DT_UNKNOWN | */ _ => Self::Unknown,
}
}
}
+21
View File
@@ -0,0 +1,21 @@
use crate::zero_ok;
use std::{
io,
os::unix::io::{AsRawFd, RawFd},
};
/// `fcntl(fd, F_GETPATH)`
pub fn getpath<Fd: AsRawFd>(fd: &Fd, buf: &mut [u8]) -> io::Result<()> {
let fd = fd.as_raw_fd();
unsafe { _getpath(fd, buf) }
}
unsafe fn _getpath(fd: RawFd, buf: &mut [u8]) -> io::Result<()> {
// From the macOS `fcntl` man page:
// `F_GETPATH` - Get the path of the file descriptor `Fildes`. The argument
// must be a buffer of size `MAXPATHLEN` or greater.
//
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html
assert!(buf.len() >= libc::PATH_MAX as usize);
zero_ok(libc::fcntl(fd, libc::F_GETPATH, buf.as_mut_ptr()))
}
+22
View File
@@ -0,0 +1,22 @@
/// `makedev(maj, min)`
#[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "wasi")))]
#[inline]
pub fn makedev(maj: u32, min: u32) -> u64 {
unsafe { libc::makedev(maj, min) }
}
/// `makedev(maj, min)`
#[cfg(target_os = "android")]
#[inline]
pub fn makedev(maj: u32, min: u32) -> u64 {
// Android's `makedev` oddly has signed argument types.
unsafe { libc::makedev(maj as i32, min as i32) }
}
/// `makedev(maj, min)`
#[cfg(target_os = "emscripten")]
#[inline]
pub fn makedev(maj: u32, min: u32) -> u64 {
// Emscripten's `makedev` has a 32-bit return value.
u64::from(unsafe { libc::makedev(maj, min) })
}
+91
View File
@@ -0,0 +1,91 @@
//! Filesystem operations.
#[cfg(target_os = "android")]
mod android;
mod at;
mod constants;
mod dir;
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "netbsd")))]
mod fadvise;
mod fcntl;
mod fd;
mod file_type;
#[cfg(any(target_os = "macos", target_os = "ios"))]
mod getpath;
#[cfg(not(any(
target_os = "netbsd",
target_os = "macos",
target_os = "ios",
target_os = "freebsd"
)))]
mod makedev;
mod path_arg;
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
// Most Modern OS's have `preadv`/`pwritev`.
mod pv;
#[cfg(any(target_os = "macos", target_os = "ios"))]
mod rdadvise;
#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod statx;
pub use at::{
accessat, linkat, mkdirat, openat, readlinkat, renameat, statat, symlinkat, unlinkat, utimensat,
};
#[cfg(not(target_os = "wasi"))]
pub use at::{chmodat, cwd};
pub use constants::{Access, AtFlags, FdFlags, Mode, OFlags};
pub use dir::{Dir, Entry, SeekLoc};
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "netbsd")))]
pub use fadvise::{fadvise, Advice};
pub use fcntl::{getfd, getfl, setfd, setfl};
#[cfg(not(target_os = "wasi"))]
pub use fd::fchmod;
#[cfg(not(any(target_os = "netbsd", target_os = "wasi")))]
// not implemented in libc for netbsd yet
pub use fd::fstatfs;
pub use fd::{futimens, seek, tell};
pub use file_type::FileType;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub use getpath::getpath;
#[cfg(not(any(
target_os = "netbsd",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "wasi",
)))]
pub use makedev::makedev;
pub use path_arg::PathArg;
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
pub use pv::{preadv, pwritev};
#[cfg(any(target_os = "ios", target_os = "macos"))]
pub use rdadvise::rdadvise;
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub use statx::statx;
/// Re-export `libc::stat` (or `libc::stat64` where applicable).
#[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "l4re")))]
pub type LibcStat = libc::stat;
/// Re-export `libc::statfs` (or `libc::statfs64` where applicable).
#[cfg(not(any(
target_os = "linux",
target_os = "emscripten",
target_os = "l4re",
target_os = "netbsd",
target_os = "wasi",
)))]
#[allow(clippy::module_name_repetitions)]
pub type LibcStatFs = libc::statfs;
/// Re-export `libc::stat` (or `libc::stat64` where applicable).
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
pub type LibcStat = libc::stat64;
/// Re-export `libc::statfs` (or `libc::statfs64` where applicable).
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
pub type LibcStatFs = libc::statfs64;
/// Re-export `libc::statx`. Only available on Linux with GLIBC for now.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub type LibcStatx = libc::statx;
+446
View File
@@ -0,0 +1,446 @@
#[cfg(target_os = "hermit")]
use std::os::hermit::ext::ffi::OsStrExt;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
#[cfg(target_os = "vxworks")]
use std::os::vxworks::ext::ffi::OsStrExt;
#[cfg(target_os = "wasi")]
use std::os::wasi::ffi::OsStrExt;
use std::{
borrow::Cow,
ffi::{CStr, CString, OsStr, OsString},
io,
ops::Deref,
path::{Component, Components, Iter, Path, PathBuf},
str,
};
/// A trait for passing path arguments. This is similar to
/// [`AsRef`]`<`[`Path`]`>`, but is implemented for more kinds of strings and
/// can convert into more kinds of strings.
///
/// [`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
/// [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html
pub trait PathArg {
/// Return a view of this string as a string slice.
fn as_str(&self) -> io::Result<&str>;
/// Return a potentially-lossy rendering of this string as a `Cow<str>`.
fn to_string_lossy(&self) -> Cow<str>;
/// Return a view of this string as a maybe-owend [`CStr`].
///
/// [`CStr`]: https://doc.rust-lang.org/std/ffi/struct.CStr.html
#[cfg(not(windows))]
fn as_cstr(&self) -> io::Result<Cow<CStr>>;
/// Return a view of this string as a byte slice.
#[cfg(not(windows))]
fn as_utf8_bytes(&self) -> &[u8];
/// Return a view of this string as a maybe-owend [`OsStr`].
///
/// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>>;
}
impl PathArg for &str {
#[inline]
fn as_str(&self) -> io::Result<&str> {
Ok(self)
}
fn to_string_lossy(&self) -> Cow<str> {
Cow::Borrowed(self)
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(self.as_bytes())?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl PathArg for String {
#[inline]
fn as_str(&self) -> io::Result<&str> {
Ok(self)
}
fn to_string_lossy(&self) -> Cow<str> {
Cow::Borrowed(self)
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(self.as_bytes())?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl PathArg for &OsStr {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.to_str().ok_or_else(utf8_error)
}
fn to_string_lossy(&self) -> Cow<str> {
OsStr::to_string_lossy(self)
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(self.as_utf8_bytes())?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl PathArg for OsString {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.as_os_str().to_str().ok_or_else(utf8_error)
}
fn to_string_lossy(&self) -> Cow<str> {
self.as_os_str().to_string_lossy()
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(self.as_utf8_bytes())?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl PathArg for &Path {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.as_os_str().to_str().ok_or_else(utf8_error)
}
fn to_string_lossy(&self) -> Cow<str> {
Path::to_string_lossy(self)
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(self.as_os_str().as_bytes())?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_os_str().as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl PathArg for PathBuf {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.as_os_str().to_str().ok_or_else(utf8_error)
}
fn to_string_lossy(&self) -> Cow<str> {
self.as_os_str().to_string_lossy()
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(self.as_os_str().as_bytes())?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_os_str().as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl PathArg for &CStr {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.to_str().map_err(|_| utf8_error())
}
fn to_string_lossy(&self) -> Cow<str> {
CStr::to_string_lossy(self)
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Borrowed(self))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.to_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl PathArg for CString {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.as_c_str().to_str().map_err(|_| utf8_error())
}
fn to_string_lossy(&self) -> Cow<str> {
self.as_c_str().to_string_lossy()
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Borrowed(self.as_c_str()))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl<'a> PathArg for Cow<'a, OsStr> {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.deref().to_str().ok_or_else(utf8_error)
}
fn to_string_lossy(&self) -> Cow<str> {
self.deref().to_string_lossy()
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(self.deref().as_bytes())?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.deref().as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl<'a> PathArg for Component<'a> {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.as_os_str().to_str().ok_or_else(utf8_error)
}
fn to_string_lossy(&self) -> Cow<str> {
self.as_os_str().to_string_lossy()
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(self.as_os_str().as_bytes())?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_os_str().as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl<'a> PathArg for Components<'a> {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.as_path().to_str().ok_or_else(utf8_error)
}
fn to_string_lossy(&self) -> Cow<str> {
self.as_path().to_string_lossy()
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(
self.as_path().as_os_str().as_bytes(),
)?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_path().as_os_str().as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl<'a> PathArg for Iter<'a> {
#[inline]
fn as_str(&self) -> io::Result<&str> {
self.as_path().to_str().ok_or_else(utf8_error)
}
fn to_string_lossy(&self) -> Cow<str> {
self.as_path().to_string_lossy()
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(
self.as_path().as_os_str().as_bytes(),
)?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self.as_path().as_os_str().as_bytes()
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl PathArg for &[u8] {
#[inline]
fn as_str(&self) -> io::Result<&str> {
str::from_utf8(self).map_err(|_| utf8_error())
}
fn to_string_lossy(&self) -> Cow<str> {
String::from_utf8_lossy(self)
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(*self)?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
impl PathArg for Vec<u8> {
#[inline]
fn as_str(&self) -> io::Result<&str> {
str::from_utf8(self).map_err(|_| utf8_error())
}
fn to_string_lossy(&self) -> Cow<str> {
String::from_utf8_lossy(self)
}
#[cfg(not(windows))]
#[inline]
fn as_cstr(&self) -> io::Result<Cow<CStr>> {
Ok(Cow::Owned(CString::new(self.clone())?))
}
#[cfg(not(windows))]
#[inline]
fn as_utf8_bytes(&self) -> &[u8] {
self
}
#[cfg(windows)]
fn as_os_str(&self) -> io::Result<Cow<OsStr>> {
self.as_ref()
}
}
fn utf8_error() -> io::Error {
io::Error::from_raw_os_error(libc::EILSEQ)
}
+81
View File
@@ -0,0 +1,81 @@
//! Positioned *and* vectored I/O: `preadv` and `pwritev`.
use crate::negone_err;
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))]
use libc::{preadv as libc_preadv, pwritev as libc_pwritev};
#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
use libc::{preadv64 as libc_preadv, pwritev64 as libc_pwritev};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, RawFd};
use std::{
cmp,
convert::TryInto,
io::{self, IoSlice, IoSliceMut},
sync::atomic::{AtomicUsize, Ordering},
};
/// `preadv(fd, bufs.as_ptr(), bufs.len(), offset)`
#[inline]
pub fn preadv<Fd: AsRawFd>(fd: &Fd, bufs: &[IoSliceMut], offset: u64) -> io::Result<usize> {
let fd = fd.as_raw_fd();
unsafe { _preadv(fd, bufs, offset) }
}
unsafe fn _preadv(fd: RawFd, bufs: &[IoSliceMut], offset: u64) -> io::Result<usize> {
let offset = offset
.try_into()
.map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
let nread = negone_err(libc_preadv(
fd as libc::c_int,
bufs.as_ptr() as *const libc::iovec,
cmp::min(bufs.len(), max_iov()).try_into().unwrap(),
offset,
))?;
Ok(nread.try_into().unwrap())
}
/// `pwritev(fd, bufs.as_ptr(), bufs.len(), offset)`
#[inline]
pub fn pwritev<Fd: AsRawFd>(fd: &Fd, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
let fd = fd.as_raw_fd();
unsafe { _pwritev(fd, bufs, offset) }
}
unsafe fn _pwritev(fd: RawFd, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
let offset = offset
.try_into()
.map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
let nwritten = negone_err(libc_pwritev(
fd as libc::c_int,
bufs.as_ptr() as *const libc::iovec,
cmp::min(bufs.len(), max_iov()).try_into().unwrap(),
offset,
))?;
Ok(nwritten.try_into().unwrap())
}
// These functions are derived from Rust's library/std/src/sys/unix/fd.rs at
// revision 108e90ca78f052c0c1c49c42a22c85620be19712.
#[cfg(not(any(target_os = "redox", target_env = "newlib")))]
fn max_iov() -> usize {
static LIM: AtomicUsize = AtomicUsize::new(0);
let mut lim = LIM.load(Ordering::Relaxed);
if lim == 0 {
let ret = unsafe { libc::sysconf(libc::_SC_IOV_MAX) };
// 16 is the minimum value required by POSIX.
lim = if ret > 0 { ret as usize } else { 16 };
LIM.store(lim, Ordering::Relaxed);
}
lim
}
#[cfg(any(target_os = "redox", target_env = "newlib"))]
fn max_iov() -> usize {
16 // The minimum value required by POSIX.
}
+46
View File
@@ -0,0 +1,46 @@
use crate::zero_ok;
use std::{
convert::TryInto,
io,
os::unix::io::{AsRawFd, RawFd},
};
/// `fcntl(fd, F_RDADVISE, radvisory { offset, len })`
pub fn rdadvise<Fd: AsRawFd>(fd: &Fd, offset: u64, len: u64) -> io::Result<()> {
let fd = fd.as_raw_fd();
unsafe { _rdadvise(fd, offset, len) }
}
unsafe fn _rdadvise(fd: RawFd, offset: u64, len: u64) -> io::Result<()> {
// From the macOS `fcntl` man page:
// `F_RDADVISE` - Issue an advisory read async with no copy to user.
//
// The `F_RDADVISE` command operates on the following structure which holds information passed
// from the user to the system:
//
// ```
// struct radvisory {
// off_t ra_offset; /* offset into the file */
// int ra_count; /* size of the read */
// };
// ```
//
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html
let ra_offset = match offset.try_into() {
Ok(len) => len,
// If this conversion fails, the user is providing an offset outside
// any possible file extent, so just ignore it.
Err(_) => return Ok(()),
};
let ra_count = match len.try_into() {
Ok(len) => len,
// If this conversion fails, the user is providing a dubiously large
// hint which is unlikely to improve performance.
Err(_) => return Ok(()),
};
let radvisory = libc::radvisory {
ra_offset,
ra_count,
};
zero_ok(libc::fcntl(fd, libc::F_RDADVISE, &radvisory))
}
+116
View File
@@ -0,0 +1,116 @@
//! Linux `statx`.
use crate::{
fs::{AtFlags, LibcStatx, PathArg},
zero_ok,
};
use bitflags::bitflags;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, RawFd};
use std::{ffi::CStr, io, mem::MaybeUninit};
bitflags! {
/// `STATX_*` constants.
pub struct StatxFlags: u32 {
/// `STATX_TYPE`
const TYPE = libc::STATX_TYPE;
/// `STATX_MODE`
const MODE = libc::STATX_MODE;
/// `STATX_NLINK`
const NLINK = libc::STATX_NLINK;
/// `STATX_UID`
const UID = libc::STATX_UID;
/// `STATX_GID`
const GID = libc::STATX_GID;
/// `STATX_ATIME`
const ATIME = libc::STATX_ATIME;
/// `STATX_MTIME`
const MTIME = libc::STATX_MTIME;
/// `STATX_CTIME`
const CTIME = libc::STATX_CTIME;
/// `STATX_INO`
const INO = libc::STATX_INO;
/// `STATX_SIZE`
const SIZE = libc::STATX_SIZE;
/// `STATX_BLOCKS`
const BLOCKS = libc::STATX_BLOCKS;
/// `STATX_BASIC_STATS`
const BASIC_STATS = libc::STATX_BASIC_STATS;
/// `STATX_BTIME`
const BTIME = libc::STATX_BTIME;
/// `STATX_ALL`
const ALL = libc::STATX_ALL;
}
}
/// `statx(dirfd, path, flags, mask, statxbuf)`. Note that this isn't available
/// on older Linux; returns `ENOSYS` in that case.
#[inline]
pub fn statx<P: PathArg, Fd: AsRawFd>(
dirfd: &Fd,
path: P,
flags: AtFlags,
mask: StatxFlags,
) -> io::Result<LibcStatx> {
let dirfd = dirfd.as_raw_fd();
let path = path.as_cstr()?;
unsafe { _statx(dirfd, &path, flags, mask) }
}
macro_rules! syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
unsafe fn $name($($arg_name: $t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
errno::set_errno(errno::Errno(libc::ENOSYS));
-1
}
}
)
}
#[allow(non_upper_case_globals)]
unsafe fn _statx(
dirfd: RawFd,
path: &CStr,
flags: AtFlags,
mask: StatxFlags,
) -> io::Result<LibcStatx> {
syscall! {
fn statx(
dirfd: libc::c_int,
path: *const libc::c_char,
flags: libc::c_int,
mask: libc::c_uint,
buf: *mut LibcStatx
) -> libc::c_int
}
let mut statx_buf = MaybeUninit::<LibcStatx>::uninit();
zero_ok(statx(
dirfd as libc::c_int,
path.as_ptr(),
flags.bits(),
mask.bits(),
statx_buf.as_mut_ptr(),
))?;
Ok(statx_buf.assume_init())
}
+78
View File
@@ -0,0 +1,78 @@
//! Functions which operate on file descriptors.
use crate::zero_ok;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, RawFd};
use std::{convert::TryInto, io, mem::MaybeUninit};
/// `ioctl(fd, FIONREAD)`.
#[inline]
pub fn fionread<Fd: AsRawFd>(fd: &Fd) -> io::Result<u64> {
let fd = fd.as_raw_fd();
unsafe { _fionread(fd) }
}
unsafe fn _fionread(fd: RawFd) -> io::Result<u64> {
let mut nread = MaybeUninit::<libc::c_int>::uninit();
zero_ok(libc::ioctl(
fd as libc::c_int,
libc::FIONREAD,
nread.as_mut_ptr(),
))?;
Ok(nread.assume_init().try_into().unwrap())
}
/// `isatty(fd)`
#[inline]
pub fn isatty<Fd: AsRawFd>(fd: &Fd) -> io::Result<bool> {
let fd = fd.as_raw_fd();
unsafe { _isatty(fd) }
}
unsafe fn _isatty(fd: RawFd) -> io::Result<bool> {
let res = libc::isatty(fd as libc::c_int);
if res == 0 {
let err = io::Error::last_os_error();
match err.raw_os_error() {
#[cfg(not(target_os = "linux"))]
Some(libc::ENOTTY) => Ok(false),
// Old Linux versions reportedly return `EINVAL`.
// https://man7.org/linux/man-pages/man3/isatty.3.html#ERRORS
#[cfg(target_os = "linux")]
Some(libc::ENOTTY) | Some(libc::EINVAL) => Ok(false),
_ => Err(err),
}
} else {
Ok(true)
}
}
/// `fcntl(fd, F_GETFL) & O_ACCMODE`. Returns a pair of booleans indicating
/// whether the file descriptor is readable and/or writeable, respectively.
pub fn is_read_write<Fd: AsRawFd>(fd: &Fd) -> io::Result<(bool, bool)> {
let mode = crate::fs::getfl(fd)?;
// Check for `O_PATH`.
#[cfg(any(
target_os = "android",
target_os = "fuchsia",
target_os = "linux",
target_os = "emscripten"
))]
if mode.contains(crate::fs::OFlags::PATH) {
return Ok((false, false));
}
// Use `RWMODE` rather than `ACCMODE` as `ACCMODE` may include `O_PATH`.
// We handled `O_PATH` above.
match mode & crate::fs::OFlags::RWMODE {
crate::fs::OFlags::RDONLY => Ok((true, false)),
crate::fs::OFlags::RDWR => Ok((true, true)),
crate::fs::OFlags::WRONLY => Ok((false, true)),
_ => unreachable!(),
}
}
+7
View File
@@ -0,0 +1,7 @@
//! I/O operations.
mod fd;
mod poll;
pub use fd::{fionread, is_read_write, isatty};
pub use poll::{PollFd, PollFdVec};
+74
View File
@@ -0,0 +1,74 @@
use crate::negone_err;
use bitflags::bitflags;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, RawFd};
use std::{convert::TryInto, io};
bitflags! {
pub struct PollFlags: libc::c_short {
const POLLIN = libc::POLLIN;
const POLLPRI = libc::POLLPRI;
const POLLOUT = libc::POLLOUT;
const POLLRDNORM = libc::POLLRDNORM;
const POLLWRNORM = libc::POLLWRNORM;
const POLLRDBAND = libc::POLLRDBAND;
const POLLWRBAND = libc::POLLWRBAND;
const POLLERR = libc::POLLERR;
const POLLHUP = libc::POLLHUP;
const POLLNVAL = libc::POLLNVAL;
}
}
/// `pollfd`
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(C)]
pub struct PollFd(libc::pollfd);
impl PollFd {
/// # Safety
///
/// `PollFd` does not take ownership of the file descriptors passed in here,
/// and they need to live at least through the `PollFdVec::poll` call.
pub unsafe fn new<Fd: AsRawFd>(fd: &Fd, events: PollFlags) -> Self {
let fd = fd.as_raw_fd();
Self::_new(fd, events)
}
unsafe fn _new(fd: RawFd, events: PollFlags) -> Self {
Self(libc::pollfd {
fd: fd as libc::c_int,
events: events.bits(),
revents: PollFlags::empty().bits(),
})
}
/// Return the ready events.
pub fn revents(self) -> PollFlags {
PollFlags::from_bits(self.0.revents).unwrap()
}
}
/// A [`Vec`] of `libc::pollfd`.
///
/// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PollFdVec {
fds: Vec<libc::pollfd>,
}
impl PollFdVec {
/// `poll(self.fds.as_mut_ptr(), self.fds.len(), timeout)`
pub fn poll(&mut self, timeout: libc::c_int) -> io::Result<usize> {
let nfds = self
.fds
.len()
.try_into()
.map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?;
let nready = negone_err(unsafe { libc::poll(self.fds.as_mut_ptr(), nfds, timeout) })?;
Ok(nready.try_into().unwrap())
}
}
+103
View File
@@ -0,0 +1,103 @@
//! `posish` provides safe wrappers to `libc` functions. The current focus is
//! on the functionality needed by [`cap-std`] and [`system-interface`] that
//! isn't provided by [`std`] or [`getrandom`].
//!
//! The wrappers perform the following tasks:
//! - Error values are translated to `Result`s.
//! - Out-parameters are translated to return values.
//! - Path arguments can by any kind of string type.
//! - File descriptors are passed in through arguments implementing
//! [`AsRawFd`] instead of as bare integers and returned as
//! [`std::fs::File`]s.
//! - Constants use `enum`s and [`bitflags`] types.
//! - Multiplexed functions (eg. `fcntl`, `ioctl`, etc.) are de-multiplexed.
//! - Variadic functions (eg. `openat`, etc.) are presented as non-variadic.
//! - Functions and types which need `64` suffixes to enable large-file
//! support are used automatically.
//! - Behaviors that depend on the sizes of C types like `long` are hidden.
//! - File offsets and sizes are presented as `i64` and `u64` rather than
//! `off_t`.
//! - In some places, more human-friendly and less historical-accident names
//! are used.
//!
//! Things they don't do include:
//! - Emulating functions that aren't natively supported on a platform.
//! - Detecting whether functions are supported at runtime.
//! - Hiding significant differences between platforms.
//! - Hiding ambient authorities.
//! - Imposing sandboxing features such as filesystem path or network address
//! sandboxing.
//!
//! See [`cap-std`] and [`system-interface`] for libraries which do hide
//! ambient authorities and perform sandboxing.
//!
//! # Safety
//!
//! This library follows [`std`] in considering dynamic integer values that
//! have no meaning outside of OS APIs to be similar to raw pointers, from a
//! safety perspective. For example,
//! [`std::os::unix::io::FromRawFd::from_raw_fd`] is unsafe since it takes a
//! raw file descriptor. In this library, raw file descriptors and raw
//! directory seek locations are considered to be similar to raw pointers in
//! terms of safety.
//!
//! [`cap-std`]: https://crates.io/crates/cap-std
//! [`system-interface`]: https://crates.io/crates/system-interface
//! [`std`]: https://doc.rust-lang.org/std/
//! [`getrandom`]: https://crates.io/crates/getrandom
//! [`AsRawFd`]: https://doc.rust-lang.org/std/os/unix/io/trait.AsRawFd.html
//! [`std::fs::File`]: https://doc.rust-lang.org/std/fs/struct.File.html
//! [`bitflags`]: https://crates.io/crates/bitflags
//! [`std::os::unix::io::FromRawFd::from_raw_fd`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.from_raw_fd
#![deny(missing_docs)]
#![cfg_attr(target_os = "wasi", feature(wasi_ext))]
#[macro_use]
mod weak;
pub mod fs;
pub mod io;
#[cfg(not(target_os = "wasi"))] // WASI doesn't support `net` yet.
pub mod net;
pub mod process;
pub mod time;
/// Given a `libc` return value, translate `0` into `Ok(())` and any other value
/// to an `Err` with the error from `errno`.
fn zero_ok<T: LibcResult>(t: T) -> std::io::Result<()> {
if t.is_zero() {
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
/// Given a `libc` return value, translate `-1` into an `Err` with the error
/// from `errno`, and any other value to an `Ok` containing the value.
fn negone_err<T: LibcResult>(t: T) -> std::io::Result<T> {
if t.is_negone() {
Err(std::io::Error::last_os_error())
} else {
Ok(t)
}
}
trait LibcResult {
fn is_zero(&self) -> bool;
fn is_negone(&self) -> bool;
}
macro_rules! is_impls {
($($t:ident)*) => ($(impl LibcResult for $t {
fn is_zero(&self) -> bool {
*self == 0
}
fn is_negone(&self) -> bool {
*self == -1
}
})*)
}
is_impls! { i32 i64 isize }
+5
View File
@@ -0,0 +1,5 @@
//! Network-related operations.
mod socket;
pub use socket::{socket_type, SocketType};
+50
View File
@@ -0,0 +1,50 @@
use crate::zero_ok;
use std::{
io,
mem::{size_of, MaybeUninit},
os::unix::io::{AsRawFd, RawFd},
};
/// `SOCK_*` constants.
#[derive(Debug, Clone, Copy)]
#[repr(i32)]
pub enum SocketType {
/// `SOCK_STREAM`.
Stream = libc::SOCK_STREAM,
/// `SOCK_DGRAM`.
Datagram = libc::SOCK_DGRAM,
/// `SOCK_SEQPACKET`.
SeqPacket = libc::SOCK_SEQPACKET,
/// `SOCK_RAW`.
Raw = libc::SOCK_RAW,
/// `SOCK_RDM`.
Rdm = libc::SOCK_RDM,
}
/// `getsockopt(fd, SOL_SOCKET, SO_TYPE)`
pub fn socket_type<Fd: AsRawFd>(fd: &Fd) -> io::Result<SocketType> {
let fd = fd.as_raw_fd();
unsafe { _socket_type(fd) }
}
unsafe fn _socket_type(fd: RawFd) -> io::Result<SocketType> {
let mut buffer = MaybeUninit::<SocketType>::uninit();
let mut out_len = size_of::<SocketType>() as libc::socklen_t;
zero_ok(libc::getsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_TYPE,
buffer.as_mut_ptr() as *mut libc::c_void,
&mut out_len,
))?;
assert_eq!(
out_len as usize,
size_of::<SocketType>(),
"unexpected SocketType size"
);
Ok(buffer.assume_init())
}
+18
View File
@@ -0,0 +1,18 @@
/// `getuid()`
#[inline]
pub fn getuid() -> u32 {
unsafe { libc::getuid() }
}
/// `getgid()`
#[inline]
pub fn getgid() -> u32 {
unsafe { libc::getgid() }
}
/// `getpid()`
#[inline]
pub fn getpid() -> u32 {
let pid: i32 = unsafe { libc::getpid() };
pid as u32
}
+9
View File
@@ -0,0 +1,9 @@
//! Process-associated operations.
#[cfg(not(target_os = "wasi"))] // WASI doesn't have get[gpu]id.
mod id;
mod sched;
#[cfg(not(target_os = "wasi"))]
pub use id::{getgid, getpid, getuid};
pub use sched::sched_yield;
+7
View File
@@ -0,0 +1,7 @@
use crate::zero_ok;
/// `sched_yield()`
#[inline]
pub fn sched_yield() {
zero_ok(unsafe { libc::sched_yield() }).unwrap()
}
+56
View File
@@ -0,0 +1,56 @@
use crate::zero_ok;
use std::mem::MaybeUninit;
pub use libc::{timespec, UTIME_NOW, UTIME_OMIT};
/// `clockid_t`
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(i32)]
pub enum ClockId {
/// `CLOCK_REALTIME`
Realtime = libc::CLOCK_REALTIME,
/// `CLOCK_MONOTONIC`
Monotonic = libc::CLOCK_MONOTONIC,
/// `CLOCK_PROCESS_CPUTIME_ID`
#[cfg(not(target_os = "netbsd"))]
ProcessCPUTime = libc::CLOCK_PROCESS_CPUTIME_ID,
/// `CLOCK_THREAD_CPUTIME_ID`
#[cfg(not(target_os = "netbsd"))]
ThreadCPUTime = libc::CLOCK_THREAD_CPUTIME_ID,
}
/// `clockid_t`
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u32)]
pub enum ClockId {
/// `CLOCK_REALTIME`
Realtime = libc::CLOCK_REALTIME,
/// `CLOCK_MONOTONIC`
Monotonic = libc::CLOCK_MONOTONIC,
/// `CLOCK_PROCESS_CPUTIME_ID`
ProcessCPUTime = libc::CLOCK_PROCESS_CPUTIME_ID,
/// `CLOCK_THREAD_CPUTIME_ID`
ThreadCPUTime = libc::CLOCK_THREAD_CPUTIME_ID,
}
/// `clock_getres(id)`
pub fn clock_getres(id: ClockId) -> timespec {
let mut timespec = MaybeUninit::<timespec>::uninit();
zero_ok(unsafe { libc::clock_getres(id as libc::clockid_t, timespec.as_mut_ptr()) }).unwrap();
unsafe { timespec.assume_init() }
}
/// `clock_gettime(id)`
pub fn clock_gettime(id: ClockId) -> timespec {
let mut timespec = MaybeUninit::<timespec>::uninit();
zero_ok(unsafe { libc::clock_gettime(id as libc::clockid_t, timespec.as_mut_ptr()) }).unwrap();
unsafe { timespec.assume_init() }
}
+7
View File
@@ -0,0 +1,7 @@
//! Time-related operations.
#[cfg(not(target_os = "wasi"))] // not implmented in libc for WASI yet
mod clock;
#[cfg(not(target_os = "wasi"))] // not implmented in libc for WASI yet
pub use clock::{clock_getres, clock_gettime, timespec, ClockId, UTIME_NOW, UTIME_OMIT};
+111
View File
@@ -0,0 +1,111 @@
// Implementation derived from `weak` in Rust's
// library/std/src/sys/unix/weak.rs at revision
// 108e90ca78f052c0c1c49c42a22c85620be19712.
//! Support for "weak linkage" to symbols on Unix
//!
//! Some I/O operations we do in libstd require newer versions of OSes but we
//! need to maintain binary compatibility with older releases for now. In order
//! to use the new functionality when available we use this module for
//! detection.
//!
//! One option to use here is weak linkage, but that is unfortunately only
//! really workable on Linux. Hence, use dlsym to get the symbol value at
//! runtime. This is also done for compatibility with older versions of glibc,
//! and to avoid creating dependencies on `GLIBC_PRIVATE` symbols. It assumes that
//! we've been dynamically linked to the library the symbol comes from, but that
//! is currently always the case for things like libpthread/libc.
//!
//! A long time ago this used weak linkage for the `__pthread_get_minstack`
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
//! dependency on libc6 (#23628).
// There are a variety of `#[cfg]`s controlling which targets are involved in
// each instance of `weak!` and `syscall!`. Rather than trying to unify all of
// that, we'll just allow that some unix targets don't use this module at all.
#![allow(dead_code, unused_macros)]
#![allow(clippy::doc_markdown)]
use std::{
ffi::CStr,
marker, mem,
sync::atomic::{AtomicUsize, Ordering},
};
macro_rules! weak {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
static $name: crate::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
crate::weak::Weak::new(concat!(stringify!($name), '\0'));
)
}
pub(crate) struct Weak<F> {
name: &'static str,
addr: AtomicUsize,
_marker: marker::PhantomData<F>,
}
impl<F> Weak<F> {
pub(crate) const fn new(name: &'static str) -> Self {
Self {
name,
addr: AtomicUsize::new(1),
_marker: marker::PhantomData,
}
}
pub(crate) fn get(&self) -> Option<F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
unsafe {
if self.addr.load(Ordering::SeqCst) == 1 {
self.addr.store(fetch(self.name), Ordering::SeqCst);
}
match self.addr.load(Ordering::SeqCst) {
0 => None,
addr => Some(mem::transmute_copy::<usize, F>(&addr)),
}
}
}
}
unsafe fn fetch(name: &str) -> usize {
let name = match CStr::from_bytes_with_nul(name.as_bytes()) {
Ok(cstr) => cstr,
Err(..) => return 0,
};
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
}
#[cfg(not(target_os = "linux"))]
macro_rules! syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
unsafe fn $name($($arg_name: $t),*) -> $ret {
use super::os;
weak! { fn $name($($t),*) -> $ret }
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
os::set_errno(libc::ENOSYS);
-1
}
}
)
}
#[cfg(target_os = "linux")]
macro_rules! syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
unsafe fn $name($($arg_name:$t),*) -> $ret {
// This looks like a hack, but concat_idents only accepts idents
// (not paths).
use libc::*;
syscall(
concat_idents!(SYS_, $name),
$($arg_name as c_long),*
) as $ret
}
)
}