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