├── .builds ├── freebsd.yml └── openbsd.yml ├── .github └── workflows │ ├── linux.yml │ ├── macos.yml │ └── rustfmt.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── design └── 0001-goals.md ├── rustfmt.toml ├── uapi-proc ├── Cargo.toml └── src │ └── lib.rs ├── uapi-testutils ├── Cargo.toml └── src │ ├── lib.rs │ └── strace.rs └── uapi ├── Cargo.toml ├── build.rs ├── coverage ├── for_coverage ├── src ├── black_box.c ├── c │ ├── linux.rs │ └── mod.rs ├── dir │ └── mod.rs ├── docs.rs ├── errno │ └── mod.rs ├── fcntl │ ├── linux.rs │ └── mod.rs ├── fd.rs ├── file │ ├── linux.rs │ └── mod.rs ├── ioctl │ ├── bsd.rs │ ├── linux.rs │ └── mod.rs ├── lib.rs ├── macros.rs ├── mount │ ├── linux.rs │ └── mod.rs ├── other │ ├── linux.rs │ └── mod.rs ├── pod.rs ├── poll │ ├── linux.rs │ └── mod.rs ├── process │ ├── linux.rs │ └── mod.rs ├── result.rs ├── sched │ ├── linux.rs │ └── mod.rs ├── signal │ ├── linux.rs │ └── mod.rs ├── socket │ ├── cmsg.rs │ ├── linux │ │ ├── mod.rs │ │ └── netlink.rs │ ├── mod.rs │ └── sockopt.rs ├── timer │ ├── linux.rs │ └── mod.rs ├── uninit.rs ├── ustr │ ├── as_ustr.rs │ ├── bstr.rs │ ├── bytes.rs │ ├── eq.rs │ ├── into.rs │ ├── mod.rs │ ├── read.rs │ ├── ustr.rs │ ├── ustring.rs │ └── ustrptr.rs └── util.rs └── tests ├── chdir.rs ├── chroot.rs ├── close_range.rs ├── dupfd.rs ├── exec1.rs ├── exec2.rs ├── fcntl_lck.rs ├── main ├── dir │ └── mod.rs ├── errno │ └── mod.rs ├── fcntl │ ├── linux.rs │ └── mod.rs ├── fd.rs ├── file │ ├── linux.rs │ └── mod.rs ├── ioctl │ ├── linux.rs │ └── mod.rs ├── main.rs ├── mount │ ├── linux.rs │ └── mod.rs ├── other │ ├── linux.rs │ └── mod.rs ├── pod.rs ├── poll │ ├── linux.rs │ └── mod.rs ├── process │ ├── linux.rs │ └── mod.rs ├── result.rs ├── sched │ ├── linux.rs │ └── mod.rs ├── signal │ └── mod.rs ├── socket │ ├── cmsg.rs │ ├── linux.rs │ ├── mod.rs │ └── sockopt.rs ├── timer │ ├── linux.rs │ └── mod.rs ├── ustr │ └── mod.rs └── util.rs ├── mknod_mode.rs ├── mknodat_mode.rs ├── open_mode.rs ├── openat2_mode.rs ├── openat_mode.rs ├── pivot_root.rs ├── process1.rs ├── process2.rs ├── process3.rs ├── process4.rs ├── process5.rs ├── process6.rs ├── sched.rs ├── sched2.rs ├── sched3.rs ├── sched4.rs ├── sethostname.rs ├── setrlimit.rs ├── setuid1.rs ├── setuid2.rs ├── setuid3.rs ├── signal.rs ├── signalfd.rs └── unshare.rs /.builds/freebsd.yml: -------------------------------------------------------------------------------- 1 | image: freebsd/14.x 2 | sources: 3 | - https://github.com/mahkoh/uapi 4 | tasks: 5 | - install: | 6 | curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable 7 | - build: | 8 | cd uapi 9 | sudo -E $HOME/.cargo/bin/cargo build --verbose 10 | - test: | 11 | cd uapi 12 | export RUST_BACKTRACE=1 13 | sudo -E $HOME/.cargo/bin/cargo test --verbose 14 | -------------------------------------------------------------------------------- /.builds/openbsd.yml: -------------------------------------------------------------------------------- 1 | image: openbsd/7.3 2 | sources: 3 | - https://github.com/mahkoh/uapi 4 | tasks: 5 | - install: | 6 | doas pkg_add lang/rust 7 | - build: | 8 | cd uapi 9 | doas cargo build --verbose 10 | - test: | 11 | cd uapi 12 | export RUST_BACKTRACE=1 13 | doas cargo test --verbose 14 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | glibc-2_31: 14 | runs-on: ubuntu-20.04 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Show env 18 | run: | 19 | uname -a 20 | ldd --version 21 | - name: Install 22 | run: | 23 | curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable 24 | echo "$HOME/.cargo/bin" >> $GITHUB_PATH 25 | - name: Build 26 | run: sudo -E $HOME/.cargo/bin/cargo build --verbose 27 | - name: Run tests 28 | run: sudo -E $HOME/.cargo/bin/cargo test --verbose 29 | latest-glibc: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | - name: Show env 34 | run: | 35 | uname -a 36 | ldd --version 37 | - name: Install 38 | run: | 39 | curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable 40 | echo "$HOME/.cargo/bin" >> $GITHUB_PATH 41 | - name: Build 42 | run: sudo -E $HOME/.cargo/bin/cargo build --verbose 43 | - name: Run tests 44 | run: sudo -E $HOME/.cargo/bin/cargo test --verbose 45 | latest-musl: 46 | runs-on: ubuntu-latest 47 | steps: 48 | - uses: actions/checkout@v2 49 | - name: Show env 50 | run: | 51 | uname -a 52 | ldd --version 53 | - name: Install 54 | run: | 55 | curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable 56 | . $HOME/.cargo/env 57 | rustup target add x86_64-unknown-linux-musl 58 | sudo apt-get install musl musl-tools 59 | echo "$HOME/.cargo/bin" >> $GITHUB_PATH 60 | - name: Build 61 | run: | 62 | export RUST_BACKTRACE=1 63 | sudo -E $HOME/.cargo/bin/cargo build --verbose --target x86_64-unknown-linux-musl 64 | - name: Run tests 65 | run: sudo -E $HOME/.cargo/bin/cargo test --verbose --target x86_64-unknown-linux-musl 66 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macos 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | latest: 14 | runs-on: macos-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Show env 18 | run: | 19 | uname -a 20 | - name: Install 21 | run: | 22 | curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable 23 | echo "$HOME/.cargo/bin" >> $GITHUB_PATH 24 | - name: Build 25 | run: sudo -E $HOME/.cargo/bin/cargo build --verbose 26 | - name: Run tests 27 | run: | 28 | export RUST_BACKTRACE=1 29 | sudo -E $HOME/.cargo/bin/cargo test --verbose 30 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yml: -------------------------------------------------------------------------------- 1 | name: rustfmt 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | rustfmt: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Show env 18 | run: | 19 | uname -a 20 | ldd --version 21 | - name: Install 22 | run: | 23 | curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly 24 | rustup toolchain install nightly --allow-downgrade -c rustfmt 25 | echo "$HOME/.cargo/bin" >> $GITHUB_PATH 26 | - name: Check 27 | run: cargo +nightly fmt -- --check 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | .* 4 | !.gitignore 5 | !.github 6 | !.builds 7 | /coverage 8 | strace 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.2.1 4 | 5 | ### New features 6 | 7 | - Restore as_mut_bytes function 8 | - Add openat2 9 | - Add scheduling functions 10 | 11 | ## 0.2.0 12 | 13 | ### Breaking changes 14 | 15 | - Move to MaybeUninit-based IO 16 | 17 | ### New features 18 | 19 | - New functions for parsing and constructing netlink messages on linux 20 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "bitflags" 16 | version = "2.4.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 19 | 20 | [[package]] 21 | name = "cc" 22 | version = "1.0.89" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" 25 | 26 | [[package]] 27 | name = "cfg-if" 28 | version = "1.0.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 31 | 32 | [[package]] 33 | name = "errno" 34 | version = "0.3.8" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 37 | dependencies = [ 38 | "libc", 39 | "windows-sys", 40 | ] 41 | 42 | [[package]] 43 | name = "fastrand" 44 | version = "2.0.1" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" 47 | 48 | [[package]] 49 | name = "lazy_static" 50 | version = "1.4.0" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 53 | 54 | [[package]] 55 | name = "libc" 56 | version = "0.2.153" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 59 | 60 | [[package]] 61 | name = "linux-raw-sys" 62 | version = "0.4.13" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 65 | 66 | [[package]] 67 | name = "memchr" 68 | version = "2.7.1" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 71 | 72 | [[package]] 73 | name = "proc-macro2" 74 | version = "1.0.78" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 77 | dependencies = [ 78 | "unicode-ident", 79 | ] 80 | 81 | [[package]] 82 | name = "quote" 83 | version = "1.0.35" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 86 | dependencies = [ 87 | "proc-macro2", 88 | ] 89 | 90 | [[package]] 91 | name = "regex" 92 | version = "1.10.3" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" 95 | dependencies = [ 96 | "aho-corasick", 97 | "memchr", 98 | "regex-automata", 99 | "regex-syntax", 100 | ] 101 | 102 | [[package]] 103 | name = "regex-automata" 104 | version = "0.4.6" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 107 | dependencies = [ 108 | "aho-corasick", 109 | "memchr", 110 | "regex-syntax", 111 | ] 112 | 113 | [[package]] 114 | name = "regex-syntax" 115 | version = "0.8.2" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 118 | 119 | [[package]] 120 | name = "rustix" 121 | version = "0.38.31" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" 124 | dependencies = [ 125 | "bitflags", 126 | "errno", 127 | "libc", 128 | "linux-raw-sys", 129 | "windows-sys", 130 | ] 131 | 132 | [[package]] 133 | name = "syn" 134 | version = "2.0.52" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" 137 | dependencies = [ 138 | "proc-macro2", 139 | "quote", 140 | "unicode-ident", 141 | ] 142 | 143 | [[package]] 144 | name = "tempfile" 145 | version = "3.10.1" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 148 | dependencies = [ 149 | "cfg-if", 150 | "fastrand", 151 | "rustix", 152 | "windows-sys", 153 | ] 154 | 155 | [[package]] 156 | name = "testutils" 157 | version = "0.1.0" 158 | dependencies = [ 159 | "cfg-if", 160 | "tempfile", 161 | "uapi", 162 | "uapi-proc", 163 | ] 164 | 165 | [[package]] 166 | name = "uapi" 167 | version = "0.2.13" 168 | dependencies = [ 169 | "cc", 170 | "cfg-if", 171 | "libc", 172 | "tempfile", 173 | "testutils", 174 | "uapi-proc", 175 | ] 176 | 177 | [[package]] 178 | name = "uapi-proc" 179 | version = "0.0.5" 180 | dependencies = [ 181 | "lazy_static", 182 | "libc", 183 | "proc-macro2", 184 | "quote", 185 | "regex", 186 | "syn", 187 | ] 188 | 189 | [[package]] 190 | name = "unicode-ident" 191 | version = "1.0.12" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 194 | 195 | [[package]] 196 | name = "windows-sys" 197 | version = "0.52.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 200 | dependencies = [ 201 | "windows-targets", 202 | ] 203 | 204 | [[package]] 205 | name = "windows-targets" 206 | version = "0.52.4" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 209 | dependencies = [ 210 | "windows_aarch64_gnullvm", 211 | "windows_aarch64_msvc", 212 | "windows_i686_gnu", 213 | "windows_i686_msvc", 214 | "windows_x86_64_gnu", 215 | "windows_x86_64_gnullvm", 216 | "windows_x86_64_msvc", 217 | ] 218 | 219 | [[package]] 220 | name = "windows_aarch64_gnullvm" 221 | version = "0.52.4" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" 224 | 225 | [[package]] 226 | name = "windows_aarch64_msvc" 227 | version = "0.52.4" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" 230 | 231 | [[package]] 232 | name = "windows_i686_gnu" 233 | version = "0.52.4" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" 236 | 237 | [[package]] 238 | name = "windows_i686_msvc" 239 | version = "0.52.4" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" 242 | 243 | [[package]] 244 | name = "windows_x86_64_gnu" 245 | version = "0.52.4" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" 248 | 249 | [[package]] 250 | name = "windows_x86_64_gnullvm" 251 | version = "0.52.4" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" 254 | 255 | [[package]] 256 | name = "windows_x86_64_msvc" 257 | version = "0.52.4" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 260 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["uapi", "uapi-proc", "uapi-testutils"] 3 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) The uapi developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unix API 2 | 3 | [![crates.io](https://img.shields.io/crates/v/uapi.svg)](http://crates.io/crates/uapi) 4 | [![docs.rs](https://docs.rs/uapi/badge.svg)](http://docs.rs/uapi) 5 | 6 | This crate contains safe wrappers around Unix APIs. 7 | 8 | ## Supported Targets 9 | 10 | A target is supported if and only if the crate is tested against it via CI. 11 | 12 | The following targets are supported: 13 | 14 | - x86_64-unknown-linux-gnu (glibc >= 2.23) 15 | - x86_64-unknown-linux-musl (musl >= 1.1.19) 16 | - x86_64-unknown-freebsd (12) 17 | - x86_64-unknown-openbsd (6.7) 18 | - x86_64-apple-darwin (10.15) 19 | 20 | This crate contains little architecture-specific code. Therefore, other 21 | architectures (arm, aarch64, etc.) will probably also work. 22 | 23 | ## Future changes 24 | 25 | This crate fully supports reading into uninitialized buffers but the API will 26 | most likely change when the same functionality becomes stable in libstd. 27 | 28 | ## Comparison with other crates 29 | 30 | ### libc 31 | 32 | This crate builds on the libc crate and uses its declarations of the raw OS APIs 33 | if possible. This crate considers itself to be the next step up from libc: It 34 | safely wraps the raw OS functions but does little beyond that. Integer 35 | parameters are still integer parameters, functions operating on sockets accept 36 | the socket file descriptor as a raw integer, there is no socket wrapper type, 37 | etc. 38 | 39 | At the same time, this crate provides the necessary tools to make it as easy to 40 | use from Rust as the raw APIs are to use from C. For example, all of the 41 | following just work: 42 | 43 | ```rust 44 | open("./file", c::O_RDWR, 0); 45 | open(b"./file", c::O_RDWR, 0); 46 | open(CStr::from_ptr(p), c::O_RDWR, 0); 47 | open(Path::new("./file"), c::O_RDWR, 0); 48 | ``` 49 | 50 | See the crate documentation for more details. 51 | 52 | ### nix 53 | 54 | - **nix** uses a nested module structure. **uapi** exports all APIs in the crate 55 | root. 56 | - **nix** I/O works on `[u8]`. **uapi** I/O works on `[MaybeUninit]`. 57 | - **nix** uses enums and bitflags for integer/flag parameters. **uapi** uses 58 | plain integers. 59 | - **nix** uses methods declared on wrapper types to expose APIs. **uapi** uses 60 | free functions unless doing so would be unsafe. 61 | - **nix** uses enums for the values produced and consumed by certain generic OS 62 | APIs (e.g. control messages.) **uapi** uses generic functions that do not 63 | restrict the types that can be used. 64 | 65 | ## License 66 | 67 | This project is licensed under either of 68 | 69 | - Apache License, Version 2.0 70 | - MIT License 71 | 72 | at your option. 73 | -------------------------------------------------------------------------------- /design/0001-goals.md: -------------------------------------------------------------------------------- 1 | # Goals 2 | 3 | ## Status 4 | 5 | 2020-05-28 - Active 6 | 7 | ## Context 8 | 9 | The goals of the crate should be clearly documented. 10 | 11 | ## Resolution 12 | 13 | 1. The crate should provide wrappers around OS APIs of UNIX-like operating 14 | systems. 15 | 2. The wrappers should be safe if possible. 16 | 3. No additional documentation should be required for someone who knows the 17 | upstream API to understand the wrapper. 18 | 4. It should not be necessary to modify the wrapper if upstream extends the API. 19 | 5. The wrappers should be as easy to use from Rust as the wrapped APIs are to 20 | use from C. This includes the verbosity of the code. 21 | 6. It should be possible to use the wrappers with 0 overhead. 22 | 7. Resources should be managed via RAII. 23 | 8. Differences between operating systems should not be unified. 24 | 25 | ## Consequences 26 | 27 | By 3, all wrappers will be available in the crate root. 28 | 29 | By 3 and 4, wrapper types will be avoided if possible. By 2, exceptions will be 30 | made if the underlying data structures cannot be used safely. 31 | 32 | By 4, there will be no enums in the API. Flag parameters will have the same type 33 | as the underlying parameter. 34 | 35 | By 5, wrappers of APIs that accept strings accept all of Rust's string types 36 | such as `str`, `[u8]`, `CStr`, `Path`, `OsStr`, etc. By 6, no allocations must 37 | be performed for `CStr` arguments. 38 | 39 | By 5, custom string types will be added to make working with non-UTF-8 and 40 | C-style strings easier. 41 | 42 | By 5, traits will be added to make conversions between binary data and structs 43 | easier. 44 | 45 | By 7, APIs that return new file descriptors return a wrapper that closes the 46 | file descriptor upon drop. 47 | 48 | By 8, APIs that are only available on some operating systems will not be 49 | emulated on other operating systems. For example, macOS has a time API that is 50 | different from the POSIX `clock_*` family of functions. 51 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | imports_granularity = "Crate" 2 | max_width = 90 3 | empty_item_single_line = false 4 | -------------------------------------------------------------------------------- /uapi-proc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uapi-proc" 3 | version = "0.0.5" 4 | authors = ["Julian Orth "] 5 | edition = "2018" 6 | description = "Internal macros for the uapi crate. DO NOT USE!" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/mahkoh/uapi" 9 | 10 | [lib] 11 | proc-macro = true 12 | 13 | [dependencies] 14 | libc = "0.2" 15 | quote = "1" 16 | proc-macro2 = "1" 17 | syn = "2" 18 | regex = { version = "1", default_features = false, features = ["std"] } 19 | lazy_static = "1" 20 | -------------------------------------------------------------------------------- /uapi-testutils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "testutils" 3 | version = "0.1.0" 4 | authors = ["Julian Orth "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | uapi = { path = "../uapi" } 9 | tempfile = "3.0.5" 10 | proc = { package = "uapi-proc", path = "../uapi-proc" } 11 | cfg-if = "1" 12 | -------------------------------------------------------------------------------- /uapi-testutils/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | 3 | use std::{ 4 | borrow::Cow, 5 | fmt, 6 | fmt::{Debug, Display, Formatter}, 7 | fs::File, 8 | io::Read, 9 | panic::AssertUnwindSafe, 10 | process::exit, 11 | }; 12 | use tempfile::TempDir; 13 | use uapi::*; 14 | 15 | cfg_if::cfg_if! { 16 | if #[cfg(any(target_os = "linux", target_os = "android"))] { 17 | mod strace; 18 | pub use strace::strace; 19 | } 20 | } 21 | 22 | #[derive(Debug)] 23 | pub struct Tempdir { 24 | dir: TempDir, 25 | } 26 | 27 | impl Tempdir { 28 | pub fn new() -> Tempdir { 29 | Tempdir { 30 | dir: TempDir::new().unwrap(), 31 | } 32 | } 33 | 34 | pub fn bstr(&self) -> &Bstr { 35 | Bstr::from_path(self.dir.path()) 36 | } 37 | } 38 | 39 | impl Default for Tempdir { 40 | fn default() -> Self { 41 | Self::new() 42 | } 43 | } 44 | 45 | impl<'a> IntoUstr<'a> for &'a Tempdir { 46 | fn into_ustr(self) -> Cow<'a, Ustr> { 47 | self.bstr().into_ustr() 48 | } 49 | } 50 | 51 | impl Display for Tempdir { 52 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 53 | self.bstr().display().fmt(f) 54 | } 55 | } 56 | 57 | pub fn create_file<'a>(f: impl IntoUstr<'a>) { 58 | open(f, c::O_CREAT | c::O_RDONLY, 0).unwrap(); 59 | } 60 | 61 | pub struct Defer(Option); 62 | 63 | impl Defer { 64 | pub fn new(f: F) -> Defer { 65 | Defer(Some(f)) 66 | } 67 | } 68 | 69 | impl Drop for Defer { 70 | fn drop(&mut self) { 71 | self.0.take().unwrap()() 72 | } 73 | } 74 | 75 | #[macro_export] 76 | macro_rules! defer { 77 | ($f:expr) => { 78 | let _x = $crate::Defer::new($f); 79 | }; 80 | } 81 | 82 | pub fn in_fork(f: F) { 83 | match std::panic::catch_unwind(AssertUnwindSafe(f)) { 84 | Ok(_) => exit(0), 85 | Err(_) => exit(1), 86 | } 87 | } 88 | 89 | pub fn read_file(mut f: File) -> String { 90 | let mut s = String::new(); 91 | f.read_to_string(&mut s).unwrap(); 92 | s 93 | } 94 | -------------------------------------------------------------------------------- /uapi-testutils/src/strace.rs: -------------------------------------------------------------------------------- 1 | use std::panic::AssertUnwindSafe; 2 | use uapi::*; 3 | 4 | pub fn strace T>(trace: bool, f: F) -> T { 5 | if !trace { 6 | return f(); 7 | } 8 | 9 | let id = gettid(); 10 | 11 | let start_efd = eventfd(0, 0).unwrap(); 12 | let start_efd_copy = start_efd.borrow(); 13 | let stop_efd = eventfd(0, 0).unwrap(); 14 | let stop_efd_copy = stop_efd.borrow(); 15 | 16 | let thread = std::thread::spawn(move || { 17 | let mut command = std::process::Command::new("strace") 18 | // .arg("-f") 19 | .arg("-v") 20 | .arg("-s") 21 | .arg("999") 22 | // .arg("-X").arg("raw") 23 | .arg("-p") 24 | .arg(id.to_string()) 25 | .stderr(std::process::Stdio::piped()) 26 | .spawn() 27 | .unwrap(); 28 | let mut notified_parent = false; 29 | let stderr = OwnedFd::from(command.stderr.take().unwrap()); 30 | fcntl_setfl(*stderr, fcntl_getfl(*stderr).unwrap() | c::O_NONBLOCK).unwrap(); 31 | let mut buf = [0; 1024]; 32 | let mut strace = 33 | open("strace", c::O_WRONLY | c::O_TRUNC | c::O_CREAT, 0o777).unwrap(); 34 | let mut pipe = || loop { 35 | let res = read(*stderr, &mut buf[..]); 36 | match res { 37 | Ok([]) => break, 38 | Err(Errno(c::EAGAIN)) => break, 39 | Ok(buf) => { 40 | // std::io::Write::write_all(&mut Fd::new(2), &buf[..n]).unwrap(); 41 | std::io::Write::write_all(&mut strace, buf).unwrap(); 42 | if !notified_parent { 43 | notified_parent = true; 44 | eventfd_write(*start_efd_copy, 1).unwrap(); 45 | } 46 | } 47 | e => { 48 | e.unwrap(); 49 | } 50 | }; 51 | }; 52 | loop { 53 | let mut fds = [ 54 | c::pollfd { 55 | fd: *stderr, 56 | events: c::POLLIN | c::POLLHUP, 57 | revents: 0, 58 | }, 59 | c::pollfd { 60 | fd: *stop_efd_copy, 61 | events: c::POLLIN | c::POLLHUP, 62 | revents: 0, 63 | }, 64 | ]; 65 | poll(&mut fds, 0).unwrap(); 66 | if fds[0].revents & c::POLLHUP != 0 || fds[1].revents != 0 { 67 | break; 68 | } 69 | if fds[0].revents & c::POLLIN != 0 { 70 | pipe(); 71 | } 72 | } 73 | command.kill().unwrap(); 74 | pipe(); 75 | }); 76 | 77 | eventfd_read(*start_efd).unwrap(); 78 | 79 | let res = std::panic::catch_unwind(AssertUnwindSafe(f)); 80 | 81 | eventfd_write(*stop_efd, 1).unwrap(); 82 | thread.join().unwrap(); 83 | 84 | match res { 85 | Ok(r) => r, 86 | Err(p) => std::panic::resume_unwind(p), 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /uapi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uapi" 3 | version = "0.2.13" 4 | authors = ["Julian Orth "] 5 | edition = "2018" 6 | description = "Wrappers for OS APIs on UNIX-like platform" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/mahkoh/uapi" 9 | keywords = ["unix", "linux", "macos", "osx", "bsd"] 10 | categories = ["os::unix-apis"] 11 | readme = "../README.md" 12 | 13 | [dependencies] 14 | libc = "0.2" 15 | cfg-if = "1" 16 | proc = { package = "uapi-proc", version = "0.0.5", path = "../uapi-proc" } 17 | 18 | [dev-dependencies] 19 | tempfile = "3.0.5" 20 | testutils = { path = "../uapi-testutils" } 21 | 22 | [build-dependencies] 23 | cc = "1" 24 | 25 | [package.metadata.docs.rs] 26 | targets = [ 27 | "x86_64-unknown-linux-gnu", 28 | "x86_64-unknown-linux-musl", 29 | "x86_64-unknown-freebsd", 30 | "x86_64-apple-darwin", 31 | ] 32 | -------------------------------------------------------------------------------- /uapi/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | cc::Build::new() 3 | .file("src/black_box.c") 4 | .compile("libblack_box.a"); 5 | } 6 | -------------------------------------------------------------------------------- /uapi/coverage: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./for_coverage sudo -E cargo +nightly test 4 | sudo grcov ../target/debug/ -s . -t lcov --llvm --branch --ignore-not-existing -o ../target/debug/lcov.info 5 | sudo genhtml -o ../coverage/ --show-details --highlight --ignore-errors source --legend ../target/debug/lcov.info 6 | sudo -E cargo clean -------------------------------------------------------------------------------- /uapi/for_coverage: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export CARGO_INCREMENTAL=0 4 | export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" 5 | export RUSTDOCFLAGS="-Cpanic=abort" 6 | 7 | exec "$@" 8 | -------------------------------------------------------------------------------- /uapi/src/black_box.c: -------------------------------------------------------------------------------- 1 | void *uapi_black_box(void *ptr) { 2 | asm volatile("" : : "g"(&ptr) : "memory"); 3 | return ptr; 4 | } -------------------------------------------------------------------------------- /uapi/src/c/mod.rs: -------------------------------------------------------------------------------- 1 | //! Re-export of the libc crate with missing items added 2 | //! 3 | //! Items should be upstreamed if possible. 4 | 5 | pub use libc::*; 6 | 7 | use cfg_if::cfg_if; 8 | 9 | #[cfg(target_os = "dragonfly")] 10 | extern "C" { 11 | pub fn __errno_location() -> *mut c_int; 12 | } 13 | 14 | cfg_if! { 15 | if #[cfg(target_os = "linux")] { 16 | mod linux; 17 | pub use linux::{ 18 | fsconfig, fsmount, fsopen, fspick, ifinfomsg, move_mount, open_how, open_tree, 19 | openat2, renameat2, sched_attr, sched_getattr, sched_getparam, sched_getscheduler, 20 | sched_setattr, sched_setparam, sched_setscheduler, sockaddr_nl, uinput_setup, 21 | SYS_clone3, SYS_close_range, SYS_epoll_pwait2, SYS_faccessat2, SYS_fsconfig, 22 | SYS_fsmount, SYS_fsopen, SYS_fspick, SYS_futex_waitv, SYS_io_uring_enter, 23 | SYS_io_uring_register, SYS_io_uring_setup, SYS_landlock_add_rule, 24 | SYS_landlock_create_ruleset, SYS_landlock_restrict_self, SYS_memfd_secret, 25 | SYS_mount_setattr, SYS_move_mount, SYS_open_tree, SYS_openat2, SYS_pidfd_getfd, 26 | SYS_pidfd_open, SYS_pidfd_send_signal, SYS_process_madvise, SYS_process_mrelease, 27 | SYS_quotactl_fd, SYS_set_mempolicy_home_node, AT_RECURSIVE, CLOSE_RANGE_CLOEXEC, 28 | CLOSE_RANGE_UNSHARE, FSCONFIG_CMD_CREATE, FSCONFIG_CMD_RECONFIGURE, 29 | FSCONFIG_SET_BINARY, FSCONFIG_SET_FD, FSCONFIG_SET_FLAG, FSCONFIG_SET_PATH, 30 | FSCONFIG_SET_PATH_EMPTY, FSCONFIG_SET_STRING, FSMOUNT_CLOEXEC, FSOPEN_CLOEXEC, 31 | FSPICK_CLOEXEC, FSPICK_EMPTY_PATH, FSPICK_NO_AUTOMOUNT, FSPICK_SYMLINK_NOFOLLOW, 32 | MOUNT_ATTR_NOATIME, MOUNT_ATTR_NODEV, MOUNT_ATTR_NODIRATIME, MOUNT_ATTR_NOEXEC, 33 | MOUNT_ATTR_NOSUID, MOUNT_ATTR_RDONLY, MOUNT_ATTR_RELATIME, MOUNT_ATTR_STRICTATIME, 34 | MOUNT_ATTR__ATIME, MOVE_MOUNT_F_AUTOMOUNTS, MOVE_MOUNT_F_EMPTY_PATH, 35 | MOVE_MOUNT_F_SYMLINKS, MOVE_MOUNT_T_AUTOMOUNTS, MOVE_MOUNT_T_EMPTY_PATH, 36 | MOVE_MOUNT_T_SYMLINKS, MOVE_MOUNT__MASK, OPEN_TREE_CLOEXEC, OPEN_TREE_CLONE, 37 | RESOLVE_BENEATH, RESOLVE_IN_ROOT, RESOLVE_NO_MAGICLINKS, RESOLVE_NO_SYMLINKS, 38 | RESOLVE_NO_XDEV, RWF_APPEND, RWF_DSYNC, RWF_HIPRI, RWF_NOWAIT, RWF_SYNC, 39 | SCHED_FLAG_ALL, SCHED_FLAG_DL_OVERRUN, SCHED_FLAG_KEEP_ALL, SCHED_FLAG_KEEP_PARAMS, 40 | SCHED_FLAG_KEEP_POLICY, SCHED_FLAG_RECLAIM, SCHED_FLAG_RESET_ON_FORK, 41 | SCHED_FLAG_UTIL_CLAMP, SCHED_FLAG_UTIL_CLAMP_MAX, SCHED_FLAG_UTIL_CLAMP_MIN, 42 | UINPUT_IOCTL_BASE, UINPUT_MAX_NAME_SIZE, PIDFD_NONBLOCK, 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /uapi/src/dir/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{ 3 | ffi::CStr, 4 | mem, 5 | ops::{Deref, DerefMut}, 6 | }; 7 | 8 | #[man(opendir(3))] 9 | pub fn opendir<'a>(name: impl IntoUstr<'a>) -> Result { 10 | let name = name.into_ustr(); 11 | let dir = unsafe { c::opendir(name.as_ptr()) }; 12 | if dir.is_null() { 13 | Err(Errno::default()) 14 | } else { 15 | Ok(Dir { dir }) 16 | } 17 | } 18 | 19 | #[man(fdopendir(3))] 20 | pub fn fdopendir(fd: OwnedFd) -> Result { 21 | let fd = fd.unwrap(); 22 | let dir = unsafe { c::fdopendir(fd) }; 23 | if dir.is_null() { 24 | Err(Errno::default()) 25 | } else { 26 | Ok(Dir { dir }) 27 | } 28 | } 29 | 30 | #[man(readdir(3))] 31 | #[allow(clippy::should_implement_trait)] // https://github.com/rust-lang/rust-clippy/issues/5004 32 | pub fn readdir<'a>(dir: &'a mut c::DIR) -> Option>> { 33 | set_errno(0); 34 | let ent = unsafe { c::readdir(dir) }; 35 | if ent.is_null() { 36 | if get_errno() == 0 { 37 | None 38 | } else { 39 | Some(Err(Errno::default())) 40 | } 41 | } else { 42 | unsafe { Some(Ok(Dirent { raw: &*ent })) } 43 | } 44 | } 45 | 46 | #[man(rewinddir(3))] 47 | pub fn rewinddir(dir: &mut c::DIR) { 48 | unsafe { c::rewinddir(dir) } 49 | } 50 | 51 | #[man(seekdir(3))] 52 | pub fn seekdir(dir: &mut c::DIR, loc: c::c_long) { 53 | unsafe { c::seekdir(dir, loc) } 54 | } 55 | 56 | #[man(telldir(3))] 57 | pub fn telldir(dir: &mut c::DIR) -> c::c_long { 58 | unsafe { c::telldir(dir) } 59 | } 60 | 61 | #[man(dirfd(3))] 62 | pub fn dirfd(dir: &mut c::DIR) -> c::c_int { 63 | unsafe { c::dirfd(dir) } 64 | } 65 | 66 | /// Wrapper for `*mut libc::DIR` 67 | /// 68 | /// Upon `Drop`, the `*mut libc::DIR` will be closed. 69 | pub struct Dir { 70 | dir: *mut c::DIR, 71 | } 72 | 73 | #[allow(clippy::needless_lifetimes)] 74 | impl Dir { 75 | /// Unwraps the underlying `*mut libc::DIR` 76 | pub fn unwrap(self) -> *mut c::DIR { 77 | let res = self.dir; 78 | mem::forget(self); 79 | res 80 | } 81 | 82 | /// Wraps the provided `*mut libc::DIR` 83 | /// 84 | /// # Safety 85 | /// 86 | /// The pointer must be valid and `Self` acquires sole ownership 87 | pub unsafe fn from_ptr(dir: *mut c::DIR) -> Self { 88 | Self { dir } 89 | } 90 | } 91 | 92 | impl Drop for Dir { 93 | fn drop(&mut self) { 94 | unsafe { 95 | c::closedir(self.dir); 96 | } 97 | } 98 | } 99 | 100 | impl Deref for Dir { 101 | type Target = c::DIR; 102 | 103 | fn deref(&self) -> &Self::Target { 104 | unsafe { &*self.dir } 105 | } 106 | } 107 | 108 | impl DerefMut for Dir { 109 | fn deref_mut(&mut self) -> &mut Self::Target { 110 | unsafe { &mut *self.dir } 111 | } 112 | } 113 | 114 | /// Wrapper for `*const libc::dirent` 115 | pub struct Dirent<'a> { 116 | raw: &'a c::dirent, 117 | } 118 | 119 | impl<'a> Dirent<'a> { 120 | /// Returns `dirent.d_name` as a `CStr` 121 | pub fn name(&self) -> &CStr { 122 | unsafe { CStr::from_ptr(self.raw.d_name.as_ptr()) } 123 | } 124 | } 125 | 126 | impl<'a> Deref for Dirent<'a> { 127 | type Target = c::dirent; 128 | 129 | fn deref(&self) -> &Self::Target { 130 | self.raw 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /uapi/src/docs.rs: -------------------------------------------------------------------------------- 1 | //! Module containing the crate documentation 2 | //! 3 | //! This crate provides safe wrappers for Unix APIs. 4 | //! 5 | //! All wrappers are provided in the crate root. There are no wrapper types unless the raw 6 | //! types cannot be used safely and as noted below. 7 | //! 8 | //! # File Descriptors 9 | //! 10 | //! This crate implements lifecycle management of file descriptors with two types: 11 | //! 12 | //! - `OwnedFd` - An owned file descriptor 13 | //! - `Fd` - Any kind of file descriptor 14 | //! 15 | //! `OwnedFd` is distinct from `Fd` in that it assumes ownership of the contained file 16 | //! descriptor. It implements `Drop` and can be converted from and to many libstd types. 17 | //! 18 | //! Both `OwnedFd` and `Fd` implement `Read` and `Write`, and `Deref` to `libc::c_int`. 19 | //! 20 | //! APIs that return owned file descriptors return `OwnedFd`. APIs that assume ownership 21 | //! take `OwnedFd`. 22 | //! 23 | //! It's important to recognize that `OwnedFd` is not about safety but about convenience. 24 | //! An `OwnedFd` can be created from any integer by calling `OwnedFd::new`. An `OwnedFd` 25 | //! can be unwrapped by calling `OwnedFd::unwrap`. 26 | //! 27 | //! # Strings 28 | //! 29 | //! This crate contains 3 string types: 30 | //! 31 | //! - `&Bstr` - Any sequence of bytes 32 | //! - `&Ustr` - Any sequence of bytes with a terminating nul byte 33 | //! - `Ustring` - Owned version of `&Ustr` 34 | //! 35 | //! And several associated traits: 36 | //! 37 | //! - `AsUstr` - Cheap conversion into `&Ustr` 38 | //! - `Bytes` - Types with a binary representation 39 | //! - `IntoUstr` - Types that can be converted into `Cov<'a, Ustr>` 40 | //! 41 | //! `&Bstr` is a simple wrapper around `&[u8]` which supports conversions from and to many 42 | //! libstd string types: 43 | //! 44 | //! - `&[u8]` 45 | //! - `&str` 46 | //! - `&Path` 47 | //! - `&OsStr` 48 | //! - `&CStr` 49 | //! 50 | //! `&Bstr` supports `Debug` and `Display`. The implementations are the same as the ones 51 | //! used for `&Path`. To access the `Display` implementation, call `Bstr::display()`. 52 | //! `&Bstr` supports `PartialEq` for many libstd types, `&Ustr`, and `Ustring`. 53 | //! 54 | //! `&Ustr` is like `&Bstr` but guarantees that there is a nul byte after the last element 55 | //! of the sequence. Unlike `&CStr`, `&Ustr` can contain inner nul bytes. `&Ustr` derefs 56 | //! to `&Bstr`. Like `&Bstr`, `&Ustr` can be converted from and to many libstd types. 57 | //! 58 | //! `Ustring` is the owned version of `&Ustr`. It supports conversion from and to many 59 | //! owned libstd string types. 60 | //! 61 | //! APIs that return strings usually return `&CStr`. Since `&CStr` is uncomfortable to 62 | //! work with, `AsUstr` provides the `as_ustr` method on `&CStr`. 63 | //! 64 | //! APIs that accept strings usually accept `impl IntoUstr`. `IntoUstr` is implemented for 65 | //! many libstd string types, `&Bstr`, `&Ustr`, and `Ustring`. The implementations of 66 | //! `IntoUstr` perform least-cost conversions into `&Ustr`. For example 67 | //! 68 | //! ```rust,ignore 69 | //! fn f<'a>(s: impl IntoUstr<'a>) { 70 | //! let _ = s.into_ustr(); 71 | //! } 72 | //! 73 | //! f("abc"); // allocates 74 | //! f("abc\0"); // does not allocate 75 | //! f("abc".to_string()); // appends a nul byte to the `String` 76 | //! f(CStr::from_ptr(p)); // does not allocate 77 | //! ``` 78 | //! 79 | //! # Pod & Packed 80 | //! 81 | //! This crate contains two traits for conversions of data structures from and to bytes: 82 | //! 83 | //! - `Pod` - Plain Old Data 84 | //! - `Packed` - Types without padding bytes 85 | //! 86 | //! The central property of `Pod` types is that they can be converted from bytes without 87 | //! first having to validate the bytes. All C types are `Pod`, though this crate only 88 | //! implements `Pod` for a selected number of types. More implementations might get added 89 | //! in the future. 90 | //! 91 | //! A `Packed` type does not contain padding bytes. This means that they can be safely 92 | //! converted into bytes because all bytes are guaranteed to be initialized. 93 | //! 94 | //! These types are useful for working with APIs that transfer structures over 95 | //! byte-oriented APIs like cmsg, inotify, signalfd, etc. 96 | //! 97 | //! The utilities to facilitate this are 98 | //! 99 | //! - `pod_zeroed` - Returns an instance of a `Pod` type with all bytes zeroed. 100 | //! - `pod_read` - Reads an instance of a `Packed` type as an instance of a `Pod` type. 101 | //! - `pod_iter` - Iterates over the instances of a `Pod` typed stored in an instance of a 102 | //! `Packed` type. 103 | //! - `pod_read_init` - Reads an initial part of an instance of a `Packed` type as an 104 | //! instance of a `Pod` type. 105 | //! - `pod_write` - Writes an instance of a `Packed` type to an instance of a `Pod` type. 106 | //! - `as_bytes` - Returns the bytes of an instance of a `Packed` type. 107 | //! 108 | //! If a certain type does not implement `Pod` or `Packed` and you require it, you can 109 | //! use `assert_pod` or `assert_packed`. 110 | //! 111 | //! # Control Messages (cmsg) 112 | //! 113 | //! This crate provides safe functions for writing and reading control messages: 114 | //! 115 | //! - `cmsg_write` - Writes a cmsg to a byte buffer 116 | //! - `cmsg_read` - Reads a cmsg from a byte buffer 117 | -------------------------------------------------------------------------------- /uapi/src/errno/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::c; 2 | 3 | /// Gets a pointer to the current thread's errno 4 | pub fn errno_location() -> *mut c::c_int { 5 | #[cfg(any(target_os = "dragonfly", target_os = "linux"))] 6 | unsafe { 7 | c::__errno_location() 8 | } 9 | 10 | #[cfg(any(target_os = "android", target_os = "netbsd", target_os = "openbsd"))] 11 | unsafe { 12 | c::__errno() 13 | } 14 | 15 | #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] 16 | unsafe { 17 | c::__error() 18 | } 19 | } 20 | 21 | /// Gets the current thread's errno 22 | pub fn get_errno() -> c::c_int { 23 | unsafe { *errno_location() } 24 | } 25 | 26 | /// Sets the current thread's errno 27 | pub fn set_errno(val: c::c_int) { 28 | unsafe { *errno_location() = val }; 29 | } 30 | -------------------------------------------------------------------------------- /uapi/src/fcntl/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[man("fcntl(2) with cmd = `F_ADD_SEALS`")] 4 | pub fn fcntl_add_seals(fd: c::c_int, seals: c::c_int) -> Result<()> { 5 | let res = unsafe { c::fcntl(fd, c::F_ADD_SEALS, seals) }; 6 | map_err!(res).map(drop) 7 | } 8 | 9 | #[man("fcntl(2) with cmd = `F_GET_SEALS`")] 10 | pub fn fcntl_get_seals(fd: c::c_int) -> Result { 11 | let res = unsafe { c::fcntl(fd, c::F_GET_SEALS) }; 12 | map_err!(res) 13 | } 14 | 15 | #[man("fcntl(2) with cmd = `F_OFD_SETLK`")] 16 | pub fn fcntl_ofd_setlk(fd: c::c_int, lock: &c::flock) -> Result<()> { 17 | let res = unsafe { c::fcntl(fd, c::F_OFD_SETLK, lock) }; 18 | map_err!(res).map(drop) 19 | } 20 | 21 | #[man("fcntl(2) with cmd = `F_OFD_SETLKW`")] 22 | pub fn fcntl_ofd_setlkw(fd: c::c_int, lock: &c::flock) -> Result<()> { 23 | let res = unsafe { c::fcntl(fd, c::F_OFD_SETLKW, lock) }; 24 | map_err!(res).map(drop) 25 | } 26 | 27 | #[man("fcntl(2) with cmd = `F_OFD_GETLK`")] 28 | pub fn fcntl_ofd_getlk(fd: c::c_int, lock: &mut c::flock) -> Result<()> { 29 | let res = unsafe { c::fcntl(fd, c::F_OFD_GETLK, lock) }; 30 | map_err!(res).map(drop) 31 | } 32 | -------------------------------------------------------------------------------- /uapi/src/fcntl/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | cfg_if::cfg_if! { 4 | if #[cfg(target_os = "linux")] { 5 | mod linux; 6 | pub use linux::*; 7 | } 8 | } 9 | 10 | #[man("fcntl(2) with cmd = `F_GETFD`")] 11 | pub fn fcntl_getfd(fd: c::c_int) -> Result { 12 | let res = unsafe { c::fcntl(fd, c::F_GETFD) }; 13 | map_err!(res) 14 | } 15 | 16 | #[man("fcntl(2) with cmd = `F_SETFD`")] 17 | pub fn fcntl_setfd(fd: c::c_int, flags: c::c_int) -> Result<()> { 18 | let res = unsafe { c::fcntl(fd, c::F_SETFD, flags) }; 19 | map_err!(res).map(drop) 20 | } 21 | 22 | #[man("fcntl(2) with cmd = `F_GETFL`")] 23 | pub fn fcntl_getfl(fd: c::c_int) -> Result { 24 | let res = unsafe { c::fcntl(fd, c::F_GETFL) }; 25 | map_err!(res) 26 | } 27 | 28 | #[man("fcntl(2) with cmd = `F_SETFL`")] 29 | pub fn fcntl_setfl(fd: c::c_int, flags: c::c_int) -> Result<()> { 30 | let res = unsafe { c::fcntl(fd, c::F_SETFL, flags) }; 31 | map_err!(res).map(drop) 32 | } 33 | 34 | #[man("fcntl(2) with cmd = `F_DUPFD`")] 35 | pub fn fcntl_dupfd(fd: c::c_int, lower_bound: c::c_int) -> Result { 36 | let res = unsafe { c::fcntl(fd, c::F_DUPFD, lower_bound) }; 37 | map_err!(res).map(OwnedFd::new) 38 | } 39 | 40 | #[man("fcntl(2) with cmd = `F_DUPFD_CLOEXEC`")] 41 | pub fn fcntl_dupfd_cloexec(fd: c::c_int, lower_bound: c::c_int) -> Result { 42 | let res = unsafe { c::fcntl(fd, c::F_DUPFD_CLOEXEC, lower_bound) }; 43 | map_err!(res).map(OwnedFd::new) 44 | } 45 | 46 | #[man("fcntl(2) with cmd = `F_SETLK`")] 47 | pub fn fcntl_setlk(fd: c::c_int, lock: &c::flock) -> Result<()> { 48 | let res = unsafe { c::fcntl(fd, c::F_SETLK, lock) }; 49 | map_err!(res).map(drop) 50 | } 51 | 52 | #[man("fcntl(2) with cmd = `F_SETLKW`")] 53 | pub fn fcntl_setlkw(fd: c::c_int, lock: &c::flock) -> Result<()> { 54 | let res = unsafe { c::fcntl(fd, c::F_SETLKW, lock) }; 55 | map_err!(res).map(drop) 56 | } 57 | 58 | #[man("fcntl(2) with cmd = `F_GETLK`")] 59 | pub fn fcntl_getlk(fd: c::c_int, lock: &mut c::flock) -> Result<()> { 60 | let res = unsafe { c::fcntl(fd, c::F_GETLK, lock) }; 61 | map_err!(res).map(drop) 62 | } 63 | 64 | #[man("fcntl(2) with cmd = `F_SETPIPE_SZ`")] 65 | #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd")))] 66 | pub fn fcntl_setpipe_sz(fd: c::c_int, size: c::c_int) -> Result { 67 | let res = unsafe { c::fcntl(fd, c::F_SETPIPE_SZ, size) }; 68 | map_err!(res) 69 | } 70 | 71 | #[man("fcntl(2) with cmd = `F_GETPIPE_SZ`")] 72 | #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd")))] 73 | pub fn fcntl_getpipe_sz(fd: c::c_int) -> Result { 74 | let res = unsafe { c::fcntl(fd, c::F_GETPIPE_SZ) }; 75 | map_err!(res) 76 | } 77 | -------------------------------------------------------------------------------- /uapi/src/fd.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{ 3 | fs::File, 4 | io::{IoSlice, IoSliceMut, Read, Write}, 5 | mem, 6 | net::{TcpListener, TcpStream, UdpSocket}, 7 | ops::Deref, 8 | os::{ 9 | fd::{AsFd, AsRawFd, BorrowedFd, RawFd}, 10 | raw::c_int, 11 | unix::{ 12 | io::{FromRawFd, IntoRawFd}, 13 | net::{UnixDatagram, UnixListener, UnixStream}, 14 | }, 15 | }, 16 | process::{ChildStderr, ChildStdin, ChildStdout, Stdio}, 17 | }; 18 | 19 | /// An owned file descriptor 20 | /// 21 | /// Upon `Drop`, the contained file descriptor will be closed. 22 | /// Errors from `close()` are ignored. 23 | /// 24 | /// The contained file descriptor can be accessed via deref: `*self`. 25 | /// 26 | /// This struct can be converted `From` and `Into` various `std` types. 27 | /// 28 | /// This struct implements [`AsFd`], however, note that we do not enforce the requirements 29 | /// of [`BorrowedFd`]. 30 | #[derive(Debug, Eq, PartialEq)] 31 | #[repr(transparent)] 32 | pub struct OwnedFd { 33 | raw: c_int, 34 | } 35 | 36 | impl OwnedFd { 37 | pub fn new(raw: c_int) -> OwnedFd { 38 | Self { raw } 39 | } 40 | 41 | /// Shortcut for `Fd::new(*self)` 42 | pub fn borrow(&self) -> Fd { 43 | Fd::new(self.raw) 44 | } 45 | 46 | /// Returns `*self` and does not run `Drop` 47 | pub fn unwrap(self) -> c_int { 48 | let raw = self.raw; 49 | mem::forget(self); 50 | raw 51 | } 52 | 53 | /// Returns `*self` 54 | pub fn raw(&self) -> c_int { 55 | self.raw 56 | } 57 | } 58 | 59 | impl From for OwnedFd { 60 | fn from(value: std::os::fd::OwnedFd) -> Self { 61 | Self::new(value.into_raw_fd()) 62 | } 63 | } 64 | 65 | impl From for std::os::fd::OwnedFd { 66 | fn from(value: OwnedFd) -> Self { 67 | unsafe { std::os::fd::OwnedFd::from_raw_fd(value.unwrap()) } 68 | } 69 | } 70 | 71 | impl AsFd for OwnedFd { 72 | fn as_fd(&self) -> BorrowedFd<'_> { 73 | assert_ne!(self.raw, -1); 74 | unsafe { BorrowedFd::borrow_raw(self.raw) } 75 | } 76 | } 77 | 78 | impl AsRawFd for OwnedFd { 79 | fn as_raw_fd(&self) -> RawFd { 80 | self.raw 81 | } 82 | } 83 | 84 | impl FromRawFd for OwnedFd { 85 | unsafe fn from_raw_fd(fd: RawFd) -> Self { 86 | Self::new(fd) 87 | } 88 | } 89 | 90 | impl IntoRawFd for OwnedFd { 91 | fn into_raw_fd(self) -> RawFd { 92 | self.unwrap() 93 | } 94 | } 95 | 96 | impl Deref for OwnedFd { 97 | type Target = c_int; 98 | 99 | fn deref(&self) -> &Self::Target { 100 | &self.raw 101 | } 102 | } 103 | 104 | impl Drop for OwnedFd { 105 | fn drop(&mut self) { 106 | unsafe { 107 | c::close(self.raw); 108 | } 109 | } 110 | } 111 | 112 | macro_rules! from { 113 | ($ty:ident) => { 114 | impl From<$ty> for OwnedFd { 115 | fn from(x: $ty) -> Self { 116 | OwnedFd::new(IntoRawFd::into_raw_fd(x)) 117 | } 118 | } 119 | }; 120 | } 121 | 122 | macro_rules! to { 123 | ($ty:ident) => { 124 | impl From for $ty { 125 | fn from(fd: OwnedFd) -> Self { 126 | unsafe { <$ty as FromRawFd>::from_raw_fd(fd.unwrap()) } 127 | } 128 | } 129 | }; 130 | } 131 | 132 | macro_rules! bi { 133 | ($ty:ident) => { 134 | from!($ty); 135 | to!($ty); 136 | }; 137 | } 138 | 139 | bi!(File); 140 | bi!(TcpListener); 141 | bi!(TcpStream); 142 | bi!(UdpSocket); 143 | bi!(UnixDatagram); 144 | bi!(UnixStream); 145 | bi!(UnixListener); 146 | 147 | to!(Stdio); 148 | 149 | from!(ChildStderr); 150 | from!(ChildStdin); 151 | from!(ChildStdout); 152 | 153 | /// A borrowed file descriptor 154 | /// 155 | /// The contained file descriptor can be accessed via deref: `*self`. 156 | /// 157 | /// This struct implements `Read` and `Write`. 158 | /// 159 | /// This struct implements [`AsFd`], however, note that we do not enforce the requirements 160 | /// of [`BorrowedFd`]. 161 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 162 | #[repr(transparent)] 163 | pub struct Fd { 164 | raw: c_int, 165 | } 166 | 167 | impl Fd { 168 | /// Creates a new `Fd` 169 | pub fn new(raw: c_int) -> Fd { 170 | Fd { raw } 171 | } 172 | 173 | /// Returns `*self` 174 | pub fn raw(self) -> c_int { 175 | self.raw 176 | } 177 | } 178 | 179 | impl AsFd for Fd { 180 | fn as_fd(&self) -> BorrowedFd<'_> { 181 | assert_ne!(self.raw, -1); 182 | unsafe { BorrowedFd::borrow_raw(self.raw) } 183 | } 184 | } 185 | 186 | impl AsRawFd for Fd { 187 | fn as_raw_fd(&self) -> RawFd { 188 | self.raw 189 | } 190 | } 191 | 192 | impl Deref for Fd { 193 | type Target = c_int; 194 | 195 | fn deref(&self) -> &Self::Target { 196 | &self.raw 197 | } 198 | } 199 | 200 | macro_rules! impl_io { 201 | ($ty:ident) => { 202 | impl Read for $ty { 203 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result { 204 | Ok(read(self.raw, buf)?.len()) 205 | } 206 | 207 | fn read_vectored( 208 | &mut self, 209 | bufs: &mut [IoSliceMut<'_>], 210 | ) -> std::io::Result { 211 | Ok(readv(self.raw, bufs)?.len()) 212 | } 213 | } 214 | 215 | impl Write for $ty { 216 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 217 | Ok(write(self.raw, buf)?) 218 | } 219 | 220 | fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> std::io::Result { 221 | Ok(writev(self.raw, bufs)?) 222 | } 223 | 224 | fn flush(&mut self) -> std::io::Result<()> { 225 | Ok(()) 226 | } 227 | } 228 | }; 229 | } 230 | 231 | impl_io!(Fd); 232 | impl_io!(OwnedFd); 233 | 234 | impl PartialEq for OwnedFd { 235 | fn eq(&self, other: &Fd) -> bool { 236 | self.raw == other.raw 237 | } 238 | } 239 | 240 | impl PartialEq for Fd { 241 | fn eq(&self, other: &OwnedFd) -> bool { 242 | self.raw == other.raw 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /uapi/src/ioctl/bsd.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use crate::c::c_int; 4 | use std::mem; 5 | 6 | /// [`IOCPARAM_SHIFT`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 7 | pub const IOCPARM_SHIFT: u64 = 13; 8 | 9 | /// [`IOCPARAM_SHIFT`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 10 | pub const IOCPARM_MASK: u64 = (1 << IOCPARM_SHIFT) - 1; 11 | 12 | /// [`IOCPARM_LEN`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 13 | pub const fn IOCPARM_LEN(x: u64) -> u64 { 14 | (x >> 16) & IOCPARM_MASK 15 | } 16 | /// [`IOCBASECMD`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 17 | pub const fn IOCBASECMD(x: u64) -> u64 { 18 | x & !(IOCPARM_MASK << 16) 19 | } 20 | /// [`IOCGROUP`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 21 | pub const fn IOCGROUP(x: u64) -> u64 { 22 | (x >> 8) & 0xff 23 | } 24 | 25 | /// [`IOCPARM_MAX`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 26 | pub const IOCPARM_MAX: u64 = (1 << IOCPARM_SHIFT) - 1; 27 | 28 | /// [`IOC_VOID`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 29 | pub const IOC_VOID: u64 = 0x20000000; 30 | 31 | /// [`IOC_OUT`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 32 | pub const IOC_OUT: u64 = 0x40000000; 33 | 34 | /// [`IOC_IN`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 35 | pub const IOC_IN: u64 = 0x80000000; 36 | 37 | /// [`IOC_INOUT`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 38 | pub const IOC_INOUT: u64 = IOC_IN | IOC_OUT; 39 | 40 | /// [`IOC_DIRMASK`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 41 | pub const IOC_DIRMASK: u64 = IOC_VOID | IOC_OUT | IOC_IN; 42 | 43 | /// [`_IOC`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 44 | pub const fn _IOC(inout: u64, group: u64, num: u64, len: u64) -> u64 { 45 | inout | ((len & IOCPARM_MASK) << 16) | (group << 8) | (num) 46 | } 47 | 48 | /// [`_IO`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 49 | pub const fn _IO(g: u64, n: u64) -> u64 { 50 | _IOC(IOC_VOID, g, n, 0) 51 | } 52 | 53 | /// [`_IOWINT`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 54 | pub const fn _IOWINT(g: u64, n: u64) -> u64 { 55 | _IOC(IOC_VOID, g, n, mem::size_of::() as _) 56 | } 57 | 58 | /// [`_IOR`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 59 | pub const fn _IOR(g: u64, n: u64) -> u64 { 60 | _IOC(IOC_OUT, g, n, mem::size_of::() as _) 61 | } 62 | 63 | /// [`_IOW`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 64 | pub const fn _IOW(g: u64, n: u64) -> u64 { 65 | _IOC(IOC_IN, g, n, mem::size_of::() as _) 66 | } 67 | 68 | /// [`_IOWR`](https://github.com/DragonFlyBSD/DragonFlyBSD/blob/v5.9.0/sys/sys/ioccom.h) 69 | pub const fn _IOWR(g: u64, n: u64) -> u64 { 70 | _IOC(IOC_INOUT, g, n, mem::size_of::() as _) 71 | } 72 | -------------------------------------------------------------------------------- /uapi/src/ioctl/linux.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use cfg_if::cfg_if; 4 | use std::mem; 5 | 6 | cfg_if! { 7 | if #[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "sparc64"))] { 8 | /// [`_IOC_NONE`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 9 | pub const _IOC_NONE: u64 = 1; 10 | /// [`_IOC_READ`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 11 | pub const _IOC_READ: u64 = 2; 12 | /// [`_IOC_WRITE`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 13 | pub const _IOC_WRITE: u64 = 4; 14 | /// [`_IOC_SIZEBITS`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 15 | pub const _IOC_SIZEBITS: u64 = 13; 16 | /// [`_IOC_DIRBITS`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 17 | pub const _IOC_DIRBITS: u64 = 3; 18 | } 19 | } 20 | cfg_if! { 21 | if #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "s390x", target_arch = "x86_64", target_arch = "aarch64", target_arch = "riscv64"))] { 22 | /// [`_IOC_NONE`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 23 | pub const _IOC_NONE: u64 = 0; 24 | /// [`_IOC_READ`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 25 | pub const _IOC_READ: u64 = 2; 26 | /// [`_IOC_WRITE`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 27 | pub const _IOC_WRITE: u64 = 1; 28 | /// [`_IOC_SIZEBITS`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 29 | pub const _IOC_SIZEBITS: u64 = 14; 30 | /// [`_IOC_DIRBITS`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 31 | pub const _IOC_DIRBITS: u64 = 2; 32 | } 33 | } 34 | 35 | /// [`_IOC_NRBITS`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 36 | pub const _IOC_NRBITS: u64 = 8; 37 | /// [`_IOC_TYPEBITS`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 38 | pub const _IOC_TYPEBITS: u64 = 8; 39 | /// [`_IOC_NRSHIFT`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 40 | pub const _IOC_NRSHIFT: u64 = 0; 41 | /// [`_IOC_TYPESHIFT`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 42 | pub const _IOC_TYPESHIFT: u64 = _IOC_NRSHIFT + _IOC_NRBITS; 43 | /// [`_IOC_SIZESHIFT`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 44 | pub const _IOC_SIZESHIFT: u64 = _IOC_TYPESHIFT + _IOC_TYPEBITS; 45 | /// [`_IOC_DIRSHIFT`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 46 | pub const _IOC_DIRSHIFT: u64 = _IOC_SIZESHIFT + _IOC_SIZEBITS; 47 | /// [`_IOC_NRMASK`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 48 | pub const _IOC_NRMASK: u64 = (1 << _IOC_NRBITS) - 1; 49 | /// [`_IOC_TYPEMASK`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 50 | pub const _IOC_TYPEMASK: u64 = (1 << _IOC_TYPEBITS) - 1; 51 | /// [`_IOC_SIZEMASK`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 52 | pub const _IOC_SIZEMASK: u64 = (1 << _IOC_SIZEBITS) - 1; 53 | /// [`_IOC_DIRMASK`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 54 | pub const _IOC_DIRMASK: u64 = (1 << _IOC_DIRBITS) - 1; 55 | 56 | /// [`_IOC`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 57 | pub const fn _IOC(dir: u64, ty: u64, nr: u64, size: u64) -> u64 { 58 | (dir << _IOC_DIRSHIFT) 59 | | (ty << _IOC_TYPESHIFT) 60 | | (nr << _IOC_NRSHIFT) 61 | | (size << _IOC_SIZESHIFT) 62 | } 63 | 64 | /// [`_IO`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 65 | pub const fn _IO(ty: u64, nr: u64) -> u64 { 66 | _IOC(_IOC_NONE, ty, nr, 0) 67 | } 68 | 69 | /// [`_IOR`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 70 | pub const fn _IOR(ty: u64, nr: u64) -> u64 { 71 | _IOC(_IOC_READ, ty, nr, mem::size_of::() as _) 72 | } 73 | 74 | /// [`_IOW`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 75 | pub const fn _IOW(ty: u64, nr: u64) -> u64 { 76 | _IOC(_IOC_WRITE, ty, nr, mem::size_of::() as _) 77 | } 78 | 79 | /// [`_IOWR`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 80 | pub const fn _IOWR(ty: u64, nr: u64) -> u64 { 81 | _IOC(_IOC_READ | _IOC_WRITE, ty, nr, mem::size_of::() as _) 82 | } 83 | 84 | /// [`_IOC_DIR`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 85 | pub const fn _IOC_DIR(nr: u64) -> u64 { 86 | (nr >> _IOC_DIRSHIFT) & _IOC_DIRMASK 87 | } 88 | 89 | /// [`_IOC_TYPE`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 90 | pub const fn _IOC_TYPE(nr: u64) -> u64 { 91 | (nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK 92 | } 93 | 94 | /// [`_IOC_NR`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 95 | pub const fn _IOC_NR(nr: u64) -> u64 { 96 | (nr >> _IOC_NRSHIFT) & _IOC_NRMASK 97 | } 98 | 99 | /// [`_IOC_SIZE`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 100 | pub const fn _IOC_SIZE(nr: u64) -> u64 { 101 | (nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK 102 | } 103 | 104 | /// [`IOC_IN`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 105 | pub const IOC_IN: u64 = _IOC_WRITE << _IOC_DIRSHIFT; 106 | 107 | /// [`IOC_OUT`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 108 | pub const IOC_OUT: u64 = _IOC_READ << _IOC_DIRSHIFT; 109 | 110 | /// [`IOC_INOUT`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 111 | pub const IOC_INOUT: u64 = (_IOC_WRITE | _IOC_READ) << _IOC_DIRSHIFT; 112 | 113 | /// [`IOCSIZE_MASK`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 114 | pub const IOCSIZE_MASK: u64 = _IOC_SIZEMASK << _IOC_SIZESHIFT; 115 | 116 | /// [`IOCSIZE_SHIFT`](https://github.com/torvalds/linux/blob/v5.6/include/uapi/asm-generic/ioctl.h) 117 | pub const IOCSIZE_SHIFT: u64 = _IOC_SIZESHIFT; 118 | -------------------------------------------------------------------------------- /uapi/src/ioctl/mod.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | 3 | cfg_if! { 4 | if #[cfg(any(target_os = "linux", target_os = "android"))] { 5 | mod linux; 6 | pub use linux::*; 7 | } 8 | } 9 | 10 | cfg_if! { 11 | if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] { 12 | mod bsd; 13 | pub use bsd::*; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /uapi/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | #![allow( 3 | clippy::or_fun_call, 4 | clippy::manual_non_exhaustive, 5 | clippy::needless_lifetimes 6 | )] 7 | // https://github.com/rust-lang/rust-clippy/issues/6466 8 | #![allow(clippy::useless_conversion)] 9 | // https://github.com/rust-lang/rust-clippy/issues/6372 10 | #![allow(clippy::transmute_ptr_to_ptr)] 11 | 12 | //! Unix API crate 13 | //! 14 | //! NOTE: The crate documentation is in the `docs` module. 15 | 16 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 17 | 18 | pub use crate::{ 19 | dir::*, errno::*, fcntl::*, fd::*, file::*, ioctl::*, mount::*, other::*, pod::*, 20 | poll::*, process::*, result::*, sched::*, signal::*, socket::*, timer::*, uninit::*, 21 | ustr::*, util::*, 22 | }; 23 | 24 | use proc::*; 25 | 26 | pub mod c; 27 | pub mod docs; 28 | 29 | #[macro_use] 30 | mod macros; 31 | mod dir; 32 | mod errno; 33 | mod fcntl; 34 | mod fd; 35 | mod file; 36 | mod ioctl; 37 | mod mount; 38 | mod other; 39 | mod pod; 40 | mod poll; 41 | mod process; 42 | mod result; 43 | mod sched; 44 | mod signal; 45 | mod socket; 46 | mod timer; 47 | mod uninit; 48 | mod ustr; 49 | mod util; 50 | -------------------------------------------------------------------------------- /uapi/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Forwards its arguments to `format!` and wraps the result in a `Ustring` 2 | #[macro_export] 3 | macro_rules! format_ustr { 4 | ($($tt:tt)*) => { 5 | $crate::Ustring::from_string(format!($($tt)*)) 6 | } 7 | } 8 | 9 | /// `fn() -> &'static Ustr` 10 | #[macro_export] 11 | macro_rules! ustr { 12 | ($val:expr) => { 13 | unsafe { $crate::Ustr::from_bytes_unchecked(concat!($val, "\0").as_bytes()) } 14 | }; 15 | } 16 | 17 | /// `fn() -> Result` 18 | /// 19 | /// The result is `Err` if and only if the argument is `-1`. In this case the return value 20 | /// contains the current value of `ERRNO`. 21 | #[macro_export] 22 | macro_rules! map_err { 23 | ($expr:expr) => {{ 24 | let val = $expr; 25 | if val == -1 { 26 | Err($crate::Errno::default()) 27 | } else { 28 | Ok(val) 29 | } 30 | }}; 31 | } 32 | 33 | #[cfg(target_os = "linux")] 34 | macro_rules! usize_right_shift { 35 | ($x:expr) => {{ 36 | const HALF_USIZE: usize = std::mem::size_of::() / 2; 37 | (($x) >> HALF_USIZE >> HALF_USIZE) 38 | }}; 39 | } 40 | -------------------------------------------------------------------------------- /uapi/src/mount/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{convert::TryFrom, mem::MaybeUninit, ptr}; 3 | 4 | #[man(mount(2))] 5 | pub fn mount<'a, 'b, 'c, 'd>( 6 | src: impl IntoUstr<'a>, 7 | target: impl IntoUstr<'b>, 8 | fstype: impl IntoUstr<'c>, 9 | flags: c::c_ulong, 10 | data: Option<&'d [MaybeUninit]>, 11 | ) -> Result<()> { 12 | let src = src.into_ustr(); 13 | let target = target.into_ustr(); 14 | let fstype = fstype.into_ustr(); 15 | let data = data 16 | .map(|d| black_box_id(d.as_ptr()) as *const _) 17 | .unwrap_or(ptr::null()); 18 | let res = unsafe { 19 | c::mount( 20 | src.as_ptr(), 21 | target.as_ptr(), 22 | fstype.as_ptr_null(), 23 | flags, 24 | data, 25 | ) 26 | }; 27 | map_err!(res).map(drop) 28 | } 29 | 30 | #[man(umount2(2))] 31 | pub fn umount2<'a>(target: impl IntoUstr<'a>, flags: c::c_int) -> Result<()> { 32 | let target = target.into_ustr(); 33 | let res = unsafe { c::umount2(target.as_ptr(), flags) }; 34 | map_err!(res).map(drop) 35 | } 36 | 37 | /// [`open_tree(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/namespace.c#L2359) 38 | pub fn open_tree<'a>( 39 | dfd: c::c_int, 40 | filename: impl IntoUstr<'a>, 41 | flags: c::c_uint, 42 | ) -> Result { 43 | let filename = filename.into_ustr(); 44 | let res = unsafe { c::open_tree(dfd, filename.as_ptr(), flags) }; 45 | map_err!(res).map(OwnedFd::new) 46 | } 47 | 48 | /// [`move_mount(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/namespace.c#L3467-L3479) 49 | pub fn move_mount<'a, 'b>( 50 | from_dfd: c::c_int, 51 | from_pathname: impl IntoUstr<'a>, 52 | to_dfd: c::c_int, 53 | to_pathname: impl IntoUstr<'b>, 54 | flags: c::c_uint, 55 | ) -> Result<()> { 56 | let from_pathname = from_pathname.into_ustr(); 57 | let to_pathname = to_pathname.into_ustr(); 58 | let res = unsafe { 59 | c::move_mount( 60 | from_dfd, 61 | from_pathname.as_ptr(), 62 | to_dfd, 63 | to_pathname.as_ptr(), 64 | flags, 65 | ) 66 | }; 67 | map_err!(res).map(drop) 68 | } 69 | 70 | /// [`fsopen(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L107-L115) 71 | pub fn fsopen<'a>(fs_name: impl IntoUstr<'a>, flags: c::c_uint) -> Result { 72 | let fs_name = fs_name.into_ustr(); 73 | let res = unsafe { c::fsopen(fs_name.as_ptr(), flags) }; 74 | map_err!(res).map(OwnedFd::new) 75 | } 76 | 77 | /// [`fsconfig(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L271-L320) with cmd = `FSCONFIG_SET_FLAG` 78 | pub fn fsconfig_set_flag<'a>(fs_fd: c::c_int, key: impl IntoUstr<'a>) -> Result<()> { 79 | let key = key.into_ustr(); 80 | let res = 81 | unsafe { c::fsconfig(fs_fd, c::FSCONFIG_SET_FLAG, key.as_ptr(), ptr::null(), 0) }; 82 | map_err!(res).map(drop) 83 | } 84 | 85 | /// [`fsconfig(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L271-L320) with cmd = `FSCONFIG_SET_STRING` 86 | pub fn fsconfig_set_string<'a, 'b>( 87 | fs_fd: c::c_int, 88 | key: impl IntoUstr<'a>, 89 | value: impl IntoUstr<'b>, 90 | ) -> Result<()> { 91 | let key = key.into_ustr(); 92 | let value = value.into_ustr(); 93 | let res = unsafe { 94 | c::fsconfig( 95 | fs_fd, 96 | c::FSCONFIG_SET_STRING, 97 | key.as_ptr(), 98 | value.as_ptr() as *const c::c_void, 99 | 0, 100 | ) 101 | }; 102 | map_err!(res).map(drop) 103 | } 104 | 105 | /// [`fsconfig(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L271-L320) with cmd = `FSCONFIG_SET_BINARY` 106 | pub fn fsconfig_set_binary<'a, T: ?Sized>( 107 | fs_fd: c::c_int, 108 | key: impl IntoUstr<'a>, 109 | value: &T, 110 | ) -> Result<()> { 111 | let key = key.into_ustr(); 112 | let value = as_maybe_uninit_bytes(value); 113 | let len = match c::c_int::try_from(value.len()) { 114 | Ok(len) => len, 115 | Err(_) => return Err(Errno(c::EINVAL)), 116 | }; 117 | let res = unsafe { 118 | c::fsconfig( 119 | fs_fd, 120 | c::FSCONFIG_SET_BINARY, 121 | key.as_ptr(), 122 | black_box_id(value.as_ptr()) as *const c::c_void, 123 | len, 124 | ) 125 | }; 126 | map_err!(res).map(drop) 127 | } 128 | 129 | fn _fsconfig_set_path<'a, 'b>( 130 | fs_fd: c::c_int, 131 | cmd: c::c_uint, 132 | key: impl IntoUstr<'a>, 133 | dfd: c::c_int, 134 | path: impl IntoUstr<'b>, 135 | ) -> Result<()> { 136 | let key = key.into_ustr(); 137 | let path = path.into_ustr(); 138 | let res = unsafe { 139 | c::fsconfig( 140 | fs_fd, 141 | cmd, 142 | key.as_ptr(), 143 | path.as_ptr() as *const c::c_void, 144 | dfd, 145 | ) 146 | }; 147 | map_err!(res).map(drop) 148 | } 149 | 150 | /// [`fsconfig(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L271-L320) with cmd = `FSCONFIG_SET_PATH` 151 | pub fn fsconfig_set_path<'a, 'b>( 152 | fs_fd: c::c_int, 153 | key: impl IntoUstr<'a>, 154 | dfd: c::c_int, 155 | path: impl IntoUstr<'b>, 156 | ) -> Result<()> { 157 | _fsconfig_set_path(fs_fd, c::FSCONFIG_SET_PATH, key, dfd, path) 158 | } 159 | 160 | /// [`fsconfig(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L271-L320) with cmd = `FSCONFIG_SET_PATH_EMPTY` 161 | pub fn fsconfig_set_path_empty<'a, 'b>( 162 | fs_fd: c::c_int, 163 | key: impl IntoUstr<'a>, 164 | dfd: c::c_int, 165 | path: impl IntoUstr<'b>, 166 | ) -> Result<()> { 167 | _fsconfig_set_path(fs_fd, c::FSCONFIG_SET_PATH_EMPTY, key, dfd, path) 168 | } 169 | 170 | /// [`fsconfig(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L271-L320) with cmd = `FSCONFIG_SET_FD` 171 | pub fn fsconfig_set_fd<'a>( 172 | fs_fd: c::c_int, 173 | key: impl IntoUstr<'a>, 174 | fd: c::c_int, 175 | ) -> Result<()> { 176 | let key = key.into_ustr(); 177 | let res = 178 | unsafe { c::fsconfig(fs_fd, c::FSCONFIG_SET_FD, key.as_ptr(), ptr::null(), fd) }; 179 | map_err!(res).map(drop) 180 | } 181 | 182 | fn _fsconfig_cmd(fs_fd: c::c_int, cmd: c::c_uint) -> Result<()> { 183 | let res = unsafe { c::fsconfig(fs_fd, cmd, ptr::null(), ptr::null(), 0) }; 184 | map_err!(res).map(drop) 185 | } 186 | 187 | /// [`fsconfig(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L271-L320) with cmd = `FSCONFIG_CMD_CREATE` 188 | pub fn fsconfig_cmd_create(fs_fd: c::c_int) -> Result<()> { 189 | _fsconfig_cmd(fs_fd, c::FSCONFIG_CMD_CREATE) 190 | } 191 | 192 | /// [`fsconfig(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L271-L320) with cmd = `FSCONFIG_CMD_RECONFIGURE` 193 | pub fn fsconfig_cmd_reconfigure(fs_fd: c::c_int) -> Result<()> { 194 | _fsconfig_cmd(fs_fd, c::FSCONFIG_CMD_RECONFIGURE) 195 | } 196 | 197 | /// [`fsmount(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/namespace.c#L3327-L3333) 198 | pub fn fsmount( 199 | fs_fd: c::c_int, 200 | flags: c::c_uint, 201 | attr_flags: c::c_uint, 202 | ) -> Result { 203 | let res = unsafe { c::fsmount(fs_fd, flags, attr_flags) }; 204 | map_err!(res).map(OwnedFd::new) 205 | } 206 | 207 | /// [`fspick(2)`](https://github.com/torvalds/linux/blob/v5.6/fs/fsopen.c#L155-L158) 208 | pub fn fspick<'a>( 209 | dfd: c::c_int, 210 | path: impl IntoUstr<'a>, 211 | flags: c::c_uint, 212 | ) -> Result { 213 | let path = path.into_ustr(); 214 | let res = unsafe { c::fspick(dfd, path.as_ptr(), flags) }; 215 | map_err!(res).map(OwnedFd::new) 216 | } 217 | -------------------------------------------------------------------------------- /uapi/src/mount/mod.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | 3 | cfg_if! { 4 | if #[cfg(target_os = "linux")] { 5 | mod linux; 6 | pub use linux::*; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /uapi/src/other/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::mem::MaybeUninit; 3 | 4 | #[man(eventfd(2))] 5 | pub fn eventfd(initval: c::c_uint, flags: c::c_int) -> Result { 6 | let res = unsafe { c::eventfd(initval, flags) }; 7 | map_err!(res).map(OwnedFd::new) 8 | } 9 | 10 | /// Reads from an eventfd file descriptor 11 | pub fn eventfd_read(fd: c::c_int) -> Result { 12 | let mut num = 0; 13 | let res = read(fd, &mut num)?.len(); 14 | if res < 8 { 15 | Err(Errno(c::EBADF)) 16 | } else { 17 | Ok(num) 18 | } 19 | } 20 | 21 | /// Writes to an eventfd file descriptor 22 | pub fn eventfd_write(fd: c::c_int, num: u64) -> Result<()> { 23 | let res = write(fd, &num)?; 24 | if res < 8 { 25 | Err(Errno(c::EBADF)) 26 | } else { 27 | Ok(()) 28 | } 29 | } 30 | 31 | #[man(memfd_create(2))] 32 | pub fn memfd_create<'a>(name: impl IntoUstr<'a>, flags: c::c_uint) -> Result { 33 | let name = name.into_ustr(); 34 | let res = unsafe { 35 | c::syscall(c::SYS_memfd_create, name.as_ptr() as usize, flags as usize) 36 | }; 37 | map_err!(res).map(|val| OwnedFd::new(val as _)) 38 | } 39 | 40 | #[man(sysinfo(2))] 41 | pub fn sysinfo() -> Result { 42 | let mut sysinfo = MaybeUninit::uninit(); 43 | let res = unsafe { c::sysinfo(sysinfo.as_mut_ptr()) }; 44 | map_err!(res).map(|_| unsafe { sysinfo.assume_init() }) 45 | } 46 | 47 | #[man(pipe2(2))] 48 | pub fn pipe2(flags: c::c_int) -> Result<(OwnedFd, OwnedFd)> { 49 | let mut buf = [0; 2]; 50 | let res = unsafe { c::pipe2(buf.as_mut_ptr(), flags) }; 51 | map_err!(res).map(|_| (OwnedFd::new(buf[0]), OwnedFd::new(buf[1]))) 52 | } 53 | 54 | #[man(syncfs(2))] 55 | pub fn syncfs(fd: c::c_int) -> Result<()> { 56 | let res = unsafe { libc::syscall(c::SYS_syncfs, fd as usize) }; 57 | map_err!(res).map(drop) 58 | } 59 | -------------------------------------------------------------------------------- /uapi/src/other/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use cfg_if::cfg_if; 3 | use std::{convert::TryInto, ffi::CStr, mem::MaybeUninit, ops::Deref, ptr}; 4 | 5 | cfg_if! { 6 | if #[cfg(target_os = "linux")] { 7 | mod linux; 8 | pub use linux::*; 9 | } 10 | } 11 | 12 | #[man(umask(2))] 13 | pub fn umask(mask: c::mode_t) -> c::mode_t { 14 | unsafe { c::umask(mask) } 15 | } 16 | 17 | #[man(pipe(2))] 18 | pub fn pipe() -> Result<(OwnedFd, OwnedFd)> { 19 | let mut buf = [0; 2]; 20 | let res = unsafe { c::pipe(buf.as_mut_ptr()) }; 21 | map_err!(res).map(|_| (OwnedFd::new(buf[0]), OwnedFd::new(buf[1]))) 22 | } 23 | 24 | /// Wrapper for `libc::utsname` 25 | pub struct UtsName { 26 | buf: c::utsname, 27 | } 28 | 29 | impl UtsName { 30 | /// Returns `self.sysname` as a `CStr` 31 | pub fn sysname(&self) -> &CStr { 32 | unsafe { CStr::from_ptr(self.buf.sysname.as_ptr()) } 33 | } 34 | 35 | /// Returns `self.nodename` as a `CStr` 36 | pub fn nodename(&self) -> &CStr { 37 | unsafe { CStr::from_ptr(self.buf.nodename.as_ptr()) } 38 | } 39 | 40 | /// Returns `self.release` as a `CStr` 41 | pub fn release(&self) -> &CStr { 42 | unsafe { CStr::from_ptr(self.buf.release.as_ptr()) } 43 | } 44 | 45 | /// Returns `self.version` as a `CStr` 46 | pub fn version(&self) -> &CStr { 47 | unsafe { CStr::from_ptr(self.buf.version.as_ptr()) } 48 | } 49 | 50 | /// Returns `self.machine` as a `CStr` 51 | pub fn machine(&self) -> &CStr { 52 | unsafe { CStr::from_ptr(self.buf.machine.as_ptr()) } 53 | } 54 | } 55 | 56 | impl Deref for UtsName { 57 | type Target = c::utsname; 58 | 59 | fn deref(&self) -> &Self::Target { 60 | &self.buf 61 | } 62 | } 63 | 64 | #[man(uname(2))] 65 | pub fn uname() -> Result { 66 | let mut uname = MaybeUninit::uninit(); 67 | let res = unsafe { c::uname(uname.as_mut_ptr()) }; 68 | map_err!(res).map(|_| UtsName { 69 | buf: unsafe { uname.assume_init() }, 70 | }) 71 | } 72 | 73 | #[man(daemon(3))] 74 | pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> { 75 | let res = unsafe { c::daemon(nochdir as _, noclose as _) }; 76 | map_err!(res).map(drop) 77 | } 78 | 79 | #[man(sethostname(2))] 80 | pub fn sethostname(buf: &[u8]) -> Result<()> { 81 | let res = unsafe { 82 | c::sethostname(buf.as_ptr() as *const _, buf.len().try_into().or(einval())?) 83 | }; 84 | map_err!(res).map(drop) 85 | } 86 | 87 | #[man(gethostname(2))] 88 | /// 89 | /// This function returns `libc::ENAMETOOLONG` if the hostname does not fit in the supplied 90 | /// buffer. If the hostname is longer than 255 bytes (excluding the nul byte), then this 91 | /// function always returns `libc::ENAMETOOLONG`. 92 | pub fn gethostname(buf: &mut T) -> Result<&CStr> { 93 | // Posix implies: If gethostname returns without an error then 94 | // - if the buffer does not contain a nul byte then the hostname was truncated 95 | // - if the buffer contains a nul byte in the last place then the hostname was 96 | // possibly truncated 97 | // - otherwise the buffer contains the hostname 98 | // In either case, the buffer has been fully initialized up to the first nul byte or 99 | // the end of the buffer depending on what comes first. 100 | // 101 | // Therefore strlen on the buffer is defined as long as we manually insert a 102 | // nul byte at the end of the buffer after gethostname returns 103 | // 104 | // SUSv2 guarantees that "Host names are limited to 255 bytes". Presumably this means 105 | // 255 bytes excluding the terminating nul byte. Therefore we use an 106 | // internal buffer of size 257 and manually set the last byte to 0 after 107 | // gethostname returns. If the first nul byte in the buffer is that very byte, then 108 | // we assume that there was a truncation and return an error. 109 | unsafe { 110 | let buf = as_maybe_uninit_bytes_mut2(buf); 111 | const SIZE: usize = 257; 112 | let mut inner = MaybeUninit::<[u8; SIZE]>::uninit(); 113 | let res = c::gethostname(inner.as_mut_ptr() as *mut _, SIZE); 114 | map_err!(res)?; 115 | *(inner.as_mut_ptr() as *mut u8).add(SIZE - 1) = 0; 116 | let cstr = CStr::from_ptr(inner.as_ptr() as *mut c::c_char); 117 | let bytes = cstr.to_bytes_with_nul(); 118 | if bytes.len() < SIZE && bytes.len() <= buf.len() { 119 | ptr::copy_nonoverlapping( 120 | bytes.as_ptr(), 121 | buf.as_mut_ptr() as *mut _, 122 | bytes.len(), 123 | ); 124 | Ok(CStr::from_bytes_with_nul_unchecked( 125 | buf[..bytes.len()].slice_assume_init_ref(), 126 | )) 127 | } else { 128 | Err(Errno(c::ENAMETOOLONG)) 129 | } 130 | } 131 | } 132 | 133 | #[man(sync(2))] 134 | pub fn sync() { 135 | unsafe { libc::sync() } 136 | } 137 | 138 | #[man(sysconf(3))] 139 | pub fn sysconf(name: c::c_int) -> Result { 140 | set_errno(0); 141 | let res = unsafe { c::sysconf(name) }; 142 | map_err!(res) 143 | } 144 | -------------------------------------------------------------------------------- /uapi/src/poll/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{convert::TryInto, ptr}; 3 | 4 | #[man(epoll_create1(2))] 5 | pub fn epoll_create1(flags: c::c_int) -> Result { 6 | let res = unsafe { c::epoll_create1(flags) }; 7 | map_err!(res).map(OwnedFd::new) 8 | } 9 | 10 | #[man(epoll_ctl(2))] 11 | pub fn epoll_ctl( 12 | epfd: c::c_int, 13 | op: c::c_int, 14 | fd: c::c_int, 15 | event: Option<&c::epoll_event>, 16 | ) -> Result<()> { 17 | let res = unsafe { 18 | c::epoll_ctl( 19 | epfd, 20 | op, 21 | fd, 22 | event.map(|v| v as *const _).unwrap_or(ptr::null()) as *mut _, 23 | ) 24 | }; 25 | map_err!(res).map(drop) 26 | } 27 | 28 | #[man(epoll_wait(2))] 29 | pub fn epoll_wait( 30 | epfd: c::c_int, 31 | events: &mut [c::epoll_event], 32 | timeout: c::c_int, 33 | ) -> Result { 34 | let len = events.len().try_into().unwrap_or(c::c_int::max_value()); 35 | let res = unsafe { c::epoll_wait(epfd, events.as_mut_ptr(), len, timeout) }; 36 | map_err!(res).map(|v| v as usize) 37 | } 38 | -------------------------------------------------------------------------------- /uapi/src/poll/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use cfg_if::cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(target_os = "linux")] { 6 | mod linux; 7 | pub use linux::*; 8 | } 9 | } 10 | 11 | #[man(poll(2))] 12 | pub fn poll(fds: &mut [c::pollfd], timeout: c::c_int) -> Result { 13 | let res = unsafe { c::poll(fds.as_mut_ptr(), fds.len() as _, timeout) }; 14 | map_err!(res).map(|r| r as usize) 15 | } 16 | -------------------------------------------------------------------------------- /uapi/src/process/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[man(setns(2))] 4 | pub fn setns(fd: c::c_int, nstype: c::c_int) -> Result<()> { 5 | let res = unsafe { c::setns(fd, nstype) }; 6 | map_err!(res).map(drop) 7 | } 8 | 9 | #[man(unshare(2))] 10 | pub fn unshare(flags: c::c_int) -> Result<()> { 11 | let res = unsafe { c::unshare(flags) }; 12 | map_err!(res).map(drop) 13 | } 14 | 15 | #[man(execveat(2))] 16 | pub fn execveat<'a>( 17 | dirfd: c::c_int, 18 | pathname: impl IntoUstr<'a>, 19 | argv: &UstrPtr, 20 | envp: &UstrPtr, 21 | flags: c::c_int, 22 | ) -> Result<()> { 23 | let pathname = pathname.into_ustr(); 24 | let res = unsafe { 25 | c::syscall( 26 | c::SYS_execveat, 27 | dirfd, 28 | pathname.as_ptr(), 29 | argv.as_ptr(), 30 | envp.as_ptr(), 31 | flags, 32 | ) 33 | }; 34 | map_err!(res).map(drop) 35 | } 36 | 37 | #[man(execvpe(3))] 38 | pub fn execvpe<'a, 'b, 'c>( 39 | pathname: impl IntoUstr<'a>, 40 | argv: &UstrPtr, 41 | envp: &UstrPtr, 42 | ) -> Result<()> { 43 | let pathname = pathname.into_ustr(); 44 | let res = unsafe { c::execvpe(pathname.as_ptr(), argv.as_ptr(), envp.as_ptr()) }; 45 | map_err!(res).map(drop) 46 | } 47 | 48 | #[man(gettid(2))] 49 | pub fn gettid() -> c::pid_t { 50 | unsafe { c::syscall(c::SYS_gettid) as _ } 51 | } 52 | 53 | #[man(pivot_root(2))] 54 | pub fn pivot_root<'a, 'b>( 55 | new_root: impl IntoUstr<'a>, 56 | old_root: impl IntoUstr<'a>, 57 | ) -> Result<()> { 58 | let new_root = new_root.into_ustr(); 59 | let old_root = old_root.into_ustr(); 60 | let res = unsafe { 61 | c::syscall( 62 | c::SYS_pivot_root, 63 | new_root.as_ptr() as usize, 64 | old_root.as_ptr() as usize, 65 | ) 66 | }; 67 | map_err!(res).map(drop) 68 | } 69 | 70 | #[man(pidfd_open(2))] 71 | pub fn pidfd_open(pid: c::pid_t, flags: c::c_uint) -> Result { 72 | let res = unsafe { c::syscall(c::SYS_pidfd_open, pid as usize, flags as usize) }; 73 | map_err!(res).map(|f| OwnedFd::new(f as _)) 74 | } 75 | 76 | #[man(pidfd_getfd(2))] 77 | pub fn pidfd_getfd( 78 | pidfd: c::c_int, 79 | targetfd: c::c_int, 80 | flags: c::c_uint, 81 | ) -> Result { 82 | let res = unsafe { 83 | c::syscall( 84 | c::SYS_pidfd_getfd, 85 | pidfd as usize, 86 | targetfd as usize, 87 | flags as usize, 88 | ) 89 | }; 90 | map_err!(res).map(|f| OwnedFd::new(f as _)) 91 | } 92 | -------------------------------------------------------------------------------- /uapi/src/result.rs: -------------------------------------------------------------------------------- 1 | use crate::get_errno; 2 | use std::{ 3 | fmt, 4 | fmt::{Display, Formatter}, 5 | }; 6 | 7 | /// `c_int` newtype which wraps `ERRNO` values 8 | /// 9 | /// The `Default` implementation returns the current value of `ERRNO`. 10 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 11 | pub struct Errno(pub crate::c::c_int); 12 | 13 | impl std::error::Error for Errno { 14 | } 15 | 16 | impl Display for Errno { 17 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 18 | write!(f, "{}", self.0) 19 | } 20 | } 21 | 22 | impl From for std::io::Error { 23 | fn from(e: Errno) -> Self { 24 | Self::from_raw_os_error(e.0) 25 | } 26 | } 27 | 28 | impl Default for Errno { 29 | fn default() -> Self { 30 | Errno(get_errno()) 31 | } 32 | } 33 | 34 | pub type Result = std::result::Result; 35 | -------------------------------------------------------------------------------- /uapi/src/sched/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{mem, mem::MaybeUninit}; 3 | 4 | #[man(sched_setaffinity(2))] 5 | pub fn sched_setaffinity(pid: c::pid_t, mask: &[usize]) -> Result<()> { 6 | unsafe { 7 | let res = c::syscall( 8 | c::SYS_sched_setaffinity, 9 | pid as usize, 10 | mem::size_of_val(mask), 11 | mask.as_ptr() as usize, 12 | ); 13 | map_err!(res).map(drop) 14 | } 15 | } 16 | 17 | #[man(sched_getaffinity(2))] 18 | pub fn sched_getaffinity(pid: c::pid_t, mask: &mut [usize]) -> Result { 19 | unsafe { 20 | let res = c::syscall( 21 | c::SYS_sched_getaffinity, 22 | pid as usize, 23 | mem::size_of_val(mask), 24 | mask.as_mut_ptr() as usize, 25 | ); 26 | map_err!(res).map(|v| v as usize) 27 | } 28 | } 29 | 30 | #[man(sched_setattr(2))] 31 | pub fn sched_setattr( 32 | pid: c::pid_t, 33 | attr: &c::sched_attr, 34 | flags: c::c_uint, 35 | ) -> Result<()> { 36 | unsafe { 37 | let mut attr = *attr; 38 | attr.size = mem::size_of_val(&attr) as u32; 39 | let res = c::sched_setattr(pid, &mut attr, flags); 40 | map_err!(res).map(drop) 41 | } 42 | } 43 | 44 | #[man(sched_getattr(2))] 45 | pub fn sched_getattr(pid: c::pid_t, flags: c::c_uint) -> Result { 46 | unsafe { 47 | let mut attr = MaybeUninit::uninit(); 48 | let size = mem::size_of_val(&attr) as c::c_uint; 49 | let res = c::sched_getattr(pid, attr.as_mut_ptr(), size, flags); 50 | map_err!(res)?; 51 | Ok(attr.assume_init()) 52 | } 53 | } 54 | 55 | #[man(sched_setscheduler(2))] 56 | pub fn sched_setscheduler( 57 | pid: c::pid_t, 58 | policy: c::c_int, 59 | param: &c::sched_param, 60 | ) -> Result { 61 | unsafe { 62 | let res = c::sched_setscheduler(pid, policy, param); 63 | map_err!(res) 64 | } 65 | } 66 | 67 | #[man(sched_getscheduler(2))] 68 | pub fn sched_getscheduler(pid: c::pid_t) -> Result { 69 | unsafe { 70 | let res = c::sched_getscheduler(pid); 71 | map_err!(res) 72 | } 73 | } 74 | 75 | #[man(sched_setparam(2))] 76 | pub fn sched_setparam(pid: c::pid_t, param: &c::sched_param) -> Result<()> { 77 | unsafe { 78 | let res = c::sched_setparam(pid, param); 79 | map_err!(res).map(drop) 80 | } 81 | } 82 | 83 | #[man(sched_getparam(2))] 84 | pub fn sched_getparam(pid: c::pid_t) -> Result { 85 | unsafe { 86 | let mut param = MaybeUninit::uninit(); 87 | let res = c::sched_getparam(pid, param.as_mut_ptr()); 88 | map_err!(res)?; 89 | Ok(param.assume_init()) 90 | } 91 | } 92 | 93 | #[man(sched_get_priority_max(2))] 94 | pub fn sched_get_priority_max(policy: c::c_int) -> Result { 95 | unsafe { 96 | let res = c::sched_get_priority_max(policy); 97 | map_err!(res) 98 | } 99 | } 100 | 101 | #[man(sched_get_priority_min(2))] 102 | pub fn sched_get_priority_min(policy: c::c_int) -> Result { 103 | unsafe { 104 | let res = c::sched_get_priority_min(policy); 105 | map_err!(res) 106 | } 107 | } 108 | 109 | #[man(sched_rr_get_interval(2))] 110 | pub fn sched_rr_get_interval(pid: c::pid_t) -> Result { 111 | unsafe { 112 | let mut tp = MaybeUninit::uninit(); 113 | let res = c::sched_rr_get_interval(pid, tp.as_mut_ptr()); 114 | map_err!(res)?; 115 | Ok(tp.assume_init()) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /uapi/src/sched/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use cfg_if::cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(target_os = "linux")] { 6 | mod linux; 7 | pub use linux::*; 8 | } 9 | } 10 | 11 | fn check_errno(res: c::c_int) -> Result { 12 | if res == -1 { 13 | let errno = get_errno(); 14 | if errno == 0 { 15 | Ok(-1) 16 | } else { 17 | Err(Errno(errno)) 18 | } 19 | } else { 20 | Ok(res) 21 | } 22 | } 23 | 24 | #[man(nice(2))] 25 | pub fn nice(inc: c::c_int) -> Result { 26 | set_errno(0); 27 | let res = unsafe { c::nice(inc) }; 28 | check_errno(res) 29 | } 30 | 31 | #[man(getpriority(2))] 32 | pub fn getpriority(which: c::c_int, who: c::id_t) -> Result { 33 | set_errno(0); 34 | let res = unsafe { c::getpriority(which as _, who as _) }; 35 | check_errno(res) 36 | } 37 | 38 | #[man(setpriority(2))] 39 | pub fn setpriority(which: c::c_int, who: c::id_t, prio: c::c_int) -> Result<()> { 40 | unsafe { 41 | let res = c::setpriority(which as _, who as _, prio); 42 | map_err!(res).map(drop) 43 | } 44 | } 45 | 46 | #[man(sched_yield(2))] 47 | pub fn sched_yield() -> Result<()> { 48 | unsafe { 49 | let res = c::sched_yield(); 50 | map_err!(res).map(drop) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /uapi/src/signal/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::mem; 3 | 4 | #[man("signalfd(2) with fd = `-1`")] 5 | pub fn signalfd_new(mask: &c::sigset_t, flags: c::c_int) -> Result { 6 | let res = unsafe { c::signalfd(-1, mask, flags) }; 7 | map_err!(res).map(OwnedFd::new) 8 | } 9 | 10 | #[man("signalfd(2) with fd != `-1`")] 11 | pub fn signalfd_mod(fd: c::c_int, mask: &c::sigset_t) -> Result<()> { 12 | if fd == -1 { 13 | return Err(Errno(c::EBADF)); 14 | } 15 | let res = unsafe { c::signalfd(fd, mask, 0) }; 16 | map_err!(res).map(drop) 17 | } 18 | 19 | /// Reads from a signalfd file descriptor and returns the elements read 20 | pub fn signalfd_read( 21 | fd: c::c_int, 22 | buf: &mut [c::signalfd_siginfo], 23 | ) -> Result<&mut [c::signalfd_siginfo]> { 24 | const SIZE: usize = mem::size_of::(); 25 | let res = unsafe { c::read(fd, buf as *mut _ as *mut _, SIZE * buf.len()) }; 26 | map_err!(res)?; 27 | if res as usize % SIZE != 0 { 28 | return Err(Errno(c::EBADF)); 29 | } 30 | Ok(&mut buf[..res as usize / SIZE]) 31 | } 32 | -------------------------------------------------------------------------------- /uapi/src/signal/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use cfg_if::cfg_if; 3 | use std::ptr; 4 | 5 | cfg_if! { 6 | if #[cfg(target_os = "linux")] { 7 | mod linux; 8 | pub use linux::*; 9 | } 10 | } 11 | 12 | #[man(raise(3))] 13 | pub fn raise(sig: c::c_int) -> Result<()> { 14 | let res = unsafe { c::raise(sig) }; 15 | map_err!(res).map(drop) 16 | } 17 | 18 | /// Returns an empty sig set 19 | pub fn empty_sig_set() -> Result { 20 | let mut set = pod_zeroed(); 21 | sigemptyset(&mut set)?; 22 | Ok(set) 23 | } 24 | 25 | #[man(sigsetops(3))] 26 | pub fn sigemptyset(set: &mut c::sigset_t) -> Result<()> { 27 | let res = unsafe { c::sigemptyset(set) }; 28 | map_err!(res).map(drop) 29 | } 30 | 31 | #[man(sigsetops(3))] 32 | pub fn sigfillset(set: &mut c::sigset_t) -> Result<()> { 33 | let res = unsafe { c::sigfillset(set) }; 34 | map_err!(res).map(drop) 35 | } 36 | 37 | #[man(sigsetops(3))] 38 | pub fn sigaddset(set: &mut c::sigset_t, signum: c::c_int) -> Result<()> { 39 | let res = unsafe { c::sigaddset(set, signum) }; 40 | map_err!(res).map(drop) 41 | } 42 | 43 | #[man(sigsetops(3))] 44 | pub fn sigdelset(set: &mut c::sigset_t, signum: c::c_int) -> Result<()> { 45 | let res = unsafe { c::sigdelset(set, signum) }; 46 | map_err!(res).map(drop) 47 | } 48 | 49 | #[man(sigsetops(3))] 50 | pub fn sigismember(set: &c::sigset_t, signum: c::c_int) -> Result { 51 | let res = unsafe { c::sigismember(set, signum) }; 52 | map_err!(res).map(|_| res == 1) 53 | } 54 | 55 | #[man(pthread_sigmask(3))] 56 | pub fn pthread_sigmask( 57 | how: c::c_int, 58 | set: Option<&c::sigset_t>, 59 | oldset: Option<&mut c::sigset_t>, 60 | ) -> Result<()> { 61 | let res = unsafe { 62 | c::pthread_sigmask( 63 | how, 64 | set.map(|v| v as *const _).unwrap_or(ptr::null()), 65 | oldset.map(|v| v as *mut _).unwrap_or(ptr::null_mut()), 66 | ) 67 | }; 68 | map_err!(res).map(drop) 69 | } 70 | 71 | #[man(sigwait(3))] 72 | pub fn sigwait(set: &c::sigset_t) -> Result { 73 | let mut sig = 0; 74 | let res = unsafe { c::sigwait(set, &mut sig) }; 75 | if res == 0 { 76 | Ok(sig) 77 | } else { 78 | Err(Errno(res)) 79 | } 80 | } 81 | 82 | #[man(sigwaitinfo(2))] 83 | #[cfg(not(any(target_os = "macos", target_os = "openbsd")))] 84 | pub fn sigwaitinfo( 85 | set: &c::sigset_t, 86 | info: Option<&mut c::siginfo_t>, 87 | ) -> Result { 88 | let res = unsafe { 89 | c::sigwaitinfo(set, info.map(|v| v as *mut _).unwrap_or(ptr::null_mut())) 90 | }; 91 | map_err!(res) 92 | } 93 | 94 | #[man(sigtimedwait(2))] 95 | #[cfg(not(any(target_os = "macos", target_os = "openbsd")))] 96 | pub fn sigtimedwait( 97 | set: &c::sigset_t, 98 | info: Option<&mut c::siginfo_t>, 99 | timeout: &c::timespec, 100 | ) -> Result { 101 | let res = unsafe { 102 | c::sigtimedwait( 103 | set, 104 | info.map(|v| v as *mut _).unwrap_or(ptr::null_mut()), 105 | timeout, 106 | ) 107 | }; 108 | map_err!(res) 109 | } 110 | -------------------------------------------------------------------------------- /uapi/src/socket/cmsg.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{ 3 | convert::{TryFrom, TryInto}, 4 | mem, 5 | mem::MaybeUninit, 6 | }; 7 | 8 | #[cfg(any(target_os = "dragonfly", target_os = "macos", target_os = "ios"))] 9 | const ALIGN: usize = 4 - 1; 10 | 11 | #[cfg(any( 12 | target_os = "linux", 13 | all( 14 | target_os = "freebsd", 15 | any( 16 | target_arch = "aarch64", 17 | target_arch = "arm", 18 | target_arch = "powerpc64", 19 | target_arch = "x86", 20 | target_arch = "x86_64" 21 | ) 22 | ), 23 | all( 24 | target_os = "openbsd", 25 | any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64") 26 | ), 27 | ))] 28 | const ALIGN: usize = mem::size_of::() - 1; 29 | 30 | const fn align(size: usize) -> usize { 31 | (size + ALIGN) & !ALIGN 32 | } 33 | 34 | const HDR_SIZE: usize = mem::size_of::(); 35 | 36 | const HDR_SPACE: usize = align(HDR_SIZE); 37 | 38 | #[repr(C)] 39 | union HdrBytes { 40 | hdr: c::cmsghdr, 41 | bytes: [u8; HDR_SPACE], 42 | } 43 | 44 | /// Returns the number of bytes needed to store a cmsg with data-length `data_len` 45 | /// 46 | /// See also the crate documentation. 47 | pub const fn cmsg_space(data_len: usize) -> usize { 48 | HDR_SPACE + align(data_len) 49 | } 50 | 51 | /// Reads a cmsg from a buffer 52 | /// 53 | /// This function will 54 | /// - advance the buffer by the used space 55 | /// - return the used space, the cmsg header, and the data buffer 56 | /// 57 | /// Returns an error if no message could be read from the buffer. 58 | /// 59 | /// See also the crate documentation. 60 | pub fn cmsg_read<'a>(buf: &mut &'a [u8]) -> Result<(usize, c::cmsghdr, &'a [u8])> { 61 | if buf.len() < HDR_SPACE { 62 | return einval(); 63 | } 64 | let mut hdr_bytes = HdrBytes { 65 | bytes: [0; HDR_SPACE], 66 | }; 67 | unsafe { 68 | hdr_bytes.bytes.copy_from_slice(&buf[..HDR_SPACE]); 69 | } 70 | let hdr = unsafe { hdr_bytes.hdr }; 71 | let cmsg_len = match usize::try_from(hdr.cmsg_len) { 72 | Ok(l) => l, 73 | _ => return einval(), 74 | }; 75 | if cmsg_len < HDR_SPACE { 76 | return einval(); 77 | } 78 | if usize::max_value() - cmsg_len < ALIGN { 79 | return einval(); 80 | } 81 | let cmsg_space = align(cmsg_len); 82 | if buf.len() < cmsg_space { 83 | return einval(); 84 | } 85 | let data = &buf[HDR_SPACE..cmsg_len]; 86 | *buf = &buf[cmsg_space..]; 87 | Ok((cmsg_space, hdr, data)) 88 | } 89 | 90 | /// Writes a cmsg to a buffer 91 | /// 92 | /// This function will 93 | /// - set `hdr.cmsg_len` to the correct value 94 | /// - write `hdr` and `data` to the buffer 95 | /// - advance the buffer by the used space 96 | /// - return the used space 97 | /// 98 | /// Returns an error if there is not enough space in the buffer. 99 | /// 100 | /// See also the crate documentation. 101 | pub fn cmsg_write( 102 | buf: &mut &mut [MaybeUninit], 103 | mut hdr: c::cmsghdr, 104 | data: &T, 105 | ) -> Result { 106 | let data_size = mem::size_of_val(data); 107 | let cmsg_space = cmsg_space(data_size); 108 | if buf.len() < cmsg_space { 109 | return einval(); 110 | } 111 | hdr.cmsg_len = match (HDR_SPACE + data_size).try_into() { 112 | Ok(v) => v, 113 | Err(_) => return einval(), 114 | }; 115 | let ptr = buf.as_mut_ptr(); 116 | unsafe { 117 | ptr.copy_from_nonoverlapping(&hdr as *const _ as *const _, HDR_SIZE); 118 | ptr.add(HDR_SPACE) 119 | .copy_from_nonoverlapping(data as *const _ as *const _, data_size); 120 | } 121 | *buf = &mut mem::take(buf)[cmsg_space..]; 122 | Ok(cmsg_space) 123 | } 124 | -------------------------------------------------------------------------------- /uapi/src/socket/linux/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub use netlink::*; 4 | 5 | mod netlink; 6 | 7 | #[man(accept4(2))] 8 | pub fn accept4( 9 | sockfd: c::c_int, 10 | mut addr: Option<&mut T>, 11 | flags: c::c_int, 12 | ) -> Result<(OwnedFd, usize)> { 13 | let mut addrlen = 0; 14 | let (ptr, len) = super::opt_to_sockaddr_mut(&mut addr, &mut addrlen)?; 15 | let res = unsafe { c::accept4(sockfd, ptr, len, flags) }; 16 | let fd = map_err!(res).map(OwnedFd::new)?; 17 | Ok((fd, addrlen as usize)) 18 | } 19 | -------------------------------------------------------------------------------- /uapi/src/socket/sockopt.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{convert::TryInto, mem}; 3 | 4 | #[man(getsockopt(2))] 5 | pub fn getsockopt( 6 | sockfd: c::c_int, 7 | level: c::c_int, 8 | optname: c::c_int, 9 | t: &mut T, 10 | ) -> Result { 11 | let mut len = match mem::size_of_val(t).try_into() { 12 | Ok(l) => l, 13 | Err(_) => return einval(), 14 | }; 15 | let res = 16 | unsafe { c::getsockopt(sockfd, level, optname, t as *mut _ as *mut _, &mut len) }; 17 | black_box(t); 18 | map_err!(res).map(|_| len as usize) 19 | } 20 | 21 | #[man(setsockopt(2))] 22 | pub fn setsockopt( 23 | sockfd: c::c_int, 24 | level: c::c_int, 25 | optname: c::c_int, 26 | t: &T, 27 | ) -> Result<()> { 28 | let len = match mem::size_of_val(t).try_into() { 29 | Ok(l) => l, 30 | Err(_) => return einval(), 31 | }; 32 | let t: *const c::c_void = black_box_id(t as *const _ as *const _); 33 | let res = unsafe { c::setsockopt(sockfd, level, optname, t, len) }; 34 | map_err!(res).map(drop) 35 | } 36 | -------------------------------------------------------------------------------- /uapi/src/timer/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[man(timerfd_create(2))] 4 | pub fn timerfd_create(clockid: c::c_int, flags: c::c_int) -> Result { 5 | let res = unsafe { c::timerfd_create(clockid, flags) }; 6 | map_err!(res).map(OwnedFd::new) 7 | } 8 | 9 | #[man(timerfd_settime(2))] 10 | pub fn timerfd_settime( 11 | fd: c::c_int, 12 | flags: c::c_int, 13 | new_value: &c::itimerspec, 14 | ) -> Result { 15 | let mut old_value = pod::pod_zeroed(); 16 | let res = unsafe { c::timerfd_settime(fd, flags, new_value, &mut old_value) }; 17 | map_err!(res).map(|_| old_value) 18 | } 19 | 20 | #[man(timerfd_gettime(2))] 21 | pub fn timerfd_gettime(fd: c::c_int) -> Result { 22 | let mut curr_value = pod::pod_zeroed(); 23 | let res = unsafe { c::timerfd_gettime(fd, &mut curr_value) }; 24 | map_err!(res).map(|_| curr_value) 25 | } 26 | -------------------------------------------------------------------------------- /uapi/src/timer/mod.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | 3 | cfg_if! { 4 | if #[cfg(target_os = "linux")] { 5 | mod linux; 6 | pub use linux::*; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /uapi/src/uninit.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{IoSlice, IoSliceMut}, 3 | mem, 4 | mem::MaybeUninit, 5 | slice, 6 | }; 7 | 8 | /// A possibly uninitialized `iovec` 9 | pub trait MaybeUninitIovec { 10 | /// Returns the `iovec` 11 | fn as_iovec(&self) -> &[&[MaybeUninit]]; 12 | } 13 | 14 | /// A possibly uninitialized `iovec` (mutable) 15 | pub trait MaybeUninitIovecMut { 16 | /// Returns the `iovec` 17 | /// 18 | /// # Safety 19 | /// 20 | /// The returnved `iovec` must not be used to write any uninitialized values. 21 | unsafe fn as_iovec_mut(&mut self) -> &mut [&mut [MaybeUninit]]; 22 | } 23 | 24 | macro_rules! impl_maybe_uninit_iovec { 25 | ($($ty:ty)*) => { 26 | $( 27 | impl MaybeUninitIovec for [$ty] { 28 | fn as_iovec(&self) -> &[&[MaybeUninit]] { 29 | unsafe { mem::transmute(self) } 30 | } 31 | } 32 | 33 | // TODO: https://github.com/rust-lang/rust/pull/79135 34 | // impl MaybeUninitIovec for [$ty; N] { 35 | // fn as_iovec(&self) -> &[&[MaybeUninit]] { 36 | // unsafe { mem::transmute(&self[..]) } 37 | // } 38 | // } 39 | )* 40 | } 41 | } 42 | 43 | impl_maybe_uninit_iovec! { 44 | IoSlice<'_> 45 | IoSliceMut<'_> 46 | &[u8] 47 | &mut [u8] 48 | &[MaybeUninit] 49 | &mut [MaybeUninit] 50 | &[i8] 51 | &mut [i8] 52 | &[MaybeUninit] 53 | &mut [MaybeUninit] 54 | } 55 | 56 | macro_rules! impl_maybe_uninit_iovec_mut { 57 | ($($ty:ty)*) => { 58 | $( 59 | impl MaybeUninitIovecMut for [$ty] { 60 | unsafe fn as_iovec_mut(&mut self) -> &mut [&mut [MaybeUninit]] { 61 | mem::transmute(self) 62 | } 63 | } 64 | 65 | // TODO: https://github.com/rust-lang/rust/pull/79135 66 | // impl MaybeUninitIovecMut for [$ty; N] { 67 | // unsafe fn as_iovec_mut(&mut self) -> &mut [&mut [MaybeUninit]] { 68 | // unsafe { mem::transmute(&mut self[..]) } 69 | // } 70 | // } 71 | )* 72 | } 73 | } 74 | 75 | impl_maybe_uninit_iovec_mut! { 76 | IoSlice<'_> 77 | IoSliceMut<'_> 78 | &mut [u8] 79 | &mut [MaybeUninit] 80 | &mut [i8] 81 | &mut [MaybeUninit] 82 | } 83 | 84 | /// A wrapper for a partially initialized `iovec` 85 | pub struct InitializedIovec<'a> { 86 | inner: InitializedIovecIter<'a>, 87 | } 88 | 89 | impl<'a> InitializedIovec<'a> { 90 | /// Returns an iterator over the initialized components of the `iovec` 91 | pub fn iter(&self) -> impl Iterator { 92 | self.inner.clone() 93 | } 94 | 95 | /// Returns the number of initialized bytes 96 | pub fn len(&self) -> usize { 97 | self.inner.initialized 98 | } 99 | 100 | /// Returns if there are no initialized bytes 101 | pub fn is_empty(&self) -> bool { 102 | self.len() == 0 103 | } 104 | 105 | pub(crate) unsafe fn new( 106 | buf: &'a [&'a mut [MaybeUninit]], 107 | initialized: usize, 108 | ) -> Self { 109 | Self { 110 | inner: InitializedIovecIter { 111 | buf: mem::transmute(buf), 112 | initialized, 113 | }, 114 | } 115 | } 116 | } 117 | 118 | impl<'a> IntoIterator for InitializedIovec<'a> { 119 | type Item = &'a [u8]; 120 | type IntoIter = InitializedIovecIter<'a>; 121 | 122 | fn into_iter(self) -> Self::IntoIter { 123 | self.inner 124 | } 125 | } 126 | 127 | /// An iterator over the initialized components of an `iovec` 128 | #[derive(Clone)] 129 | pub struct InitializedIovecIter<'a> { 130 | buf: &'a [&'a [MaybeUninit]], 131 | initialized: usize, 132 | } 133 | 134 | impl<'a> Iterator for InitializedIovecIter<'a> { 135 | type Item = &'a [u8]; 136 | 137 | fn next(&mut self) -> Option { 138 | if self.initialized == 0 { 139 | return None; 140 | } 141 | let buf = self.buf[0]; 142 | let len = self.initialized.min(buf.len()); 143 | self.initialized -= len; 144 | self.buf = &self.buf[1..]; 145 | unsafe { Some(buf[..len].slice_assume_init_ref()) } 146 | } 147 | } 148 | 149 | /// Returns the object representation of `t` 150 | pub fn as_maybe_uninit_bytes(t: &T) -> &[MaybeUninit] { 151 | unsafe { 152 | let ptr = t as *const _ as *const MaybeUninit; 153 | slice::from_raw_parts(ptr, mem::size_of_val(t)) 154 | } 155 | } 156 | 157 | /// Returns the mutable object representation of `t` 158 | pub fn as_maybe_uninit_bytes_mut(t: &mut MaybeUninit) -> &mut [MaybeUninit] { 159 | unsafe { 160 | let ptr = t as *mut _ as *mut MaybeUninit; 161 | slice::from_raw_parts_mut(ptr, mem::size_of_val(t)) 162 | } 163 | } 164 | 165 | /// Returns the mutable object representation of `t` 166 | /// 167 | /// This function exists because we cannot call as_maybe_uninit_mut for unsized T. 168 | /// 169 | /// # Safety 170 | /// 171 | /// The returned reference must not be used to write uninitialized data into `t`. 172 | pub unsafe fn as_maybe_uninit_bytes_mut2(t: &mut T) -> &mut [MaybeUninit] { 173 | let ptr = t as *mut _ as *mut MaybeUninit; 174 | slice::from_raw_parts_mut(ptr, mem::size_of_val(t)) 175 | } 176 | 177 | /// Casts the argument to `MaybeUninit` of the same type 178 | pub fn as_maybe_uninit(t: &T) -> &MaybeUninit { 179 | unsafe { mem::transmute(t) } 180 | } 181 | 182 | /// Casts the argument to `MaybeUninit` of the same type 183 | /// 184 | /// # Safety 185 | /// 186 | /// The returned reference must not be used to write uninitialized data into `t`. 187 | pub unsafe fn as_maybe_uninit_mut(t: &mut T) -> &mut MaybeUninit { 188 | mem::transmute(t) 189 | } 190 | 191 | mod sealed { 192 | pub trait Sealed {} 193 | } 194 | 195 | /// Extension for [`MaybeUninit`] 196 | #[allow(clippy::missing_safety_doc)] 197 | pub trait MaybeUninitSliceExt: sealed::Sealed { 198 | /// [`MaybeUninit::slice_assume_init_ref`] 199 | unsafe fn slice_assume_init_ref(&self) -> &[T]; 200 | /// [`MaybeUninit::slice_assume_init_mut`] 201 | unsafe fn slice_assume_init_mut(&mut self) -> &mut [T]; 202 | } 203 | 204 | impl sealed::Sealed for [MaybeUninit] { 205 | } 206 | 207 | impl MaybeUninitSliceExt for [MaybeUninit] { 208 | unsafe fn slice_assume_init_ref(&self) -> &[T] { 209 | mem::transmute(self) 210 | } 211 | 212 | unsafe fn slice_assume_init_mut(&mut self) -> &mut [T] { 213 | mem::transmute(self) 214 | } 215 | } 216 | 217 | #[cfg(test)] 218 | mod test { 219 | use crate::c; 220 | use std::{io::IoSlice, mem}; 221 | 222 | #[test] 223 | fn iovec_repr() { 224 | let buf = [0u8; 11]; 225 | 226 | let slice = &buf[..]; 227 | let iovec = IoSlice::new(slice); 228 | 229 | unsafe { 230 | assert_eq!(mem::size_of_val(&iovec), mem::size_of_val(&slice)); 231 | assert_eq!( 232 | c::memcmp( 233 | &iovec as *const _ as *const _, 234 | &slice as *const _ as *const _, 235 | mem::size_of_val(&slice) 236 | ), 237 | 0 238 | ); 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /uapi/src/ustr/as_ustr.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::ffi::{CStr, CString}; 3 | 4 | /// Used for cheap conversion from into `&Ustr` 5 | pub trait AsUstr { 6 | /// Perform the conversion 7 | fn as_ustr(&self) -> &Ustr; 8 | } 9 | 10 | impl AsUstr for Ustr { 11 | fn as_ustr(&self) -> &Ustr { 12 | self 13 | } 14 | } 15 | 16 | impl AsUstr for Ustring { 17 | fn as_ustr(&self) -> &Ustr { 18 | self 19 | } 20 | } 21 | 22 | impl AsUstr for CStr { 23 | fn as_ustr(&self) -> &Ustr { 24 | Ustr::from_c_str(self) 25 | } 26 | } 27 | 28 | impl AsUstr for CString { 29 | fn as_ustr(&self) -> &Ustr { 30 | Ustr::from_c_str(self) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /uapi/src/ustr/bstr.rs: -------------------------------------------------------------------------------- 1 | use crate::{c::c_char, Ustr, Ustring}; 2 | use std::{ 3 | ffi::{CStr, OsStr}, 4 | fmt, 5 | fmt::{Debug, Display, Formatter}, 6 | ops::{Deref, DerefMut}, 7 | os::unix::ffi::OsStrExt, 8 | path::Path, 9 | str::Utf8Error, 10 | }; 11 | 12 | /// Thin wrapper for a `[u8]` 13 | /// 14 | /// See also the crate documentation. 15 | #[repr(transparent)] 16 | #[derive(Eq, Ord, PartialOrd, PartialEq, Hash)] 17 | pub struct Bstr { 18 | bytes: [u8], 19 | } 20 | 21 | impl Bstr { 22 | /// Returns an empty `&Bstr` 23 | pub fn empty() -> &'static Self { 24 | static E: [u8; 0] = []; 25 | Self::from_bytes(&E[..]) 26 | } 27 | 28 | /// Transmutes the argument into `&Bstr` 29 | pub fn from_bytes(s: &[u8]) -> &Self { 30 | unsafe { &*(s as *const _ as *const _) } 31 | } 32 | 33 | /// Transmutes the argument into `&mut Bstr` 34 | pub fn from_bytes_mut(s: &mut [u8]) -> &mut Self { 35 | unsafe { &mut *(s as *mut _ as *mut _) } 36 | } 37 | 38 | /// Shortcut for `Bstr::from_bytes(s.as_bytes())` 39 | #[allow(clippy::should_implement_trait)] // https://github.com/rust-lang/rust-clippy/issues/5612 40 | pub fn from_str(s: &str) -> &Self { 41 | Self::from_bytes(s.as_bytes()) 42 | } 43 | 44 | /// Shortcut for `Bstr::from_bytes(s.as_bytes())` 45 | pub fn from_os_str(s: &OsStr) -> &Self { 46 | Self::from_bytes(s.as_bytes()) 47 | } 48 | 49 | /// Shortcut for `Bstr::from_os_str(s.as_os_str())` 50 | pub fn from_path(s: &Path) -> &Self { 51 | Self::from_os_str(s.as_os_str()) 52 | } 53 | 54 | /// Transmutes `self` into `&[u8]` 55 | pub fn as_bytes(&self) -> &[u8] { 56 | &self.bytes 57 | } 58 | 59 | /// Transmutes `self` into `&mut [u8]` 60 | pub fn as_bytes_mut(&mut self) -> &mut [u8] { 61 | &mut self.bytes 62 | } 63 | 64 | /// Shortcut for `str::from_utf8(self.as_bytes())` 65 | pub fn as_str(&self) -> Result<&str, Utf8Error> { 66 | std::str::from_utf8(self.as_bytes()) 67 | } 68 | 69 | /// Shortcut for `OsStr::from_bytes(self.as_bytes())` 70 | pub fn as_os_str(&self) -> &OsStr { 71 | OsStr::from_bytes(self.as_bytes()) 72 | } 73 | 74 | /// Shortcut for `Path::new(self.as_os_str())` 75 | pub fn as_path(&self) -> &Path { 76 | Path::new(self.as_os_str()) 77 | } 78 | 79 | /// Shortcut for `self.as_bytes().as_ptr()` 80 | pub fn as_ptr(&self) -> *const c_char { 81 | self.as_bytes().as_ptr() as *const c_char 82 | } 83 | 84 | /// Shortcut for `self.as_mut_bytes().as_mut_ptr()` 85 | pub fn as_mut_ptr(&mut self) -> *mut c_char { 86 | self.as_bytes_mut().as_mut_ptr() as *mut c_char 87 | } 88 | 89 | /// Shortcut for `self.as_bytes().len()` 90 | pub fn len(&self) -> usize { 91 | self.bytes.len() 92 | } 93 | 94 | /// Shortcut for `self.len() == 0` 95 | pub fn is_empty(&self) -> bool { 96 | self.len() == 0 97 | } 98 | 99 | /// Shortcut for `self.as_path().display()` 100 | pub fn display(&self) -> impl Display + '_ { 101 | self.as_path().display() 102 | } 103 | 104 | /// Allocates a new `Ustring` with the contents of this object 105 | pub fn to_ustring(&self) -> Ustring { 106 | let mut vec = Vec::with_capacity(self.len() + 1); 107 | vec.extend_from_slice(self.as_bytes()); 108 | vec.push(0); 109 | unsafe { Ustring::from_vec_with_nul_unchecked(vec) } 110 | } 111 | } 112 | 113 | impl ToOwned for Bstr { 114 | type Owned = Ustring; 115 | 116 | fn to_owned(&self) -> Self::Owned { 117 | self.to_ustring() 118 | } 119 | } 120 | 121 | impl Deref for Bstr { 122 | type Target = [u8]; 123 | 124 | fn deref(&self) -> &Self::Target { 125 | self.as_bytes() 126 | } 127 | } 128 | 129 | impl DerefMut for Bstr { 130 | fn deref_mut(&mut self) -> &mut Self::Target { 131 | self.as_bytes_mut() 132 | } 133 | } 134 | 135 | impl AsRef<[u8]> for Bstr { 136 | fn as_ref(&self) -> &[u8] { 137 | self.as_bytes() 138 | } 139 | } 140 | 141 | impl AsMut<[u8]> for Bstr { 142 | fn as_mut(&mut self) -> &mut [u8] { 143 | self.as_bytes_mut() 144 | } 145 | } 146 | 147 | impl AsRef for CStr { 148 | fn as_ref(&self) -> &Bstr { 149 | Ustr::from_c_str(self).as_bstr() 150 | } 151 | } 152 | 153 | impl AsRef for Bstr { 154 | fn as_ref(&self) -> &OsStr { 155 | self.as_os_str() 156 | } 157 | } 158 | 159 | impl AsRef for Bstr { 160 | fn as_ref(&self) -> &Path { 161 | self.as_path() 162 | } 163 | } 164 | 165 | impl Debug for Bstr { 166 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 167 | Debug::fmt(self.as_os_str(), f) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /uapi/src/ustr/bytes.rs: -------------------------------------------------------------------------------- 1 | use crate::{Bstr, Ustr}; 2 | use std::{ 3 | borrow::Cow, 4 | ffi::{CStr, OsStr}, 5 | ops::Deref, 6 | os::unix::ffi::OsStrExt, 7 | path::Path, 8 | }; 9 | 10 | /// Trait for objects which can be turned into bytes 11 | /// 12 | /// This is mostly an internal API. 13 | pub trait Bytes { 14 | fn bytes(&self) -> &[u8]; 15 | } 16 | 17 | impl Bytes for Bstr { 18 | fn bytes(&self) -> &[u8] { 19 | self.as_bytes() 20 | } 21 | } 22 | 23 | impl<'a> Bytes for Cow<'a, Ustr> { 24 | fn bytes(&self) -> &[u8] { 25 | self.deref().as_bytes() 26 | } 27 | } 28 | 29 | impl Bytes for [u8] { 30 | fn bytes(&self) -> &[u8] { 31 | self 32 | } 33 | } 34 | 35 | impl Bytes for str { 36 | fn bytes(&self) -> &[u8] { 37 | self.as_bytes() 38 | } 39 | } 40 | 41 | impl Bytes for CStr { 42 | fn bytes(&self) -> &[u8] { 43 | self.to_bytes() 44 | } 45 | } 46 | 47 | impl Bytes for OsStr { 48 | fn bytes(&self) -> &[u8] { 49 | OsStrExt::as_bytes(self) 50 | } 51 | } 52 | 53 | impl Bytes for Path { 54 | fn bytes(&self) -> &[u8] { 55 | OsStrExt::as_bytes(self.as_os_str()) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /uapi/src/ustr/eq.rs: -------------------------------------------------------------------------------- 1 | use crate::{Bstr, Ustr, Ustring}; 2 | use std::{ 3 | ffi::{CStr, CString, OsStr, OsString}, 4 | os::unix::ffi::OsStrExt, 5 | path::{Path, PathBuf}, 6 | }; 7 | 8 | macro_rules! foreign_eq { 9 | ($ty:path) => { 10 | impl PartialEq<[u8]> for $ty { 11 | fn eq(&self, other: &[u8]) -> bool { 12 | Bstr::as_bytes(self) == other 13 | } 14 | } 15 | 16 | impl PartialEq<$ty> for [u8] { 17 | fn eq(&self, other: &$ty) -> bool { 18 | Bstr::as_bytes(other) == self 19 | } 20 | } 21 | 22 | impl PartialEq for $ty { 23 | fn eq(&self, other: &str) -> bool { 24 | Bstr::as_bytes(self) == str::as_bytes(other) 25 | } 26 | } 27 | 28 | impl PartialEq<$ty> for str { 29 | fn eq(&self, other: &$ty) -> bool { 30 | Bstr::as_bytes(other) == str::as_bytes(self) 31 | } 32 | } 33 | 34 | impl PartialEq for $ty { 35 | fn eq(&self, other: &CStr) -> bool { 36 | Bstr::as_bytes(self) == CStr::to_bytes(other) 37 | } 38 | } 39 | 40 | impl PartialEq<$ty> for CStr { 41 | fn eq(&self, other: &$ty) -> bool { 42 | Bstr::as_bytes(other) == CStr::to_bytes(self) 43 | } 44 | } 45 | 46 | impl PartialEq for $ty { 47 | fn eq(&self, other: &OsStr) -> bool { 48 | Bstr::as_bytes(self) == OsStr::as_bytes(other) 49 | } 50 | } 51 | 52 | impl PartialEq<$ty> for OsStr { 53 | fn eq(&self, other: &$ty) -> bool { 54 | Bstr::as_bytes(other) == OsStr::as_bytes(self) 55 | } 56 | } 57 | 58 | impl PartialEq for $ty { 59 | fn eq(&self, other: &Path) -> bool { 60 | Bstr::as_bytes(self) == OsStr::as_bytes(Path::as_os_str(other)) 61 | } 62 | } 63 | 64 | impl PartialEq<$ty> for Path { 65 | fn eq(&self, other: &$ty) -> bool { 66 | Bstr::as_bytes(other) == OsStr::as_bytes(Path::as_os_str(self)) 67 | } 68 | } 69 | 70 | impl PartialEq> for $ty { 71 | fn eq(&self, other: &Vec) -> bool { 72 | Bstr::as_bytes(self) == Vec::as_slice(other) 73 | } 74 | } 75 | 76 | impl PartialEq<$ty> for Vec { 77 | fn eq(&self, other: &$ty) -> bool { 78 | Bstr::as_bytes(other) == Vec::as_slice(self) 79 | } 80 | } 81 | 82 | impl PartialEq for $ty { 83 | fn eq(&self, other: &String) -> bool { 84 | Bstr::as_bytes(self) == str::as_bytes(other) 85 | } 86 | } 87 | 88 | impl PartialEq<$ty> for String { 89 | fn eq(&self, other: &$ty) -> bool { 90 | Bstr::as_bytes(other) == str::as_bytes(self) 91 | } 92 | } 93 | 94 | impl PartialEq for $ty { 95 | fn eq(&self, other: &CString) -> bool { 96 | Bstr::as_bytes(self) == CStr::to_bytes(other) 97 | } 98 | } 99 | 100 | impl PartialEq<$ty> for CString { 101 | fn eq(&self, other: &$ty) -> bool { 102 | Bstr::as_bytes(other) == CStr::to_bytes(self) 103 | } 104 | } 105 | 106 | impl PartialEq for $ty { 107 | fn eq(&self, other: &OsString) -> bool { 108 | Bstr::as_bytes(self) == OsStr::as_bytes(other) 109 | } 110 | } 111 | 112 | impl PartialEq<$ty> for OsString { 113 | fn eq(&self, other: &$ty) -> bool { 114 | Bstr::as_bytes(other) == OsStr::as_bytes(self) 115 | } 116 | } 117 | 118 | impl PartialEq for $ty { 119 | fn eq(&self, other: &PathBuf) -> bool { 120 | Bstr::as_bytes(self) == OsStr::as_bytes(Path::as_os_str(other)) 121 | } 122 | } 123 | 124 | impl PartialEq<$ty> for PathBuf { 125 | fn eq(&self, other: &$ty) -> bool { 126 | Bstr::as_bytes(other) == OsStr::as_bytes(Path::as_os_str(self)) 127 | } 128 | } 129 | }; 130 | } 131 | 132 | foreign_eq!(Bstr); 133 | foreign_eq!(Ustr); 134 | foreign_eq!(Ustring); 135 | 136 | macro_rules! mutual_eq { 137 | ($a:ident, $b:ident) => { 138 | impl PartialEq<$a> for $b { 139 | fn eq(&self, other: &$a) -> bool { 140 | Bstr::as_bytes(self) == Bstr::as_bytes(other) 141 | } 142 | } 143 | 144 | impl PartialEq<$b> for $a { 145 | fn eq(&self, other: &$b) -> bool { 146 | Bstr::as_bytes(self) == Bstr::as_bytes(other) 147 | } 148 | } 149 | }; 150 | } 151 | 152 | mutual_eq!(Bstr, Ustr); 153 | mutual_eq!(Bstr, Ustring); 154 | mutual_eq!(Ustr, Ustring); 155 | -------------------------------------------------------------------------------- /uapi/src/ustr/into.rs: -------------------------------------------------------------------------------- 1 | use crate::{Bstr, Ustr, Ustring}; 2 | use std::{ 3 | borrow::Cow, 4 | ffi::{CStr, CString, OsStr, OsString}, 5 | fmt::Debug, 6 | ops::Deref, 7 | os::unix::ffi::{OsStrExt, OsStringExt}, 8 | path::{Path, PathBuf}, 9 | }; 10 | 11 | /// Trait for objects which can be turned into `Cow<'a, Ustr>` 12 | /// 13 | /// # Provided Implementations 14 | /// 15 | /// The implementations for `&Ustr` and `Ustring` return `self` unchanged. 16 | /// 17 | /// The other provided implementations for borrowed objects first check if the object has a trailing 18 | /// nul byte. If so, this byte is used as the trailing nul byte for the `Ustr`. This means that 19 | /// `IntoUstr` does not guarantee to round-trip. For example 20 | /// 21 | /// ``` 22 | /// # use uapi::IntoUstr; 23 | /// assert_eq!(b"abc", b"abc\0".into_ustr().as_bytes()); 24 | /// ``` 25 | pub trait IntoUstr<'a>: Debug { 26 | /// Converts `self` into `Cow<'a, Ustr>` 27 | fn into_ustr(self) -> Cow<'a, Ustr>; 28 | } 29 | 30 | impl<'a> IntoUstr<'a> for Cow<'a, Ustr> { 31 | fn into_ustr(self) -> Cow<'a, Ustr> { 32 | self 33 | } 34 | } 35 | 36 | impl<'a> IntoUstr<'a> for &'a Cow<'a, Ustr> { 37 | fn into_ustr(self) -> Cow<'a, Ustr> { 38 | self.deref().into_ustr() 39 | } 40 | } 41 | 42 | impl<'a> IntoUstr<'a> for &'a Ustr { 43 | fn into_ustr(self) -> Cow<'a, Ustr> { 44 | Cow::Borrowed(self) 45 | } 46 | } 47 | 48 | impl<'a> IntoUstr<'a> for &'a Ustring { 49 | fn into_ustr(self) -> Cow<'a, Ustr> { 50 | self.deref().into_ustr() 51 | } 52 | } 53 | 54 | impl IntoUstr<'static> for Ustring { 55 | fn into_ustr(self) -> Cow<'static, Ustr> { 56 | Cow::Owned(self) 57 | } 58 | } 59 | 60 | impl<'a> IntoUstr<'a> for &'a [u8] { 61 | fn into_ustr(self) -> Cow<'a, Ustr> { 62 | if let Some(s) = Ustr::from_bytes(self) { 63 | return Cow::Borrowed(s); 64 | } 65 | Cow::Owned(Ustring::from_vec(self.to_owned())) 66 | } 67 | } 68 | 69 | impl<'a> IntoUstr<'a> for &'a Bstr { 70 | fn into_ustr(self) -> Cow<'a, Ustr> { 71 | self.as_bytes().into_ustr() 72 | } 73 | } 74 | 75 | impl<'a> IntoUstr<'a> for Vec { 76 | fn into_ustr(self) -> Cow<'a, Ustr> { 77 | Cow::Owned(Ustring::from_vec(self)) 78 | } 79 | } 80 | 81 | impl<'a> IntoUstr<'a> for &'a str { 82 | fn into_ustr(self) -> Cow<'a, Ustr> { 83 | self.as_bytes().into_ustr() 84 | } 85 | } 86 | 87 | impl<'a> IntoUstr<'a> for String { 88 | fn into_ustr(self) -> Cow<'a, Ustr> { 89 | self.into_bytes().into_ustr() 90 | } 91 | } 92 | 93 | impl<'a> IntoUstr<'a> for &'a CStr { 94 | fn into_ustr(self) -> Cow<'a, Ustr> { 95 | Cow::Borrowed(Ustr::from_c_str(self)) 96 | } 97 | } 98 | 99 | impl<'a> IntoUstr<'a> for CString { 100 | fn into_ustr(self) -> Cow<'a, Ustr> { 101 | Cow::Owned(Ustring::from_c_string(self)) 102 | } 103 | } 104 | 105 | impl<'a> IntoUstr<'a> for &'a OsStr { 106 | fn into_ustr(self) -> Cow<'a, Ustr> { 107 | self.as_bytes().into_ustr() 108 | } 109 | } 110 | 111 | impl<'a> IntoUstr<'a> for OsString { 112 | fn into_ustr(self) -> Cow<'a, Ustr> { 113 | self.into_vec().into_ustr() 114 | } 115 | } 116 | 117 | impl<'a> IntoUstr<'a> for &'a Path { 118 | fn into_ustr(self) -> Cow<'a, Ustr> { 119 | self.as_os_str().into_ustr() 120 | } 121 | } 122 | 123 | impl<'a> IntoUstr<'a> for PathBuf { 124 | fn into_ustr(self) -> Cow<'a, Ustr> { 125 | self.into_os_string().into_ustr() 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /uapi/src/ustr/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::{ 2 | as_ustr::*, bstr::*, bytes::*, into::*, read::*, ustr::*, ustring::*, ustrptr::*, 3 | }; 4 | 5 | mod as_ustr; 6 | mod bstr; 7 | mod bytes; 8 | mod eq; 9 | mod into; 10 | mod read; 11 | #[allow(clippy::module_inception)] 12 | mod ustr; 13 | mod ustring; 14 | mod ustrptr; 15 | -------------------------------------------------------------------------------- /uapi/src/ustr/read.rs: -------------------------------------------------------------------------------- 1 | use crate::Ustring; 2 | use std::{io, io::Read, mem}; 3 | 4 | /// `Read` extensions 5 | pub trait UapiReadExt { 6 | /// Like `Read::read_to_end()` but for `Ustring` 7 | fn read_to_ustring(&mut self, s: &mut Ustring) -> io::Result; 8 | 9 | /// Shortcut for `read_to_ustring` with a new `Ustring` 10 | fn read_to_new_ustring(&mut self) -> io::Result; 11 | } 12 | 13 | impl UapiReadExt for T { 14 | fn read_to_ustring(&mut self, orig: &mut Ustring) -> io::Result { 15 | let mut s = mem::replace(orig, Ustring::new()).into_vec(); 16 | let res = self.read_to_end(&mut s); 17 | *orig = Ustring::from_vec(s); 18 | res 19 | } 20 | 21 | fn read_to_new_ustring(&mut self) -> io::Result { 22 | let mut s = Ustring::new(); 23 | self.read_to_ustring(&mut s).map(|_| s) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /uapi/src/ustr/ustr.rs: -------------------------------------------------------------------------------- 1 | use crate::{c::c_char, Bstr, Ustring}; 2 | use std::{ 3 | ffi::{CStr, FromBytesWithNulError, OsStr}, 4 | fmt, 5 | fmt::{Debug, Formatter}, 6 | ops::{Deref, DerefMut}, 7 | os::unix::ffi::OsStrExt, 8 | path::Path, 9 | ptr, slice, 10 | }; 11 | 12 | /// Thin wrapper for a `[u8]` that has a trailing nul byte 13 | /// 14 | /// NOTE: `Ustr` derefs to `Bstr` derefs to `[u8]`. Rustdoc might not show all available methods. 15 | /// 16 | /// See also the crate documentation. 17 | #[repr(transparent)] 18 | #[derive(Eq, Ord, PartialOrd, PartialEq, Hash)] 19 | pub struct Ustr { 20 | // invariant: last byte is 0 21 | bytes: [u8], 22 | } 23 | 24 | static NULL: [u8; 1] = [0]; 25 | 26 | impl Ustr { 27 | /// Returns an empty `Ustr` 28 | pub fn empty() -> &'static Self { 29 | static E: [u8; 1] = [0]; 30 | unsafe { Self::from_bytes_unchecked(&E[..]) } 31 | } 32 | 33 | /// Returns the unique `&Ustr` for which `self.is_null() == true` 34 | /// 35 | /// Apart from `self.is_null()`, the returned value is indistinguishable from `Ustr::empty()`. 36 | pub fn null() -> &'static Self { 37 | unsafe { Self::from_bytes_unchecked(&NULL[..]) } 38 | } 39 | 40 | /// Returns `true` iff this `Ustr` was constructed via `Ustr::null()` 41 | pub fn is_null(&self) -> bool { 42 | self.bytes.as_ptr() == NULL.as_ptr() 43 | } 44 | 45 | /// Transmutes the argument into `&Ustr` 46 | /// 47 | /// # Safety 48 | /// 49 | /// `s` must have a trailing nul byte. 50 | pub unsafe fn from_bytes_unchecked(s: &[u8]) -> &Self { 51 | &*(s as *const _ as *const _) 52 | } 53 | 54 | /// Transmutes the argument into `&mut Ustr` 55 | /// 56 | /// # Safety 57 | /// 58 | /// `s` must have a trailing nul byte. 59 | pub unsafe fn from_bytes_unchecked_mut(s: &mut [u8]) -> &mut Self { 60 | &mut *(s as *mut _ as *mut _) 61 | } 62 | 63 | /// Converts the argument into `&Ustr` after checking that it has a trailing nul byte 64 | /// 65 | /// Otherwise `None` is returned. 66 | pub fn from_bytes(s: &[u8]) -> Option<&Self> { 67 | if s.is_empty() || s[s.len() - 1] != 0 { 68 | return None; 69 | } 70 | Some(unsafe { Self::from_bytes_unchecked(s) }) 71 | } 72 | 73 | /// Converts the argument into `&mut Ustr` after checking that it has a trailing nul byte 74 | /// 75 | /// Otherwise `None` is returned. 76 | pub fn from_bytes_mut(s: &mut [u8]) -> Option<&mut Self> { 77 | if s.is_empty() || s[s.len() - 1] != 0 { 78 | return None; 79 | } 80 | Some(unsafe { Self::from_bytes_unchecked_mut(s) }) 81 | } 82 | 83 | /// Transmutes `self` into `&[u8]` 84 | pub fn as_bytes_with_nul(&self) -> &[u8] { 85 | &self.bytes 86 | } 87 | 88 | /// Shortcut for `Ustr::from_bytes(s.as_bytes())` 89 | #[allow(clippy::should_implement_trait)] // https://github.com/rust-lang/rust-clippy/issues/5612 90 | pub fn from_str(s: &str) -> Option<&Self> { 91 | Self::from_bytes(s.as_bytes()) 92 | } 93 | 94 | /// Transmutes the argument into `&Ustr` 95 | pub fn from_c_str(s: &CStr) -> &Self { 96 | unsafe { Self::from_bytes_unchecked(s.to_bytes_with_nul()) } 97 | } 98 | 99 | /// Shortcut for `Ustr::from_c_str(CStr::from_ptr(ptr))` 100 | /// 101 | /// # Safety 102 | /// 103 | /// Like `CStr::from_ptr` 104 | pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self { 105 | Self::from_c_str(CStr::from_ptr(ptr)) 106 | } 107 | 108 | /// Shortcut for `Ustr::from_bytes_unchecked_mut(CStr::from_ptr_mut(ptr).as_bytes_with_nul_mut())` 109 | /// 110 | /// (`CStr::from_ptr_mut` does not actually exist so check the source if you want to know the 111 | /// truth.) 112 | /// 113 | /// # Safety 114 | /// 115 | /// Like `CStr::from_ptr` 116 | pub unsafe fn from_ptr_mut<'a>(ptr: *mut c_char) -> &'a mut Self { 117 | let len = Self::from_ptr(ptr).len_with_nul(); 118 | Self::from_bytes_unchecked_mut(slice::from_raw_parts_mut(ptr as *mut _, len)) 119 | } 120 | 121 | /// Shortcut for `CStr::from_bytes_with_nul(self.as_bytes_with_nul())` 122 | pub fn as_c_str(&self) -> Result<&CStr, FromBytesWithNulError> { 123 | CStr::from_bytes_with_nul(&self.bytes) 124 | } 125 | 126 | /// Shortcut for `Ustr::from_bytes(s.as_bytes())` 127 | pub fn from_os_str(s: &OsStr) -> Option<&Self> { 128 | Self::from_bytes(s.as_bytes()) 129 | } 130 | 131 | /// Shortcut for `OsStr::from_bytes(self.as_bytes_with_nul())` 132 | pub fn as_os_str_with_nul(&self) -> &OsStr { 133 | OsStr::from_bytes(self.as_bytes_with_nul()) 134 | } 135 | 136 | /// Shortcut for `Ustr::from_os_str(s.as_os_str())` 137 | pub fn from_path(s: &Path) -> Option<&Self> { 138 | Self::from_os_str(s.as_os_str()) 139 | } 140 | 141 | /// Shortcut for `Path::new(self.as_os_str_with_nul())` 142 | pub fn as_path_with_nul(&self) -> &Path { 143 | Path::new(self.as_os_str_with_nul()) 144 | } 145 | 146 | /// Returns the length of the underlying `[u8]` including the trailing nul byte 147 | pub fn len_with_nul(&self) -> usize { 148 | self.bytes.len() 149 | } 150 | 151 | /// Returns the `&Bstr` created by dropping the trailing nul byte 152 | pub fn as_bstr(&self) -> &Bstr { 153 | Bstr::from_bytes(&self.bytes[..self.bytes.len() - 1]) 154 | } 155 | 156 | /// Returns the `&mut Bstr` created by dropping the trailing nul byte 157 | pub fn as_bstr_mut(&mut self) -> &mut Bstr { 158 | let len = self.bytes.len(); 159 | Bstr::from_bytes_mut(&mut self.bytes[..len - 1]) 160 | } 161 | 162 | /// Returns `ptr::null()` if `self.is_null()`, otherwise `self.as_ptr()`. 163 | pub fn as_ptr_null(&self) -> *const c_char { 164 | if self.is_null() { 165 | ptr::null() 166 | } else { 167 | self.as_ptr() 168 | } 169 | } 170 | } 171 | 172 | impl ToOwned for Ustr { 173 | type Owned = Ustring; 174 | 175 | fn to_owned(&self) -> Self::Owned { 176 | self.to_ustring() 177 | } 178 | } 179 | 180 | impl Deref for Ustr { 181 | type Target = Bstr; 182 | 183 | fn deref(&self) -> &Self::Target { 184 | self.as_bstr() 185 | } 186 | } 187 | 188 | impl DerefMut for Ustr { 189 | fn deref_mut(&mut self) -> &mut Self::Target { 190 | self.as_bstr_mut() 191 | } 192 | } 193 | 194 | impl AsRef<[u8]> for Ustr { 195 | fn as_ref(&self) -> &[u8] { 196 | self.as_bytes() 197 | } 198 | } 199 | 200 | impl AsMut<[u8]> for Ustr { 201 | fn as_mut(&mut self) -> &mut [u8] { 202 | self.as_bytes_mut() 203 | } 204 | } 205 | 206 | impl AsRef for CStr { 207 | fn as_ref(&self) -> &Ustr { 208 | Ustr::from_c_str(self) 209 | } 210 | } 211 | 212 | impl AsRef for Ustr { 213 | fn as_ref(&self) -> &OsStr { 214 | self.as_os_str() 215 | } 216 | } 217 | 218 | impl AsRef for Ustr { 219 | fn as_ref(&self) -> &Path { 220 | self.as_path() 221 | } 222 | } 223 | 224 | impl Debug for Ustr { 225 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 226 | Debug::fmt(self.as_os_str(), f) 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /uapi/src/ustr/ustrptr.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{ 3 | borrow::Cow, 4 | fmt::{Debug, Formatter}, 5 | iter::FromIterator, 6 | ptr, 7 | }; 8 | 9 | /// Wrapper for a `*const *const libc::c_char` with a terminating null pointer 10 | pub struct UstrPtr<'a> { 11 | ustrs: Vec>, 12 | ptrs: Vec<*const c::c_char>, 13 | } 14 | 15 | impl<'a> UstrPtr<'a> { 16 | /// Creates a new `UstrPtr` 17 | pub fn new() -> Self { 18 | Self { 19 | ustrs: vec![], 20 | ptrs: vec![ptr::null()], 21 | } 22 | } 23 | 24 | /// Appends a `*const libc::c_char` 25 | pub fn push(&mut self, s: impl IntoUstr<'a>) { 26 | let s = s.into_ustr(); 27 | self.ustrs.reserve_exact(1); 28 | self.ptrs.reserve_exact(1); 29 | self.ptrs.pop(); 30 | self.ptrs.push(s.as_ptr()); 31 | self.ptrs.push(ptr::null()); 32 | self.ustrs.push(s); 33 | } 34 | 35 | /// Returns the `*const *const c::c_char` 36 | pub fn as_ptr(&self) -> &*const c::c_char { 37 | &self.ptrs[0] 38 | } 39 | } 40 | 41 | impl<'a, T: IntoUstr<'a>> Extend for UstrPtr<'a> { 42 | fn extend>(&mut self, iter: U) { 43 | for ustr in iter { 44 | self.push(ustr); 45 | } 46 | } 47 | } 48 | 49 | impl<'a, T: IntoUstr<'a>> FromIterator for UstrPtr<'a> { 50 | fn from_iter>(iter: U) -> Self { 51 | let mut buf = UstrPtr::new(); 52 | buf.extend(iter); 53 | buf 54 | } 55 | } 56 | 57 | impl<'a> Default for UstrPtr<'a> { 58 | fn default() -> Self { 59 | UstrPtr::new() 60 | } 61 | } 62 | 63 | impl<'a> Debug for UstrPtr<'a> { 64 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 65 | Debug::fmt(&self.ustrs, f) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /uapi/src/util.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[man("Uses readlinkat(2) on (`fd`, `path`) and appends the result to `buf`")] 4 | pub fn read_link_to_ustring<'a>( 5 | fd: c::c_int, 6 | path: impl IntoUstr<'a>, 7 | buf: &mut Ustring, 8 | ) -> Result { 9 | let path = path.into_ustr(); 10 | let stat = fstatat(fd, &path, c::AT_SYMLINK_NOFOLLOW)?; 11 | if stat.st_mode & c::S_IFLNK == 0 { 12 | return Err(Errno(c::EINVAL)); 13 | } 14 | let mut size = stat.st_size as usize + 1; 15 | if size == 1 { 16 | size = 128; 17 | } 18 | loop { 19 | buf.reserve_exact(size); 20 | let mut retry = false; 21 | let res = unsafe { 22 | buf.with_unused(|buf| { 23 | let buf_len = buf.len(); 24 | match readlinkat(fd, &path, buf) { 25 | Ok(n) if n.len() == buf_len => { 26 | retry = true; 27 | Err(Errno(c::ENAMETOOLONG)) 28 | } 29 | r => r.map(|r| r.len()), 30 | } 31 | }) 32 | }; 33 | if retry && size < c::PATH_MAX as usize { 34 | size *= 2; 35 | } else { 36 | return res; 37 | } 38 | } 39 | } 40 | 41 | /// Shortcut for `read_link_to_ustring` with a new `Ustring` 42 | pub fn read_link_to_new_ustring<'a>( 43 | fd: c::c_int, 44 | path: impl IntoUstr<'a>, 45 | ) -> Result { 46 | let mut s = Ustring::new(); 47 | read_link_to_ustring(fd, path, &mut s).map(|_| s) 48 | } 49 | 50 | extern "C" { 51 | fn uapi_black_box(ptr: *const u8) -> *mut u8; 52 | } 53 | 54 | /// Does nothing 55 | /// 56 | /// However: 57 | /// 58 | /// 1. If the argument was derived from a mutable reference, the compiler cannot 59 | /// assume anything about the value of the pointed-to object after the call. 60 | /// 61 | /// This implementation currently works but should be replaced by a compiler intrinsic. 62 | pub(crate) fn black_box(ptr: *const T) { 63 | unsafe { 64 | uapi_black_box(ptr as *const _); 65 | } 66 | } 67 | 68 | /// Returns the argument 69 | /// 70 | /// However: 71 | /// 72 | /// 1. If the argument was derived from a mutable reference, the compiler cannot 73 | /// assume anything about the value of the pointed-to object after the call. 74 | /// 2. The compiler does not know anything about the origin of the returned pointer. 75 | /// 76 | /// This implementation currently works but should be replaced by a compiler intrinsic. 77 | pub(crate) fn black_box_id(ptr: *const T) -> *mut T { 78 | unsafe { uapi_black_box(ptr as *const _) as *mut _ } 79 | } 80 | 81 | /// Returns `Err(Errno(c::EINVAL))` 82 | pub(crate) const fn einval() -> Result { 83 | Err(Errno(c::EINVAL)) 84 | } 85 | 86 | pub(crate) trait Integer: Copy { 87 | const MAX_VALUE: Self; 88 | } 89 | 90 | macro_rules! imv { 91 | ($t:ty) => { 92 | impl Integer for $t { 93 | const MAX_VALUE: Self = <$t>::max_value(); 94 | } 95 | }; 96 | } 97 | 98 | imv!(i8); 99 | imv!(i16); 100 | imv!(i32); 101 | imv!(i64); 102 | imv!(i128); 103 | imv!(isize); 104 | imv!(u8); 105 | imv!(u16); 106 | imv!(u32); 107 | imv!(u64); 108 | imv!(u128); 109 | imv!(usize); 110 | -------------------------------------------------------------------------------- /uapi/tests/chdir.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | #[test] 6 | fn chdir1() { 7 | let tmp = Tempdir::new(); 8 | let tmpdir = &std::fs::canonicalize(tmp.bstr()).unwrap(); 9 | 10 | let mut buf1 = [MaybeUninit::::uninit(); 1024]; 11 | let old = getcwd(&mut buf1[..]).unwrap(); 12 | 13 | chdir(&tmp).unwrap(); 14 | 15 | assert_eq!(tmpdir, &*std::env::current_dir().unwrap()); 16 | 17 | chdir(old).unwrap(); 18 | 19 | assert_eq!(old.as_ustr(), &*std::env::current_dir().unwrap()); 20 | 21 | fchdir(*open(&tmp, c::O_RDONLY, 0).unwrap()).unwrap(); 22 | 23 | assert_eq!(tmpdir, &*std::env::current_dir().unwrap()); 24 | } 25 | -------------------------------------------------------------------------------- /uapi/tests/chroot.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | use proc::*; 4 | use testutils::*; 5 | use uapi::*; 6 | 7 | #[test_if(root)] 8 | fn chroot1() { 9 | let tmp = Tempdir::new(); 10 | 11 | let path = format!("{}/a", tmp); 12 | 13 | let fd = open(path, c::O_CREAT | c::O_RDONLY, 0).unwrap(); 14 | 15 | chroot(tmp.bstr()).unwrap(); 16 | chdir("/").unwrap(); 17 | 18 | assert_eq!(fstat(*fd).unwrap().st_ino, stat("a").unwrap().st_ino); 19 | } 20 | -------------------------------------------------------------------------------- /uapi/tests/close_range.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | #[cfg(target_os = "linux")] 4 | mod wrapper { 5 | use proc::test_if; 6 | use uapi::{c, close_range, fcntl_getfd, memfd_create, opendir, readdir, AsUstr}; 7 | 8 | fn get_open_fds() -> Vec { 9 | let mut fds = opendir("/proc/self/fd").unwrap(); 10 | let mut res = vec![]; 11 | while let Some(entry) = readdir(&mut fds) { 12 | let entry = entry.unwrap(); 13 | let str = entry.name().as_ustr().as_str().unwrap(); 14 | if str != "." && str != ".." { 15 | res.push(str.parse().unwrap()); 16 | } 17 | } 18 | res 19 | } 20 | 21 | #[test_if(linux_5_9)] 22 | fn close_range_() { 23 | const LIMIT: c::c_int = 128; 24 | 25 | let memfd = memfd_create("dummy", 0).unwrap(); 26 | assert!(get_open_fds().into_iter().all(|fd| fd < LIMIT)); 27 | uapi::dup2(memfd.raw(), LIMIT).unwrap(); 28 | assert!(get_open_fds().into_iter().any(|fd| fd >= LIMIT)); 29 | close_range(LIMIT as _, c::c_uint::MAX, 0).unwrap(); 30 | assert!(get_open_fds().into_iter().all(|fd| fd < LIMIT)); 31 | uapi::dup2(memfd.raw(), LIMIT).unwrap(); 32 | uapi::dup2(memfd.raw(), LIMIT + 10).unwrap(); 33 | uapi::dup2(memfd.raw(), LIMIT + 20).unwrap(); 34 | assert_eq!(fcntl_getfd(LIMIT).unwrap() & c::FD_CLOEXEC, 0); 35 | assert_eq!(fcntl_getfd(LIMIT + 10).unwrap() & c::FD_CLOEXEC, 0); 36 | assert_eq!(fcntl_getfd(LIMIT + 20).unwrap() & c::FD_CLOEXEC, 0); 37 | close_range(LIMIT as _, (LIMIT + 15) as _, c::CLOSE_RANGE_CLOEXEC).unwrap(); 38 | assert_ne!(fcntl_getfd(LIMIT).unwrap() & c::FD_CLOEXEC, 0); 39 | assert_ne!(fcntl_getfd(LIMIT + 10).unwrap() & c::FD_CLOEXEC, 0); 40 | assert_eq!(fcntl_getfd(LIMIT + 20).unwrap() & c::FD_CLOEXEC, 0); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /uapi/tests/dupfd.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | #[test] 4 | fn dupfd() { 5 | let (fd, _) = pipe().unwrap(); 6 | 7 | let dup = fcntl_dupfd(*fd, 100).unwrap(); 8 | assert_eq!(*dup, 100); 9 | assert_ne!(fcntl_getfd(*dup).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 10 | 11 | let dup = fcntl_dupfd_cloexec(*fd, 101).unwrap(); 12 | assert_eq!(*dup, 101); 13 | assert_eq!(fcntl_getfd(*dup).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 14 | } 15 | -------------------------------------------------------------------------------- /uapi/tests/exec1.rs: -------------------------------------------------------------------------------- 1 | use testutils::*; 2 | use uapi::*; 3 | 4 | fn check_exit(n: c::pid_t, code: c::c_int) { 5 | let (pid, status) = waitpid(n, 0).unwrap(); 6 | assert_eq!(pid, n); 7 | assert_eq!(WIFEXITED(status), true); 8 | assert_eq!(WEXITSTATUS(status), code); 9 | } 10 | 11 | fn sh() -> UstrPtr<'static> { 12 | ["sh", "-c"].iter().copied().collect() 13 | } 14 | 15 | #[test] 16 | fn exec1() { 17 | match unsafe { fork().unwrap() } { 18 | 0 => in_fork(|| { 19 | let mut buf = sh(); 20 | buf.push("exit $a"); 21 | 22 | let mut env = UstrPtr::new(); 23 | env.push("a=55"); 24 | 25 | execve("/bin/sh", &buf, &env).unwrap(); 26 | }), 27 | n => check_exit(n, 55), 28 | } 29 | 30 | std::env::set_var("a", "99"); 31 | 32 | match unsafe { fork().unwrap() } { 33 | 0 => in_fork(|| { 34 | let mut buf = sh(); 35 | buf.push("exit $a"); 36 | 37 | execv("/bin/sh", &buf).unwrap(); 38 | }), 39 | n => check_exit(n, 99), 40 | } 41 | 42 | std::env::set_var("a", "68"); 43 | 44 | match unsafe { fork().unwrap() } { 45 | 0 => in_fork(|| { 46 | let mut buf = sh(); 47 | buf.push("exit $a"); 48 | 49 | execvp("sh", &buf).unwrap(); 50 | }), 51 | n => check_exit(n, 68), 52 | } 53 | 54 | #[cfg(not(any(target_os = "macos", target_os = "openbsd")))] 55 | match unsafe { fork().unwrap() } { 56 | 0 => in_fork(|| { 57 | let mut buf = sh(); 58 | buf.push("exit 22"); 59 | 60 | fexecve( 61 | *open("/bin/sh", c::O_RDONLY, 0).unwrap(), 62 | &buf, 63 | &UstrPtr::new(), 64 | ) 65 | .unwrap(); 66 | }), 67 | n => check_exit(n, 22), 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /uapi/tests/exec2.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | #[cfg(target_os = "linux")] 3 | fn exec2() { 4 | use testutils::*; 5 | use uapi::*; 6 | 7 | fn check_exit(n: c::pid_t, code: c::c_int) { 8 | let (pid, status) = waitpid(n, 0).unwrap(); 9 | assert_eq!(pid, n); 10 | assert_eq!(WIFEXITED(status), true); 11 | assert_eq!(WEXITSTATUS(status), code); 12 | } 13 | 14 | fn sh() -> UstrPtr<'static> { 15 | ["sh", "-c"].iter().copied().collect() 16 | } 17 | 18 | match unsafe { fork().unwrap() } { 19 | 0 => in_fork(|| { 20 | let mut buf = sh(); 21 | buf.push("exit $a"); 22 | 23 | let mut env = UstrPtr::new(); 24 | env.push("a=55"); 25 | 26 | execveat(*open("/bin", c::O_RDONLY, 0).unwrap(), "sh", &buf, &env, 0) 27 | .unwrap(); 28 | }), 29 | n => check_exit(n, 55), 30 | } 31 | 32 | match unsafe { fork().unwrap() } { 33 | 0 => in_fork(|| { 34 | let mut buf = sh(); 35 | buf.push("exit $a"); 36 | 37 | let mut env = UstrPtr::new(); 38 | env.push("a=56"); 39 | 40 | execveat( 41 | *open("/bin/sh", c::O_RDONLY, 0).unwrap(), 42 | "", 43 | &buf, 44 | &env, 45 | c::AT_EMPTY_PATH, 46 | ) 47 | .unwrap(); 48 | }), 49 | n => check_exit(n, 56), 50 | } 51 | match unsafe { fork().unwrap() } { 52 | 0 => in_fork(|| { 53 | let mut buf = sh(); 54 | buf.push("exit $a"); 55 | 56 | let mut env = UstrPtr::new(); 57 | env.push("a=57"); 58 | 59 | execvpe("sh", &buf, &env).unwrap(); 60 | }), 61 | n => check_exit(n, 57), 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /uapi/tests/fcntl_lck.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | #[test] 6 | fn lock() { 7 | let tmp = Tempdir::new(); 8 | 9 | let path = &*format!("{}/a", tmp); 10 | 11 | let fd = open(path, c::O_CREAT | c::O_RDWR, 0o777).unwrap(); 12 | 13 | ftruncate(*fd, 1000).unwrap(); 14 | 15 | let mut lk: c::flock = pod_zeroed(); 16 | lk.l_type = c::F_WRLCK as _; 17 | lk.l_whence = c::SEEK_SET as _; 18 | lk.l_start = 3 as _; 19 | lk.l_len = 3 as _; 20 | 21 | fcntl_setlk(*fd, &lk).unwrap(); 22 | 23 | match unsafe { fork().unwrap() } { 24 | 0 => in_fork(|| { 25 | let mut lk2: c::flock = pod_zeroed(); 26 | 27 | lk2.l_type = c::F_RDLCK as _; 28 | lk2.l_whence = c::SEEK_SET as _; 29 | lk2.l_start = 4 as _; 30 | lk2.l_len = 1 as _; 31 | 32 | fcntl_getlk(*fd, &mut lk2).unwrap(); 33 | 34 | assert_eq!(lk2.l_type, lk.l_type); 35 | assert_eq!(lk2.l_whence, lk.l_whence); 36 | assert_eq!(lk2.l_start, lk.l_start); 37 | assert_eq!(lk2.l_len, lk.l_len); 38 | assert_eq!(lk2.l_pid, getppid()); 39 | 40 | exit(0); 41 | }), 42 | n => { 43 | let (_, ws) = waitpid(n, 0).unwrap(); 44 | assert!(WIFEXITED(ws)); 45 | assert_eq!(WEXITSTATUS(ws), 0); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /uapi/tests/main/dir/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | fn readdir_x Result>(f: F) { 6 | let tmp = Tempdir::new(); 7 | 8 | let mut result = HashSet::new(); 9 | result.extend( 10 | ["a", "b", "c"] 11 | .iter() 12 | .map(|v| Bstr::from_str(v).to_ustring()), 13 | ); 14 | 15 | for d in &result { 16 | create_file(format_ustr!("{}/{}", tmp, d.display())); 17 | } 18 | 19 | result.insert(".".into_ustr().into_owned()); 20 | result.insert("..".into_ustr().into_owned()); 21 | 22 | let mut dir = f(&tmp).unwrap(); 23 | let mut entries = HashSet::new(); 24 | 25 | while let Some(entry) = readdir(&mut dir) { 26 | assert!(entries.insert(entry.unwrap().name().as_ustr().to_ustring())); 27 | } 28 | 29 | assert_eq!(result, entries); 30 | } 31 | 32 | #[test] 33 | fn readdir1() { 34 | readdir_x(|t| opendir(t)); 35 | } 36 | 37 | #[test] 38 | fn fdopendir1() { 39 | readdir_x(|t| { 40 | let fd = open(t, c::O_RDONLY, 0).unwrap(); 41 | fdopendir(fd) 42 | }); 43 | } 44 | 45 | fn next(dir: &mut Dir) -> Ustring { 46 | readdir(dir).unwrap().unwrap().name().as_ustr().to_ustring() 47 | } 48 | 49 | #[test] 50 | fn seekdir1() { 51 | let tmp = Tempdir::new(); 52 | 53 | let mut dir = unsafe { Dir::from_ptr(opendir(&tmp).unwrap().unwrap()) }; 54 | 55 | let pos = telldir(&mut dir); 56 | 57 | let name1 = next(&mut dir); 58 | 59 | seekdir(&mut dir, pos); 60 | 61 | let name2 = next(&mut dir); 62 | let name3 = next(&mut dir); 63 | 64 | assert_eq!(name1, name2); 65 | assert_ne!(name1, name3); 66 | } 67 | 68 | #[test] 69 | fn rewinddir1() { 70 | let tmp = Tempdir::new(); 71 | 72 | create_file(format_ustr!("{}/a", tmp)); 73 | 74 | let mut dir = opendir(&tmp).unwrap(); 75 | 76 | let name1 = next(&mut dir); 77 | let name2 = next(&mut dir); 78 | 79 | rewinddir(&mut dir); 80 | 81 | let name3 = next(&mut dir); 82 | let name4 = next(&mut dir); 83 | 84 | assert_eq!(name1, name3); 85 | assert_eq!(name2, name4); 86 | } 87 | 88 | #[test] 89 | fn dirfd1() { 90 | let tmp = Tempdir::new(); 91 | 92 | let fd = open(&tmp, c::O_RDONLY, 0).unwrap(); 93 | let num = *fd; 94 | 95 | let mut dir = fdopendir(fd).unwrap(); 96 | 97 | assert_eq!(num, dirfd(&mut dir)); 98 | } 99 | -------------------------------------------------------------------------------- /uapi/tests/main/errno/mod.rs: -------------------------------------------------------------------------------- 1 | use std::io::Error; 2 | use uapi::*; 3 | 4 | #[test] 5 | fn set_errno1() { 6 | let cmp = |i| { 7 | set_errno(i); 8 | assert_eq!(i, get_errno()); 9 | assert_eq!(Some(i), Error::last_os_error().raw_os_error()); 10 | }; 11 | cmp(1); 12 | cmp(2); 13 | } 14 | -------------------------------------------------------------------------------- /uapi/tests/main/fcntl/linux.rs: -------------------------------------------------------------------------------- 1 | use testutils::Tempdir; 2 | use uapi::*; 3 | 4 | #[test] 5 | fn seal() { 6 | let fd = memfd_create("", c::MFD_ALLOW_SEALING).unwrap(); 7 | 8 | assert_eq!(fcntl_get_seals(*fd).unwrap(), 0); 9 | 10 | fcntl_add_seals(*fd, c::F_SEAL_SEAL).unwrap(); 11 | 12 | assert_eq!(fcntl_get_seals(*fd).unwrap(), c::F_SEAL_SEAL); 13 | } 14 | 15 | #[test] 16 | fn ofd() { 17 | let tmp = Tempdir::new(); 18 | 19 | let path = &*format!("{}/a", tmp); 20 | 21 | let fd = open(path, c::O_CREAT | c::O_RDWR, 0o777).unwrap(); 22 | 23 | ftruncate(*fd, 1000).unwrap(); 24 | 25 | let mut lk: c::flock = pod_zeroed(); 26 | lk.l_type = c::F_WRLCK as _; 27 | lk.l_whence = c::SEEK_SET as _; 28 | lk.l_start = 3 as _; 29 | lk.l_len = 3 as _; 30 | 31 | fcntl_ofd_setlk(*fd, &lk).unwrap(); 32 | 33 | let mut lk2: c::flock = pod_zeroed(); 34 | lk2.l_type = c::F_RDLCK as _; 35 | lk2.l_whence = c::SEEK_SET as _; 36 | lk2.l_start = 4 as _; 37 | lk2.l_len = 1 as _; 38 | 39 | fcntl_ofd_getlk(*open(path, c::O_RDWR, 0).unwrap(), &mut lk2).unwrap(); 40 | 41 | assert_eq!(lk2.l_type, lk.l_type); 42 | assert_eq!(lk2.l_whence, lk.l_whence); 43 | assert_eq!(lk2.l_start, lk.l_start); 44 | assert_eq!(lk2.l_len, lk.l_len); 45 | assert_eq!(lk2.l_pid, -1); 46 | } 47 | -------------------------------------------------------------------------------- /uapi/tests/main/fcntl/mod.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | cfg_if::cfg_if! { 4 | if #[cfg(target_os = "linux")] { 5 | mod linux; 6 | } 7 | } 8 | 9 | #[test] 10 | fn setfl() { 11 | let (r, _w) = pipe().unwrap(); 12 | let old = fcntl_getfl(*r).unwrap(); 13 | assert_eq!(old & c::O_NONBLOCK, 0); 14 | fcntl_setfl(*r, old | c::O_NONBLOCK).unwrap(); 15 | assert_eq!(read(*r, &mut [0][..]), Err(Errno(c::EAGAIN))); 16 | assert_ne!(fcntl_getfl(*r).unwrap() & c::O_NONBLOCK, 0); 17 | } 18 | 19 | #[test] 20 | fn setfd() { 21 | let (r, _w) = pipe().unwrap(); 22 | let old = fcntl_getfd(*r).unwrap(); 23 | assert_eq!(old & c::FD_CLOEXEC, 0); 24 | fcntl_setfd(*r, old | c::FD_CLOEXEC).unwrap(); 25 | assert_ne!(fcntl_getfd(*r).unwrap() & c::FD_CLOEXEC, 0); 26 | } 27 | 28 | #[test] 29 | #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd")))] 30 | fn pipesize() { 31 | let (r, _) = pipe().unwrap(); 32 | 33 | let len = fcntl_getpipe_sz(*r).unwrap(); 34 | fcntl_setpipe_sz(*r, len + 1).unwrap(); 35 | let len2 = fcntl_getpipe_sz(*r).unwrap(); 36 | assert!(len2 > len); 37 | fcntl_setpipe_sz(*r, len).unwrap(); 38 | let len2 = fcntl_getpipe_sz(*r).unwrap(); 39 | assert_eq!(len2, len); 40 | } 41 | -------------------------------------------------------------------------------- /uapi/tests/main/fd.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::{IoSlice, IoSliceMut, Read, Write}, 4 | net::{TcpListener, TcpStream, UdpSocket}, 5 | os::unix::net::{UnixDatagram, UnixListener, UnixStream}, 6 | process::{Command, Stdio}, 7 | }; 8 | use uapi::*; 9 | 10 | #[test] 11 | fn fd() { 12 | assert_eq!(OwnedFd::new(-1).unwrap(), -1); 13 | assert_eq!(OwnedFd::new(-1).borrow(), Fd::new(-1)); 14 | assert_eq!(*OwnedFd::new(-1), -1); 15 | assert_eq!(OwnedFd::new(-1).raw(), -1); 16 | 17 | assert_eq!(OwnedFd::from(File::from(OwnedFd::new(37))).unwrap(), 37); 18 | assert_eq!( 19 | OwnedFd::from(TcpListener::from(OwnedFd::new(37))).unwrap(), 20 | 37 21 | ); 22 | assert_eq!( 23 | OwnedFd::from(TcpStream::from(OwnedFd::new(37))).unwrap(), 24 | 37 25 | ); 26 | assert_eq!( 27 | OwnedFd::from(UdpSocket::from(OwnedFd::new(37))).unwrap(), 28 | 37 29 | ); 30 | assert_eq!( 31 | OwnedFd::from(UnixDatagram::from(OwnedFd::new(37))).unwrap(), 32 | 37 33 | ); 34 | assert_eq!( 35 | OwnedFd::from(UnixStream::from(OwnedFd::new(37))).unwrap(), 36 | 37 37 | ); 38 | assert_eq!( 39 | OwnedFd::from(UnixListener::from(OwnedFd::new(37))).unwrap(), 40 | 37 41 | ); 42 | 43 | let (mut r, w) = pipe().unwrap(); 44 | 45 | Command::new("/bin/sh") 46 | .arg("-c") 47 | .arg("printf x") 48 | .stdout(w) 49 | .status() 50 | .unwrap(); 51 | 52 | assert_eq!(&r.read_to_new_ustring().unwrap(), "x"); 53 | 54 | let mut child = Command::new("/bin/sh") 55 | .arg("-c") 56 | .arg("printf y >&2; cat") 57 | .stdout(Stdio::piped()) 58 | .stderr(Stdio::piped()) 59 | .stdin(Stdio::piped()) 60 | .spawn() 61 | .unwrap(); 62 | 63 | OwnedFd::from(child.stdin.take().unwrap()) 64 | .write_all(b"x") 65 | .unwrap(); 66 | let mut r = OwnedFd::from(child.stdout.take().unwrap()); 67 | assert_eq!(&r.read_to_new_ustring().unwrap(), "x"); 68 | let mut r = OwnedFd::from(child.stderr.take().unwrap()); 69 | assert_eq!(&r.read_to_new_ustring().unwrap(), "y"); 70 | 71 | assert_eq!(Fd::new(-1).raw(), -1); 72 | assert_eq!(*Fd::new(-1), -1); 73 | 74 | assert_eq!(Fd::new(-1), Fd::new(-1)); 75 | assert_eq!(OwnedFd::new(-1), OwnedFd::new(-1)); 76 | assert_eq!(Fd::new(-1), OwnedFd::new(-1)); 77 | assert_eq!(OwnedFd::new(-1), Fd::new(-1)); 78 | 79 | let (mut r, mut w) = pipe().unwrap(); 80 | assert_eq!(w.write(b"abc").unwrap(), 3); 81 | assert_eq!(w.borrow().write(b"abc").unwrap(), 3); 82 | assert_eq!(w.write_vectored(&[IoSlice::new(b"abc")]).unwrap(), 3); 83 | assert_eq!( 84 | w.borrow().write_vectored(&[IoSlice::new(b"abc")]).unwrap(), 85 | 3 86 | ); 87 | assert!(w.flush().is_ok()); 88 | assert!(w.borrow().flush().is_ok()); 89 | 90 | let mut buf = [0; 1]; 91 | assert_eq!(r.read(&mut buf).unwrap(), 1); 92 | assert_eq!(buf[0], b'a'); 93 | assert_eq!(r.borrow().read(&mut buf).unwrap(), 1); 94 | assert_eq!(buf[0], b'b'); 95 | assert_eq!( 96 | r.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 97 | 1 98 | ); 99 | assert_eq!(buf[0], b'c'); 100 | assert_eq!( 101 | r.borrow() 102 | .read_vectored(&mut [IoSliceMut::new(&mut buf)]) 103 | .unwrap(), 104 | 1 105 | ); 106 | assert_eq!(buf[0], b'a'); 107 | } 108 | -------------------------------------------------------------------------------- /uapi/tests/main/file/linux.rs: -------------------------------------------------------------------------------- 1 | use proc::*; 2 | use std::{ 3 | collections::HashSet, 4 | io::{IoSlice, IoSliceMut, Write}, 5 | }; 6 | use testutils::*; 7 | use uapi::{c::open_how, *}; 8 | 9 | #[test] 10 | fn read_write1() { 11 | let tmp = Tempdir::new(); 12 | 13 | let path1 = &*format!("{}/a", tmp); 14 | 15 | let file = open(path1, c::O_CREAT | c::O_RDWR, 0o777).unwrap(); 16 | 17 | fallocate(*file, 0, 1, 2).unwrap(); 18 | 19 | let xstat = fstat(*file).unwrap(); 20 | assert_eq!(xstat.st_size, 3); 21 | 22 | std::fs::write(path1, "abc").unwrap(); 23 | 24 | fallocate( 25 | *file, 26 | c::FALLOC_FL_PUNCH_HOLE | c::FALLOC_FL_KEEP_SIZE, 27 | 1, 28 | 1, 29 | ) 30 | .unwrap(); 31 | 32 | assert_eq!(std::fs::read_to_string(path1).unwrap(), "a\0c"); 33 | 34 | let f2 = dup(*file).unwrap(); 35 | assert_eq!(fcntl_getfd(*f2).unwrap() & c::FD_CLOEXEC, 0); 36 | 37 | dup3(*file, *f2, c::O_CLOEXEC).unwrap(); 38 | assert_eq!(fcntl_getfd(*f2).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 39 | 40 | pwritev2(*file, &[IoSlice::new(b"x")][..], 1, 0).unwrap(); 41 | 42 | assert_eq!(std::fs::read_to_string(path1).unwrap(), "axc"); 43 | 44 | pwritev2(*file, &[IoSlice::new(b"x")][..], 1, 0).unwrap(); 45 | 46 | assert_eq!(std::fs::read_to_string(path1).unwrap(), "axc"); 47 | 48 | let mut buf = [0; 128]; 49 | let mut iovec = [IoSliceMut::new(&mut buf)]; 50 | let buf = preadv2(*file, &mut iovec[..], 2, 0).unwrap(); 51 | assert_eq!(buf.iter().next(), Some(&b"c"[..])); 52 | 53 | assert!(statfs(path1).is_ok()); 54 | assert!(fstatfs(*file).is_ok()); 55 | } 56 | 57 | #[test_if(linux_4_16)] 58 | fn read_write2() { 59 | let tmp = Tempdir::new(); 60 | 61 | let path1 = &*format!("{}/a", tmp); 62 | 63 | let file = open(path1, c::O_CREAT | c::O_RDWR, 0o777).unwrap(); 64 | 65 | pwritev2(*file, &[IoSlice::new(b"a")][..], 0, 0).unwrap(); 66 | 67 | assert_eq!(std::fs::read_to_string(path1).unwrap(), "a"); 68 | 69 | pwritev2(*file, &[IoSlice::new(b"x")][..], 1, c::RWF_APPEND).unwrap(); 70 | 71 | assert_eq!(std::fs::read_to_string(path1).unwrap(), "ax"); 72 | 73 | write(*file, b"y").unwrap(); 74 | 75 | assert_eq!(std::fs::read_to_string(path1).unwrap(), "yx"); 76 | 77 | pwritev2(*file, &[IoSlice::new(b"y")][..], -1, c::RWF_APPEND).unwrap(); 78 | 79 | assert_eq!(std::fs::read_to_string(path1).unwrap(), "yxy"); 80 | } 81 | 82 | #[test] 83 | fn copy_file_range_() { 84 | let tmp = Tempdir::new(); 85 | 86 | let path1 = &*format!("{}/a", tmp); 87 | let path2 = &*format!("{}/b", tmp); 88 | 89 | let mut f1 = open(path1, c::O_CREAT | c::O_RDWR, 0).unwrap(); 90 | let mut f2 = open(path2, c::O_CREAT | c::O_RDWR, 0).unwrap(); 91 | 92 | f1.write_all(b"hello world").unwrap(); 93 | 94 | let n = copy_file_range(*f1, Some(&mut 3), *f2, Some(&mut 2), 5, 0).unwrap(); 95 | assert_eq!(n, 5); 96 | 97 | let res = f2.read_to_new_ustring().unwrap(); 98 | assert_eq!(&res, "\0\0lo wo"); 99 | } 100 | 101 | #[test] 102 | fn renameat_() { 103 | let tmp = Tempdir::new(); 104 | 105 | let dir = open(tmp.bstr(), c::O_PATH, 0).unwrap(); 106 | 107 | let path1 = "a"; 108 | let path2 = "b"; 109 | 110 | let f1 = openat(*dir, path1, c::O_CREAT | c::O_RDWR, 0).unwrap(); 111 | 112 | renameat2(*dir, path1, *dir, path2, 0).unwrap(); 113 | 114 | assert!(faccessat(*dir, path1, 0, 0).is_err()); 115 | assert!(faccessat(*dir, path2, 0, 0).is_ok()); 116 | 117 | assert_eq!( 118 | fstatat(*dir, path2, 0).unwrap().st_ino, 119 | fstat(*f1).unwrap().st_ino 120 | ); 121 | 122 | let f2 = openat(*dir, path1, c::O_CREAT | c::O_RDWR, 0).unwrap(); 123 | 124 | renameat2(*dir, path1, *dir, path2, c::RENAME_EXCHANGE).unwrap(); 125 | 126 | assert_eq!( 127 | fstatat(*dir, path2, 0).unwrap().st_ino, 128 | fstat(*f2).unwrap().st_ino 129 | ); 130 | assert_eq!( 131 | fstatat(*dir, path1, 0).unwrap().st_ino, 132 | fstat(*f1).unwrap().st_ino 133 | ); 134 | } 135 | 136 | #[test] 137 | fn splice_() { 138 | let (mut r, mut w) = pipe().unwrap(); 139 | 140 | let mut fd = memfd_create("", 0).unwrap(); 141 | 142 | w.write_all(b"abcdefghi").unwrap(); 143 | 144 | splice(*r, None, *fd, Some(&mut 3), 3, 0).unwrap(); 145 | 146 | drop(w); 147 | 148 | let res = r.read_to_new_ustring().unwrap(); 149 | assert_eq!(&res, "defghi"); 150 | 151 | let res = fd.read_to_new_ustring().unwrap(); 152 | assert_eq!(&res, "\0\0\0abc"); 153 | } 154 | 155 | #[test] 156 | fn tee_() { 157 | let (mut r1, mut w1) = pipe().unwrap(); 158 | let (mut r2, w2) = pipe().unwrap(); 159 | 160 | w1.write_all(b"abcdefghi").unwrap(); 161 | 162 | tee(*r1, *w2, 3, 0).unwrap(); 163 | 164 | drop(w1); 165 | drop(w2); 166 | 167 | let res = r1.read_to_new_ustring().unwrap(); 168 | assert_eq!(&res, "abcdefghi"); 169 | 170 | let res = r2.read_to_new_ustring().unwrap(); 171 | assert_eq!(&res, "abc"); 172 | } 173 | 174 | #[test] 175 | fn inotify() { 176 | let e = inotify_init1(0).unwrap(); 177 | assert_ne!(fcntl_getfd(*e).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 178 | 179 | let e = inotify_init1(c::IN_CLOEXEC | c::IN_NONBLOCK).unwrap(); 180 | assert_eq!(fcntl_getfd(*e).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 181 | 182 | let tmp = Tempdir::new(); 183 | let mut buf = [0; 128]; 184 | 185 | assert_eq!( 186 | inotify_read(*e, &mut buf[..]).err().unwrap(), 187 | Errno(c::EAGAIN) 188 | ); 189 | 190 | let w = inotify_add_watch(*e, tmp.bstr(), c::IN_CREATE).unwrap(); 191 | 192 | assert_eq!( 193 | inotify_read(*e, &mut buf[..]).err().unwrap(), 194 | Errno(c::EAGAIN) 195 | ); 196 | 197 | let path1 = &*format!("{}/a", tmp); 198 | let path2 = &*format!("{}/b", tmp); 199 | let path3 = &*format!("{}/c", tmp); 200 | 201 | open(path1, c::O_CREAT | c::O_RDONLY, 0).unwrap(); 202 | open(path2, c::O_CREAT | c::O_RDONLY, 0).unwrap(); 203 | 204 | let mut names = HashSet::new(); 205 | names.insert(ustr!("a")); 206 | names.insert(ustr!("b")); 207 | 208 | for ev in inotify_read(*e, &mut buf[..]).unwrap() { 209 | assert_eq!(ev.mask, c::IN_CREATE); 210 | assert_eq!(ev.wd, w); 211 | assert!(names.remove(ev.name().as_ustr())); 212 | } 213 | 214 | assert!(names.is_empty()); 215 | 216 | inotify_rm_watch(*e, w).unwrap(); 217 | 218 | let events: Vec = inotify_read(*e, &mut buf[..]) 219 | .unwrap() 220 | .into_iter() 221 | .collect(); 222 | assert_eq!(events.len(), 1); 223 | 224 | assert_eq!(events[0].mask, c::IN_IGNORED); 225 | assert_eq!(events[0].wd, w); 226 | 227 | open(path3, c::O_CREAT | c::O_RDONLY, 0).unwrap(); 228 | 229 | assert_eq!( 230 | inotify_read(*e, &mut buf[..]).err().unwrap(), 231 | Errno(c::EAGAIN) 232 | ); 233 | } 234 | 235 | #[test] 236 | fn sendfile_() { 237 | let mut m = memfd_create("", 0).unwrap(); 238 | let (mut r2, w2) = pipe().unwrap(); 239 | 240 | m.write_all(b"hello world").unwrap(); 241 | 242 | sendfile(*w2, *m, Some(&mut 3), 5).unwrap(); 243 | 244 | close(w2).unwrap(); 245 | 246 | assert_eq!(&r2.read_to_new_ustring().unwrap(), "lo wo"); 247 | } 248 | 249 | #[test] 250 | fn makedev_() { 251 | assert_eq!(makedev(22222, 333333), 87962295914005); 252 | assert_eq!(major(makedev(22222, 333333)), 22222); 253 | assert_eq!(minor(makedev(22222, 333333)), 333333); 254 | } 255 | 256 | #[test_if(linux_5_6)] 257 | fn openat2_() { 258 | let tmp = Tempdir::new(); 259 | let dir = &*format!("{}/a", tmp); 260 | mkdir(dir, 0o777).unwrap(); 261 | let dfd = open(dir, c::O_PATH, 0).unwrap(); 262 | { 263 | let mut how: open_how = pod_zeroed(); 264 | how.mode = 0o777; 265 | how.flags = (c::O_CREAT | c::O_WRONLY | c::O_CLOEXEC) as u64; 266 | let mut file = openat2(*dfd, "../b", &how).unwrap(); 267 | assert_eq!(fcntl_getfd(*file).unwrap(), c::FD_CLOEXEC); 268 | write!(file, "abc").unwrap(); 269 | } 270 | { 271 | let mut how: open_how = pod_zeroed(); 272 | how.flags = c::O_RDONLY as u64; 273 | let file = openat2(*dfd, "../b", &how).unwrap(); 274 | assert_eq!(fcntl_getfd(*file).unwrap(), 0); 275 | assert_eq!(read_file(file.into()), "abc"); 276 | } 277 | { 278 | let mut how: open_how = pod_zeroed(); 279 | how.flags = c::O_RDONLY as u64; 280 | how.resolve = c::RESOLVE_IN_ROOT; 281 | let err = openat2(*dfd, "../b", &how).unwrap_err(); 282 | assert_eq!(err.0, c::ENOENT); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /uapi/tests/main/ioctl/linux.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | #[cfg(any( 4 | target_arch = "x86", 5 | target_arch = "arm", 6 | target_arch = "s390x", 7 | target_arch = "x86_64", 8 | target_arch = "aarch64", 9 | target_arch = "riscv64" 10 | ))] 11 | mod test { 12 | use uapi::*; 13 | 14 | #[test] 15 | fn IOC() { 16 | assert_eq!( 17 | 2149668140, 18 | _IOC(_IOC_READ, c::UINPUT_IOCTL_BASE as _, 44, 33) 19 | ); 20 | } 21 | 22 | #[test] 23 | fn IO() { 24 | assert_eq!(16658, _IO(b'A' as _, 0x12)); 25 | } 26 | 27 | #[test] 28 | fn IOR() { 29 | assert_eq!(2147767597, _IOR::(c::UINPUT_IOCTL_BASE as _, 45)); 30 | } 31 | 32 | #[test] 33 | fn IOW() { 34 | assert_eq!(1074025828, _IOW::(c::UINPUT_IOCTL_BASE as _, 100)); 35 | } 36 | 37 | #[test] 38 | fn IOWR() { 39 | assert_eq!(3221776773, _IOWR::(b'i' as _, 0x85)) 40 | } 41 | 42 | #[test] 43 | fn IOC_DIR() { 44 | assert_eq!(_IOC_READ, _IOC_DIR(_IOC(_IOC_READ, b'a' as _, 65, 22))); 45 | assert_eq!(_IOC_WRITE, _IOC_DIR(_IOC(_IOC_WRITE, b'a' as _, 65, 22))); 46 | } 47 | 48 | #[test] 49 | fn IOC_TYPE() { 50 | assert_eq!(b'a' as u64, _IOC_TYPE(_IOC(_IOC_READ, b'a' as _, 65, 22))); 51 | } 52 | 53 | #[test] 54 | fn IOC_NR() { 55 | assert_eq!(65, _IOC_NR(_IOC(_IOC_READ, b'a' as _, 65, 22))); 56 | } 57 | 58 | #[test] 59 | fn IOC_SIZE() { 60 | assert_eq!(22, _IOC_SIZE(_IOC(_IOC_READ, b'a' as _, 65, 22))); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /uapi/tests/main/ioctl/mod.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | 3 | cfg_if! { 4 | if #[cfg(any(target_os = "linux", target_os = "android"))] { 5 | mod linux; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /uapi/tests/main/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | 3 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 4 | 5 | mod dir; 6 | mod errno; 7 | mod fcntl; 8 | mod fd; 9 | mod file; 10 | mod ioctl; 11 | mod mount; 12 | mod other; 13 | mod pod; 14 | mod poll; 15 | mod process; 16 | mod result; 17 | mod sched; 18 | mod signal; 19 | mod socket; 20 | mod timer; 21 | mod ustr; 22 | mod util; 23 | -------------------------------------------------------------------------------- /uapi/tests/main/mount/linux.rs: -------------------------------------------------------------------------------- 1 | use proc::*; 2 | use std::{ 3 | ffi::CStr, 4 | fs, 5 | fs::File, 6 | io::{BufRead, BufReader}, 7 | os::raw::c_int, 8 | path::Path, 9 | }; 10 | use tempfile::{tempdir, tempdir_in}; 11 | use testutils::Tempdir; 12 | use uapi::*; 13 | 14 | #[test_if(root)] 15 | fn mount_() { 16 | let tmp = Tempdir::new(); 17 | let path = format!("{}", tmp.bstr().display()); 18 | 19 | mount("dummy", tmp.bstr(), "tmpfs", 0, None).unwrap(); 20 | 21 | for line in BufReader::new(File::open("/proc/self/mountinfo").unwrap()).lines() { 22 | let line = line.unwrap(); 23 | if line.contains(&path) { 24 | let fields: Vec<_> = line.split(' ').collect(); 25 | assert_eq!(fields[3], "/"); 26 | assert_eq!(fields[4], path); 27 | assert_eq!(fields[8], "tmpfs"); 28 | } 29 | } 30 | 31 | let _fd = open(format!("{}/a", tmp), c::O_CREAT | c::O_RDONLY, 0).unwrap(); 32 | 33 | assert!(umount2(tmp.bstr(), 0).is_err()); 34 | 35 | assert!(umount2(tmp.bstr(), c::MNT_DETACH).is_ok()); 36 | } 37 | 38 | fn tmpfs() -> OwnedFd { 39 | let fs = fsopen(ustr!("tmpfs"), 0).unwrap(); 40 | fsconfig_cmd_create(*fs).unwrap(); 41 | fsmount(*fs, 0, 0).unwrap() 42 | } 43 | 44 | fn with_mount_point<'a, P: Into>, T, F: FnOnce(&Path) -> T>( 45 | base: P, 46 | f: F, 47 | ) -> T { 48 | struct Umount<'a>(&'a Path); 49 | 50 | impl<'a> Drop for Umount<'a> { 51 | fn drop(&mut self) { 52 | let _ = umount2(self.0, c::MNT_DETACH); 53 | } 54 | } 55 | 56 | let dir = match base.into() { 57 | Some(base) => tempdir_in(base), 58 | _ => tempdir(), 59 | } 60 | .unwrap(); 61 | let _umount = Umount(dir.path()); 62 | f(dir.path()) 63 | } 64 | 65 | fn with_private_mount T>(f: F) -> T { 66 | with_mount_point(None, |path| { 67 | let fs = tmpfs(); 68 | move_mount( 69 | *fs, 70 | Ustr::empty(), 71 | c::AT_FDCWD, 72 | path, 73 | c::MOVE_MOUNT_F_EMPTY_PATH, 74 | ) 75 | .unwrap(); 76 | mount(Ustr::empty(), path, Ustr::empty(), c::MS_PRIVATE, None).unwrap(); 77 | f(path) 78 | }) 79 | } 80 | 81 | fn create_file<'a>(dfd: c_int, p: impl IntoUstr<'a>) { 82 | openat(dfd, p, c::O_CREAT | c::O_RDONLY, 0).unwrap(); 83 | } 84 | 85 | #[test_if(root, linux_5_2)] 86 | fn move_mount1() { 87 | with_private_mount(|path| { 88 | with_mount_point(path, |p1| { 89 | with_mount_point(path, |p2| { 90 | const FILE: &str = "test"; 91 | 92 | let mnt = tmpfs(); 93 | create_file(*mnt, FILE); 94 | 95 | // move to p1 96 | move_mount( 97 | *mnt, 98 | Ustr::empty(), 99 | c::AT_FDCWD, 100 | p1, 101 | c::MOVE_MOUNT_F_EMPTY_PATH, 102 | ) 103 | .unwrap(); 104 | assert!(p1.join(FILE).exists()); 105 | 106 | // move from p1 to p2 107 | move_mount( 108 | *mnt, 109 | Ustr::empty(), 110 | c::AT_FDCWD, 111 | p2, 112 | c::MOVE_MOUNT_F_EMPTY_PATH, 113 | ) 114 | .unwrap(); 115 | assert!(!p1.join(FILE).exists()); 116 | assert!(p2.join(FILE).exists()); 117 | 118 | // open tree at p2 and move to p1 119 | let mnt = open_tree(c::AT_FDCWD, p2, 0).unwrap(); 120 | move_mount( 121 | *mnt, 122 | Ustr::empty(), 123 | c::AT_FDCWD, 124 | p1, 125 | c::MOVE_MOUNT_F_EMPTY_PATH, 126 | ) 127 | .unwrap(); 128 | assert!(p1.join(FILE).exists()); 129 | assert!(!p2.join(FILE).exists()); 130 | 131 | // move from p2 to p1 132 | move_mount(c::AT_FDCWD, p1, c::AT_FDCWD, p2, 0).unwrap(); 133 | assert!(!p1.join(FILE).exists()); 134 | assert!(p2.join(FILE).exists()); 135 | 136 | // clone tree at p2 and attach to p1 137 | let mnt = open_tree(c::AT_FDCWD, p2, c::OPEN_TREE_CLONE).unwrap(); 138 | move_mount( 139 | *mnt, 140 | Ustr::empty(), 141 | c::AT_FDCWD, 142 | p1, 143 | c::MOVE_MOUNT_F_EMPTY_PATH, 144 | ) 145 | .unwrap(); 146 | assert!(p1.join(FILE).exists()); 147 | assert!(p2.join(FILE).exists()); 148 | }); 149 | }); 150 | }); 151 | } 152 | 153 | #[test_if(root, linux_5_2)] 154 | fn fspick1() { 155 | with_mount_point(None, |p1| { 156 | let mnt = tmpfs(); 157 | move_mount( 158 | *mnt, 159 | Ustr::empty(), 160 | c::AT_FDCWD, 161 | p1, 162 | c::MOVE_MOUNT_F_EMPTY_PATH, 163 | ) 164 | .unwrap(); 165 | 166 | fs::create_dir(p1.join("a")).unwrap(); 167 | 168 | // make read-only 169 | let fs = fspick(c::AT_FDCWD, p1, 0).unwrap(); 170 | fsconfig_set_flag(*fs, CStr::from_bytes_with_nul(b"ro\0").unwrap()).unwrap(); 171 | fsconfig_cmd_reconfigure(*fs).unwrap(); 172 | 173 | assert!(fs::create_dir(p1.join("b")).is_err()); 174 | }); 175 | } 176 | 177 | #[test_if(root, linux_5_2)] 178 | fn fsmount1() { 179 | let fs = fsopen("proc", 0).unwrap(); 180 | fsconfig_cmd_create(*fs).unwrap(); 181 | let mnt = fsmount(*fs, 0, 0).unwrap(); 182 | 183 | // open /proc/cpuinfo 184 | openat(*mnt, "cpuinfo", c::O_RDONLY, 0).unwrap(); 185 | } 186 | -------------------------------------------------------------------------------- /uapi/tests/main/mount/mod.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | 3 | cfg_if! { 4 | if #[cfg(target_os = "linux")] { 5 | mod linux; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /uapi/tests/main/other/linux.rs: -------------------------------------------------------------------------------- 1 | use testutils::*; 2 | use uapi::*; 3 | 4 | #[test] 5 | fn eventfd_() { 6 | let fd = eventfd(0, 0).unwrap(); 7 | assert_ne!(fcntl_getfd(*fd).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 8 | 9 | let fd = eventfd(22, c::O_CLOEXEC).unwrap(); 10 | assert_eq!(fcntl_getfd(*fd).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 11 | 12 | eventfd_write(*fd, 11).unwrap(); 13 | assert_eq!(eventfd_read(*fd).unwrap(), 33); 14 | 15 | eventfd_write(*fd, 11).unwrap(); 16 | assert_eq!(eventfd_read(*fd).unwrap(), 11); 17 | 18 | let memfd = memfd_create("", 0).unwrap(); 19 | write(*memfd, &[1]).unwrap(); 20 | lseek(*memfd, 0, c::SEEK_SET).unwrap(); 21 | 22 | assert_eq!(eventfd_read(*memfd).err().unwrap(), Errno(c::EBADF)); 23 | } 24 | 25 | #[test] 26 | fn memfd() { 27 | let fd = memfd_create("", 0).unwrap(); 28 | assert_ne!(fcntl_getfd(*fd).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 29 | 30 | let fd = memfd_create("xyz", c::MFD_CLOEXEC as _).unwrap(); 31 | assert_eq!(fcntl_getfd(*fd).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 32 | 33 | let name = 34 | read_link_to_new_ustring(0, format_ustr!("/proc/self/fd/{}", *fd)).unwrap(); 35 | assert!(name.starts_with(b"/memfd:xyz")); 36 | 37 | write(*fd, &[1]).unwrap(); 38 | lseek(*fd, 0, c::SEEK_SET).unwrap(); 39 | let mut buf = [0]; 40 | read(*fd, &mut buf[..]).unwrap(); 41 | assert_eq!(buf[0], 1); 42 | } 43 | 44 | #[test] 45 | fn syncfs_() { 46 | let tmp = Tempdir::new(); 47 | 48 | assert!(syncfs(*open(&tmp, c::O_RDONLY, 0).unwrap()).is_ok()); 49 | assert!(syncfs(-1).is_err()); 50 | } 51 | 52 | #[test] 53 | fn pipe2_() { 54 | let (r, _) = pipe2(0).unwrap(); 55 | assert_ne!(fcntl_getfd(*r).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 56 | 57 | let (r, w) = pipe2(c::O_CLOEXEC).unwrap(); 58 | assert_eq!(fcntl_getfd(*r).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 59 | 60 | write(*w, &[1]).unwrap(); 61 | let mut buf = [0]; 62 | read(*r, &mut buf[..]).unwrap(); 63 | assert_eq!(buf[0], 1); 64 | } 65 | 66 | #[test] 67 | fn sysinfo_() { 68 | sysinfo().unwrap(); 69 | } 70 | -------------------------------------------------------------------------------- /uapi/tests/main/other/mod.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | cfg_if::cfg_if! { 4 | if #[cfg(target_os = "linux")] { 5 | mod linux; 6 | } 7 | } 8 | 9 | #[test] 10 | fn uname_() { 11 | let _res = uname().unwrap(); 12 | #[cfg(target_os = "linux")] 13 | assert_eq!(_res.sysname().as_ustr(), "Linux"); 14 | } 15 | 16 | #[test] 17 | fn sysconf_() { 18 | let res = sysconf(c::_SC_ARG_MAX).unwrap(); 19 | assert!(res >= 4096); 20 | } 21 | -------------------------------------------------------------------------------- /uapi/tests/main/pod.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | #[test] 4 | fn pod_read_() { 5 | let buf = [1u8, 0, 0, 0]; 6 | let i: i32 = pod_read(&buf[..]).unwrap(); 7 | assert_eq!(i.to_le(), 1); 8 | 9 | assert_eq!( 10 | pod_read::(&[0u8][..]).err().unwrap(), 11 | Errno(c::EINVAL) 12 | ); 13 | } 14 | 15 | #[test] 16 | fn pod_read_init_() { 17 | let buf = [3u8, 0, 0, 0, 0, 0]; 18 | let i: i32 = pod_read_init(&buf[..]).unwrap(); 19 | assert_eq!(i.to_le(), 3); 20 | 21 | assert_eq!( 22 | pod_read_init::(&[0u8][..]).err().unwrap(), 23 | Errno(c::EINVAL) 24 | ); 25 | } 26 | 27 | #[test] 28 | fn pod_write_() { 29 | let buf = [1u8, 0, 0, 0]; 30 | let mut i: i32 = 0; 31 | pod_write(&buf[..], &mut i).unwrap(); 32 | assert_eq!(i.to_le(), 1); 33 | 34 | assert_eq!( 35 | pod_write(&[0u8][..], &mut i).err().unwrap(), 36 | Errno(c::EINVAL) 37 | ); 38 | } 39 | 40 | #[test] 41 | fn pod_iter_() { 42 | let buf = [1u8, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]; 43 | assert_eq!( 44 | pod_iter::(&buf[..]) 45 | .unwrap() 46 | .map(|v| v.to_le()) 47 | .collect::>(), 48 | [1, 2, 3] 49 | ); 50 | 51 | assert_eq!( 52 | pod_iter::(&[0u8][..]).err().unwrap(), 53 | Errno(c::EINVAL) 54 | ); 55 | } 56 | 57 | #[test] 58 | fn as_bytes_() { 59 | assert_eq!( 60 | as_bytes(&[1u32.to_le(), 2, 3][..]), 61 | [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] 62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /uapi/tests/main/poll/linux.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use uapi::*; 3 | 4 | #[test] 5 | fn epoll() { 6 | let e = epoll_create1(0).unwrap(); 7 | assert_ne!(fcntl_getfd(*e).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 8 | 9 | let e = epoll_create1(c::EPOLL_CLOEXEC).unwrap(); 10 | assert_eq!(fcntl_getfd(*e).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 11 | 12 | let (r, w) = pipe().unwrap(); 13 | 14 | epoll_ctl( 15 | *e, 16 | c::EPOLL_CTL_ADD, 17 | *r, 18 | Some(&c::epoll_event { 19 | events: c::EPOLLIN as _, 20 | u64: 3, 21 | }), 22 | ) 23 | .unwrap(); 24 | 25 | let mut events = unsafe { [mem::zeroed()] }; 26 | 27 | assert_eq!(epoll_wait(*e, &mut events, 0).unwrap(), 0); 28 | 29 | write(*w, &[0]).unwrap(); 30 | 31 | assert_eq!(epoll_wait(*e, &mut events, 1000).unwrap(), 1); 32 | 33 | let events = events[0].events; 34 | assert_eq!(events, c::EPOLLIN as _); 35 | } 36 | -------------------------------------------------------------------------------- /uapi/tests/main/poll/mod.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | cfg_if::cfg_if! { 4 | if #[cfg(target_os = "linux")] { 5 | mod linux; 6 | } 7 | } 8 | 9 | #[test] 10 | fn poll_() { 11 | let (r, w) = pipe().unwrap(); 12 | 13 | let mut fds = [c::pollfd { 14 | fd: *r, 15 | events: c::POLLIN, 16 | revents: 0, 17 | }]; 18 | assert_eq!(poll(&mut fds, 0), Ok(0)); 19 | 20 | write(*w, &[0]).unwrap(); 21 | 22 | assert_eq!(poll(&mut fds, 0), Ok(1)); 23 | 24 | assert_eq!(fds[0].revents, c::POLLIN); 25 | } 26 | -------------------------------------------------------------------------------- /uapi/tests/main/process/linux.rs: -------------------------------------------------------------------------------- 1 | use libc::O_NONBLOCK; 2 | use proc::test_if; 3 | use std::io::{Read, Write}; 4 | use uapi::*; 5 | 6 | #[test] 7 | fn gettid_() { 8 | let tid = read_link_to_new_ustring(c::AT_FDCWD, "/proc/thread-self").unwrap(); 9 | 10 | assert_eq!(tid, format!("{}/task/{}", getpid(), gettid())); 11 | } 12 | 13 | #[test_if(linux_5_10)] 14 | fn pidfd_open_nonblock() { 15 | let pidfd = pidfd_open(getpid(), 0).unwrap(); 16 | assert!(fcntl_getfl(pidfd.raw()).unwrap() & O_NONBLOCK == 0); 17 | let pidfd = pidfd_open(getpid(), c::PIDFD_NONBLOCK).unwrap(); 18 | assert!(fcntl_getfl(pidfd.raw()).unwrap() & O_NONBLOCK != 0); 19 | let (mut read, write) = pipe().unwrap(); 20 | let mut write_clone = pidfd_getfd(pidfd.raw(), write.raw(), 0).unwrap(); 21 | assert!(write_clone.raw() != write.raw()); 22 | write!(write_clone, "ayo").unwrap(); 23 | drop(write_clone); 24 | drop(write); 25 | let mut s = String::new(); 26 | read.read_to_string(&mut s).unwrap(); 27 | assert_eq!(s, "ayo"); 28 | } 29 | -------------------------------------------------------------------------------- /uapi/tests/main/process/mod.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | cfg_if::cfg_if! { 4 | if #[cfg(target_os = "linux")] { 5 | mod linux; 6 | } 7 | } 8 | 9 | #[test] 10 | fn clock() { 11 | let mut ts = pod_zeroed(); 12 | clock_gettime(c::CLOCK_MONOTONIC, &mut ts).unwrap(); 13 | clock_getres(c::CLOCK_MONOTONIC, &mut ts).unwrap(); 14 | } 15 | -------------------------------------------------------------------------------- /uapi/tests/main/result.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | #[test] 4 | fn result() { 5 | let e = Errno(1); 6 | 7 | assert_eq!(format!("{}", e), "1"); 8 | 9 | assert_eq!(std::io::Error::from(e).raw_os_error(), Some(1)); 10 | 11 | set_errno(33); 12 | 13 | assert_eq!(Errno::default(), Errno(33)); 14 | } 15 | -------------------------------------------------------------------------------- /uapi/tests/main/sched/linux.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | #[test] 4 | fn sched_get_priority() { 5 | let min = sched_get_priority_min(c::SCHED_FIFO).unwrap(); 6 | let max = sched_get_priority_max(c::SCHED_FIFO).unwrap(); 7 | assert!(max - min + 1 >= 32); 8 | } 9 | -------------------------------------------------------------------------------- /uapi/tests/main/sched/mod.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | cfg_if::cfg_if! { 4 | if #[cfg(target_os = "linux")] { 5 | mod linux; 6 | } 7 | } 8 | 9 | #[test] 10 | fn sched_yield_() { 11 | sched_yield().unwrap(); 12 | } 13 | -------------------------------------------------------------------------------- /uapi/tests/main/signal/mod.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | #[test] 4 | fn sigset() { 5 | let mut set = empty_sig_set().unwrap(); 6 | 7 | assert!(!sigismember(&set, c::SIGUSR1).unwrap()); 8 | assert!(!sigismember(&set, c::SIGUSR2).unwrap()); 9 | 10 | sigfillset(&mut set).unwrap(); 11 | 12 | assert!(sigismember(&set, c::SIGUSR1).unwrap()); 13 | assert!(sigismember(&set, c::SIGUSR2).unwrap()); 14 | 15 | sigemptyset(&mut set).unwrap(); 16 | 17 | assert!(!sigismember(&set, c::SIGUSR1).unwrap()); 18 | assert!(!sigismember(&set, c::SIGUSR2).unwrap()); 19 | 20 | sigaddset(&mut set, c::SIGUSR1).unwrap(); 21 | 22 | assert!(sigismember(&set, c::SIGUSR1).unwrap()); 23 | assert!(!sigismember(&set, c::SIGUSR2).unwrap()); 24 | 25 | sigaddset(&mut set, c::SIGUSR2).unwrap(); 26 | 27 | assert!(sigismember(&set, c::SIGUSR1).unwrap()); 28 | assert!(sigismember(&set, c::SIGUSR2).unwrap()); 29 | 30 | sigdelset(&mut set, c::SIGUSR1).unwrap(); 31 | 32 | assert!(!sigismember(&set, c::SIGUSR1).unwrap()); 33 | assert!(sigismember(&set, c::SIGUSR2).unwrap()); 34 | 35 | sigdelset(&mut set, c::SIGUSR2).unwrap(); 36 | 37 | assert!(!sigismember(&set, c::SIGUSR1).unwrap()); 38 | assert!(!sigismember(&set, c::SIGUSR2).unwrap()); 39 | } 40 | -------------------------------------------------------------------------------- /uapi/tests/main/socket/cmsg.rs: -------------------------------------------------------------------------------- 1 | use std::{mem, mem::MaybeUninit}; 2 | use uapi::*; 3 | 4 | #[test] 5 | fn test() { 6 | let mut buf = [MaybeUninit::uninit(); 1024]; 7 | let hdr = pod_zeroed::(); 8 | let mut written = 0; 9 | 10 | { 11 | let mut buf = &mut buf[..]; 12 | 13 | written += cmsg_write(&mut buf, hdr, b"hello world").unwrap(); 14 | written += cmsg_write(&mut buf, hdr, b"ayo hol up").unwrap(); 15 | } 16 | 17 | let mut buf = unsafe { buf[..written].slice_assume_init_ref() }; 18 | 19 | let (_, _, data1) = cmsg_read(&mut buf).unwrap(); 20 | let (_, _, data2) = cmsg_read(&mut buf).unwrap(); 21 | 22 | assert_eq!(data1, b"hello world"); 23 | assert_eq!(data2, b"ayo hol up"); 24 | } 25 | 26 | #[test] 27 | fn invalid() { 28 | let mut hdr = pod_zeroed::(); 29 | hdr.cmsg_len = -200i16 as _; 30 | 31 | assert_eq!(cmsg_read(&mut &[][..]).err().unwrap(), Errno(c::EINVAL)); 32 | assert_eq!( 33 | cmsg_read(&mut &[0u8; mem::size_of::()][..]) 34 | .err() 35 | .unwrap(), 36 | Errno(c::EINVAL) 37 | ); 38 | assert_eq!( 39 | cmsg_write(&mut &mut [][..], hdr, &[0u8]).err().unwrap(), 40 | Errno(c::EINVAL) 41 | ); 42 | 43 | hdr.cmsg_len = -1i8 as _; 44 | assert_eq!( 45 | cmsg_write(&mut &mut [][..], hdr, &[0u8]).err().unwrap(), 46 | Errno(c::EINVAL) 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /uapi/tests/main/socket/linux.rs: -------------------------------------------------------------------------------- 1 | use testutils::*; 2 | use uapi::*; 3 | 4 | #[test] 5 | fn accept4_() { 6 | let tmp = Tempdir::new(); 7 | 8 | let server_path = format!("{}/server", tmp); 9 | 10 | let server_addr = super::addr(&server_path); 11 | 12 | let server = socket(c::AF_UNIX, c::SOCK_STREAM, 0).unwrap(); 13 | bind(*server, &server_addr).unwrap(); 14 | listen(*server, 128).unwrap(); 15 | 16 | let thread = std::thread::spawn(move || { 17 | for _ in 0..2 { 18 | let client = socket(c::AF_UNIX, c::SOCK_STREAM, 0).unwrap(); 19 | connect(*client, &server_addr).unwrap(); 20 | } 21 | }); 22 | 23 | let client = accept4(*server, sockaddr_none_mut(), 0).unwrap().0; 24 | assert_ne!(fcntl_getfd(*client).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 25 | 26 | let client = accept4(*server, sockaddr_none_mut(), c::SOCK_CLOEXEC) 27 | .unwrap() 28 | .0; 29 | assert_eq!(fcntl_getfd(*client).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 30 | 31 | thread.join().unwrap(); 32 | } 33 | -------------------------------------------------------------------------------- /uapi/tests/main/socket/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashSet, 3 | io::{IoSlice, IoSliceMut, Write}, 4 | mem, 5 | mem::MaybeUninit, 6 | thread, 7 | }; 8 | use testutils::*; 9 | use uapi::*; 10 | 11 | cfg_if::cfg_if! { 12 | if #[cfg(target_os = "linux")] { 13 | mod linux; 14 | } 15 | } 16 | 17 | mod cmsg; 18 | mod sockopt; 19 | 20 | fn addr(s: &str) -> c::sockaddr_un { 21 | let mut addr: c::sockaddr_un = pod_zeroed(); 22 | addr.sun_family = c::AF_UNIX as _; 23 | pod_write(s.as_bytes(), &mut addr.sun_path[..s.len()]).unwrap(); 24 | addr 25 | } 26 | 27 | #[test] 28 | fn socket1() { 29 | let tmp = Tempdir::new(); 30 | let server_path = &*format!("{}/server", tmp); 31 | let client_path = &*format!("{}/client", tmp); 32 | 33 | let server_addr = addr(server_path); 34 | let client_addr = addr(client_path); 35 | 36 | let server = { 37 | let fd = socket(c::AF_UNIX, c::SOCK_STREAM, 0).unwrap(); 38 | bind(*fd, &server_addr).unwrap(); 39 | listen(*fd, 128).unwrap(); 40 | fd 41 | }; 42 | 43 | let thread = thread::spawn(move || { 44 | let mut client = socket(c::AF_UNIX, c::SOCK_STREAM, 0).unwrap(); 45 | bind(*client, &client_addr).unwrap(); 46 | connect(*client, &server_addr).unwrap(); 47 | 48 | let mut pa: c::sockaddr_un = pod_zeroed(); 49 | getpeername(*client, &mut pa).unwrap(); 50 | cmp_addr_un(&pa, &server_addr); 51 | 52 | let mut sa: c::sockaddr_un = pod_zeroed(); 53 | getsockname(*client, &mut sa).unwrap(); 54 | cmp_addr_un(&sa, &client_addr); 55 | 56 | send(*client, b"hello world", 0).unwrap(); 57 | shutdown(*client, c::SHUT_WR).unwrap(); 58 | 59 | assert_eq!(&client.read_to_new_ustring().unwrap(), "hol up"); 60 | }); 61 | 62 | let mut accepted_client_addr: c::sockaddr_un = pod_zeroed(); 63 | let (mut client, addr_size) = 64 | accept(*server, Some(&mut accepted_client_addr)).unwrap(); 65 | 66 | assert!(addr_size <= mem::size_of::()); 67 | cmp_addr_un(&accepted_client_addr, &client_addr); 68 | 69 | let mut buf = [0u8; 128]; 70 | let buf = recv(*client, &mut buf[..], 0).unwrap(); 71 | assert_eq!(buf, b"hello world"); 72 | client.write_all(b"hol up").unwrap(); 73 | shutdown(*client, c::SHUT_WR).unwrap(); 74 | 75 | thread.join().unwrap(); 76 | } 77 | 78 | fn cmp_addr_un(a: &c::sockaddr_un, b: &c::sockaddr_un) { 79 | assert_eq!(a.sun_family, b.sun_family); 80 | assert_eq!(&a.sun_path[..], &b.sun_path[..]); 81 | } 82 | 83 | #[test] 84 | fn socket2() { 85 | let tmp = Tempdir::new(); 86 | let server_path = &*format!("{}/server", tmp); 87 | let client_path = &*format!("{}/client", tmp); 88 | 89 | let server_addr = addr(server_path); 90 | let client_addr = addr(client_path); 91 | 92 | let server = { 93 | let fd = socket(c::AF_UNIX, c::SOCK_DGRAM, 0).unwrap(); 94 | bind(*fd, &server_addr).unwrap(); 95 | fd 96 | }; 97 | 98 | { 99 | let client = socket(c::AF_UNIX, c::SOCK_DGRAM, 0).unwrap(); 100 | bind(*client, &client_addr).unwrap(); 101 | let msghdr = Msghdr { 102 | iov: &[IoSlice::new(b"hello world")][..], 103 | control: msghdr_control_none_ref(), 104 | name: Some(&server_addr), 105 | }; 106 | sendmsg(*client, &msghdr, 0).unwrap(); 107 | } 108 | 109 | { 110 | let mut buf = [0; 128]; 111 | let mut accepted_client_addr: c::sockaddr_un = pod_zeroed(); 112 | let mut msghdr = MsghdrMut { 113 | iov: &mut [IoSliceMut::new(&mut buf)][..], 114 | control: msghdr_control_none_mut(), 115 | name: Some(&mut accepted_client_addr), 116 | flags: 0, 117 | }; 118 | let (buf, addr_size, _) = recvmsg(*server, &mut msghdr, 0).unwrap(); 119 | let mut buf = buf.iter(); 120 | assert_eq!(buf.next(), Some("hello world".as_bytes())); 121 | assert_eq!(buf.next(), None); 122 | assert!(addr_size <= mem::size_of::()); 123 | cmp_addr_un(&accepted_client_addr, &client_addr); 124 | } 125 | 126 | unlink(client_path).unwrap(); 127 | 128 | { 129 | let client = socket(c::AF_UNIX, c::SOCK_DGRAM, 0).unwrap(); 130 | bind(*client, &client_addr).unwrap(); 131 | sendto(*client, b"ayo", 0, &server_addr).unwrap(); 132 | } 133 | 134 | { 135 | let mut buf = [MaybeUninit::::uninit(); 128]; 136 | let mut accepted_client_addr: c::sockaddr_un = pod_zeroed(); 137 | let (buf, addr_size) = 138 | recvfrom(*server, &mut buf[..], 0, &mut accepted_client_addr).unwrap(); 139 | assert_eq!(buf, b"ayo"); 140 | assert!(addr_size <= mem::size_of::()); 141 | cmp_addr_un(&accepted_client_addr, &client_addr); 142 | } 143 | } 144 | 145 | #[test] 146 | fn cmsg1() { 147 | let tmp = Tempdir::new(); 148 | let f1 = open(format!("{}/a", tmp), c::O_CREAT | c::O_RDONLY, 0).unwrap(); 149 | let f2 = open(format!("{}/b", tmp), c::O_CREAT | c::O_RDONLY, 0).unwrap(); 150 | #[cfg(not(any(target_os = "macos", target_os = "openbsd")))] 151 | let f3 = open(format!("{}/c", tmp), c::O_CREAT | c::O_RDONLY, 0).unwrap(); 152 | 153 | let mut inos: HashSet = [ 154 | *f1, 155 | *f2, 156 | #[cfg(not(any(target_os = "macos", target_os = "openbsd")))] 157 | *f3, 158 | ] 159 | .iter() 160 | .map(|f| fstat(*f).unwrap()) 161 | .map(|s| s.st_ino) 162 | .collect(); 163 | 164 | let (a, b) = socketpair(c::AF_UNIX, c::SOCK_DGRAM, 0).unwrap(); 165 | 166 | #[cfg(any(target_os = "android", target_os = "linux"))] 167 | setsockopt(*b, c::SOL_SOCKET, c::SO_PASSCRED, &1i32).unwrap(); 168 | 169 | { 170 | let mut buf = [MaybeUninit::uninit(); 128]; 171 | let len = { 172 | let mut buf = &mut buf[..]; 173 | 174 | let mut hdr: c::cmsghdr = pod_zeroed(); 175 | hdr.cmsg_level = c::SOL_SOCKET; 176 | hdr.cmsg_type = c::SCM_RIGHTS; 177 | 178 | let mut len = 0; 179 | len += cmsg_write(&mut buf, hdr, &[*f1, *f2]).unwrap(); 180 | cfg_if::cfg_if! { 181 | if #[cfg(not(any(target_os = "macos", target_os = "openbsd")))] { 182 | len += cmsg_write(&mut buf, hdr, &[*f3]).unwrap(); 183 | } 184 | } 185 | len 186 | }; 187 | 188 | let msghdr = Msghdr { 189 | iov: &[IoSlice::new(b"hello world")][..], 190 | control: Some(&buf[..len]), 191 | name: sockaddr_none_ref(), 192 | }; 193 | sendmsg(*a, &msghdr, 0).unwrap(); 194 | } 195 | 196 | { 197 | let mut data_buf = [0; 128]; 198 | let mut cmsg_buf = [0; 128]; 199 | let mut msghdr = MsghdrMut { 200 | iov: &mut [IoSliceMut::new(&mut data_buf)][..], 201 | control: Some(&mut cmsg_buf[..]), 202 | name: sockaddr_none_mut(), 203 | flags: 0, 204 | }; 205 | 206 | let (data, _, mut cmsg) = recvmsg(*b, &mut msghdr, 0).unwrap(); 207 | let mut data = data.iter(); 208 | 209 | assert_eq!(data.next(), Some("hello world".as_bytes())); 210 | assert_eq!(data.next(), None); 211 | 212 | assert!(cmsg.len() > 0); 213 | 214 | #[cfg(any(target_os = "android", target_os = "linux"))] 215 | let mut saw_cred = false; 216 | 217 | while cmsg.len() > 0 { 218 | let (_, hdr, data) = cmsg_read(&mut cmsg).unwrap(); 219 | 220 | match (hdr.cmsg_level, hdr.cmsg_type) { 221 | (c::SOL_SOCKET, c::SCM_RIGHTS) => { 222 | let data: Vec<_> = pod_iter::(data) 223 | .unwrap() 224 | .map(OwnedFd::new) 225 | .collect(); 226 | for fd in data { 227 | assert!(inos.remove(&fstat(*fd).unwrap().st_ino)); 228 | } 229 | } 230 | #[cfg(any(target_os = "android", target_os = "linux"))] 231 | (c::SOL_SOCKET, c::SCM_CREDENTIALS) => { 232 | let data: c::ucred = pod_read(data).unwrap(); 233 | assert_eq!(data.pid, getpid()); 234 | assert_eq!(data.uid, geteuid()); 235 | assert_eq!(data.gid, getegid()); 236 | saw_cred = true; 237 | } 238 | _ => panic!("unexpected cmsg_level"), 239 | } 240 | } 241 | 242 | #[cfg(any(target_os = "android", target_os = "linux"))] 243 | assert!(saw_cred); 244 | 245 | assert!(inos.is_empty()); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /uapi/tests/main/socket/sockopt.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_imports)] 2 | use proc::*; 3 | use std::mem; 4 | use uapi::*; 5 | 6 | #[test] 7 | fn linger() { 8 | let socket = socket(c::AF_INET, c::SOCK_STREAM, 0).unwrap(); 9 | 10 | let linger = c::linger { 11 | l_onoff: 0, 12 | l_linger: 0, 13 | }; 14 | setsockopt(*socket, c::SOL_SOCKET, c::SO_LINGER, &linger).unwrap(); 15 | 16 | let mut linger: c::linger = pod_zeroed(); 17 | assert_eq!( 18 | getsockopt(*socket, c::SOL_SOCKET, c::SO_LINGER, &mut linger).unwrap(), 19 | mem::size_of::() 20 | ); 21 | assert_eq!(linger.l_onoff, 0); 22 | 23 | let linger = c::linger { 24 | l_onoff: 1, 25 | l_linger: 0, 26 | }; 27 | setsockopt(*socket, c::SOL_SOCKET, c::SO_LINGER, &linger).unwrap(); 28 | 29 | let mut linger: c::linger = pod_zeroed(); 30 | getsockopt(*socket, c::SOL_SOCKET, c::SO_LINGER, &mut linger).unwrap(); 31 | assert_ne!(linger.l_onoff, 0); 32 | 33 | let linger = c::linger { 34 | l_onoff: 0, 35 | l_linger: 0, 36 | }; 37 | setsockopt(*socket, c::SOL_SOCKET, c::SO_LINGER, &linger).unwrap(); 38 | 39 | let mut linger: c::linger = pod_zeroed(); 40 | getsockopt(*socket, c::SOL_SOCKET, c::SO_LINGER, &mut linger).unwrap(); 41 | assert_eq!(linger.l_onoff, 0); 42 | } 43 | -------------------------------------------------------------------------------- /uapi/tests/main/timer/linux.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | #[test] 4 | fn timerfd() { 5 | { 6 | let e = timerfd_create(c::CLOCK_MONOTONIC, 0).unwrap(); 7 | assert_ne!(fcntl_getfd(*e).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 8 | } 9 | 10 | { 11 | let e = timerfd_create(c::CLOCK_MONOTONIC, c::TFD_CLOEXEC).unwrap(); 12 | assert_eq!(fcntl_getfd(*e).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 13 | 14 | let time = c::itimerspec { 15 | it_interval: c::timespec { 16 | tv_sec: 1000, 17 | tv_nsec: 2000, 18 | }, 19 | it_value: c::timespec { 20 | tv_sec: 3000, 21 | tv_nsec: 4000, 22 | }, 23 | }; 24 | let time2 = c::itimerspec { 25 | it_interval: c::timespec { 26 | tv_sec: 5000, 27 | tv_nsec: 6000, 28 | }, 29 | it_value: c::timespec { 30 | tv_sec: 7000, 31 | tv_nsec: 8000, 32 | }, 33 | }; 34 | 35 | timerfd_settime(e.raw(), 0, &time).unwrap(); 36 | let old = timerfd_settime(e.raw(), 0, &time2).unwrap(); 37 | assert_eq!(time.it_interval.tv_sec, old.it_interval.tv_sec); 38 | assert_eq!(time.it_interval.tv_nsec, old.it_interval.tv_nsec); 39 | 40 | let new = timerfd_gettime(e.raw()).unwrap(); 41 | assert_eq!(time2.it_interval.tv_sec, new.it_interval.tv_sec); 42 | assert_eq!(time2.it_interval.tv_nsec, new.it_interval.tv_nsec); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /uapi/tests/main/timer/mod.rs: -------------------------------------------------------------------------------- 1 | cfg_if::cfg_if! { 2 | if #[cfg(target_os = "linux")] { 3 | mod linux; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /uapi/tests/main/util.rs: -------------------------------------------------------------------------------- 1 | use testutils::*; 2 | use uapi::*; 3 | 4 | #[test] 5 | fn read_link() { 6 | let tmp = Tempdir::new(); 7 | 8 | let path = &*format!("{}/a", tmp); 9 | let path2 = &*format!("{}/b", tmp); 10 | 11 | let mut link = "x".to_string(); 12 | for _ in 0..5 { 13 | link.push_str(&link.clone()); 14 | } 15 | link.pop(); 16 | 17 | symlink(&*link, path).unwrap(); 18 | open(path2, c::O_CREAT | c::O_RDONLY, 0).unwrap(); 19 | 20 | assert_eq!(&read_link_to_new_ustring(c::AT_FDCWD, path).unwrap(), &link); 21 | assert_eq!( 22 | &read_link_to_new_ustring(*open(tmp.bstr(), c::O_RDONLY, 0).unwrap(), "a") 23 | .unwrap(), 24 | &link 25 | ); 26 | assert_eq!( 27 | read_link_to_new_ustring(c::AT_FDCWD, path2).err().unwrap(), 28 | Errno(c::EINVAL) 29 | ); 30 | 31 | let mut s = "xyz".to_string().into(); 32 | assert_eq!( 33 | read_link_to_ustring(c::AT_FDCWD, path, &mut s).unwrap(), 34 | link.len() 35 | ); 36 | assert_eq!( 37 | read_link_to_ustring(c::AT_FDCWD, path2, &mut s) 38 | .err() 39 | .unwrap(), 40 | Errno(c::EINVAL) 41 | ); 42 | 43 | assert_eq!(s, format!("xyz{}", link)); 44 | } 45 | -------------------------------------------------------------------------------- /uapi/tests/mknod_mode.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | use proc::*; 4 | use std::{fs::metadata, os::unix::fs::PermissionsExt}; 5 | use testutils::*; 6 | use uapi::*; 7 | 8 | #[test_if(root)] 9 | fn mknod1() { 10 | const MODE: c::mode_t = 0o712; 11 | 12 | let f = || { 13 | let tmp = Tempdir::new(); 14 | let path = format_ustr!("{}/a", tmp); 15 | mknod(&path, c::S_IFCHR | MODE, 12).unwrap(); 16 | let stat = stat(&path).unwrap(); 17 | assert_eq!(stat.st_rdev, 12); 18 | metadata(path.as_path()).unwrap().permissions().mode() & 0o777 19 | }; 20 | 21 | umask(0); 22 | assert_eq!(f() as c::mode_t, MODE); 23 | umask(0o077); 24 | assert_eq!(f(), 0o700); 25 | } 26 | -------------------------------------------------------------------------------- /uapi/tests/mknodat_mode.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | #[cfg(not(target_os = "macos"))] 4 | mod wrapper { 5 | use proc::*; 6 | use std::{fs::metadata, os::unix::fs::PermissionsExt}; 7 | use testutils::*; 8 | use uapi::*; 9 | 10 | #[test_if(root)] 11 | fn mknodat1() { 12 | const MODE: c::mode_t = 0o712; 13 | const DEV: c::dev_t = 999; 14 | 15 | let f = || { 16 | let tmp = Tempdir::new(); 17 | let fd = open(&tmp, c::O_RDONLY, 0).unwrap(); 18 | let path = format_ustr!("{}/a", tmp); 19 | mknodat(*fd, "a", c::S_IFCHR | MODE, DEV).unwrap(); 20 | let stat = stat(&path).unwrap(); 21 | assert_eq!(stat.st_rdev, DEV); 22 | assert_eq!(stat.st_mode & c::S_IFMT, c::S_IFCHR); 23 | metadata(path.as_path()).unwrap().permissions().mode() & 0o777 24 | }; 25 | 26 | umask(0); 27 | assert_eq!(f() as c::mode_t, MODE); 28 | umask(0o077); 29 | assert_eq!(f() as c::mode_t, 0o700); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /uapi/tests/open_mode.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::metadata, os::unix::fs::PermissionsExt}; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | #[test] 6 | fn open1() { 7 | const MODE: c::mode_t = 0o712; 8 | 9 | let f = || { 10 | let tmp = Tempdir::new(); 11 | let path = format_ustr!("{}/a", tmp); 12 | open(&path, c::O_CREAT | c::O_RDONLY, MODE).unwrap(); 13 | metadata(path.as_path()).unwrap().permissions().mode() & 0o777 14 | }; 15 | 16 | umask(0); 17 | assert_eq!(f() as c::mode_t, MODE); 18 | umask(0o077); 19 | assert_eq!(f(), 0o700); 20 | } 21 | -------------------------------------------------------------------------------- /uapi/tests/openat2_mode.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | #[cfg(target_os = "linux")] 4 | mod wrapper { 5 | use proc::test_if; 6 | 7 | #[test_if(linux_5_6)] 8 | fn openat2_() { 9 | use std::{fs::metadata, os::unix::fs::PermissionsExt}; 10 | use testutils::*; 11 | use uapi::{c::open_how, *}; 12 | 13 | const MODE: c::mode_t = 0o712; 14 | 15 | let f = || { 16 | let tmp = Tempdir::new(); 17 | let dir = open(&tmp, c::O_RDONLY, 0).unwrap(); 18 | let mut how: open_how = pod_zeroed(); 19 | how.mode = MODE as _; 20 | how.flags = (c::O_CREAT | c::O_RDONLY) as u64; 21 | openat2(*dir, "a", &how).unwrap(); 22 | metadata(format_ustr!("{}/a", tmp).as_path()) 23 | .unwrap() 24 | .permissions() 25 | .mode() 26 | & 0o777 27 | }; 28 | 29 | umask(0); 30 | assert_eq!(f() as c::mode_t, MODE); 31 | umask(0o077); 32 | assert_eq!(f(), 0o700); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /uapi/tests/openat_mode.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::metadata, os::unix::fs::PermissionsExt}; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | #[test] 6 | fn openat1() { 7 | const MODE: c::mode_t = 0o712; 8 | 9 | let f = || { 10 | let tmp = Tempdir::new(); 11 | let dir = open(&tmp, c::O_RDONLY, 0).unwrap(); 12 | openat(*dir, "a", c::O_CREAT | c::O_RDONLY, MODE).unwrap(); 13 | metadata(format_ustr!("{}/a", tmp).as_path()) 14 | .unwrap() 15 | .permissions() 16 | .mode() 17 | & 0o777 18 | }; 19 | 20 | umask(0); 21 | assert_eq!(f() as c::mode_t, MODE); 22 | umask(0o077); 23 | assert_eq!(f(), 0o700); 24 | } 25 | -------------------------------------------------------------------------------- /uapi/tests/pivot_root.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "linux")] 2 | mod wrapper { 3 | use testutils::*; 4 | use uapi::*; 5 | 6 | #[ignore] // better not remove this 7 | // #[test_if(root)] 8 | #[test] 9 | fn pivot_root_() { 10 | let tmp = Tempdir::new(); 11 | 12 | let sub1 = &*format!("{}/a", tmp); 13 | let sub2 = &*format!("{}/a/b", tmp); 14 | 15 | mount("", tmp.bstr(), "tmpfs", 0, None).unwrap(); 16 | defer!(|| umount2(tmp.bstr(), 0).unwrap()); 17 | 18 | mount("", tmp.bstr(), Ustr::null(), c::MS_PRIVATE, None).unwrap(); 19 | 20 | mkdir(sub1, 0).unwrap(); 21 | 22 | mount("", sub1, "tmpfs", 0, None).unwrap(); 23 | defer!(|| umount2(sub1, 0).unwrap()); 24 | 25 | mkdir(sub2, 0).unwrap(); 26 | 27 | pivot_root(sub1, sub2).unwrap(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /uapi/tests/process1.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | #[test] 6 | fn process1() { 7 | let ppid = getpid(); 8 | 9 | match unsafe { fork().unwrap() } { 10 | 0 => in_fork(|| { 11 | assert_ne!(getpid(), ppid); 12 | assert_eq!(getppid(), ppid); 13 | exit(0); 14 | }), 15 | n => { 16 | let (pid, status) = wait().unwrap(); 17 | assert_eq!(n, pid); 18 | assert_eq!(WIFEXITED(status), true); 19 | assert_eq!(WEXITSTATUS(status), 0); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /uapi/tests/process2.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | #[test] 6 | fn process2() { 7 | match unsafe { fork().unwrap() } { 8 | 0 => in_fork(|| exit(0)), 9 | _ => { 10 | let (_, status) = wait().unwrap(); 11 | assert_eq!(WIFEXITED(status), true); 12 | assert_eq!(WIFSIGNALED(status), false); 13 | assert_eq!(WIFSTOPPED(status), false); 14 | assert_eq!(WIFCONTINUED(status), false); 15 | assert_eq!(WEXITSTATUS(status), 0); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /uapi/tests/process3.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | #[test] 6 | fn process3() { 7 | match unsafe { fork().unwrap() } { 8 | 0 => in_fork(|| exit(1)), 9 | _ => { 10 | let (_, status) = wait().unwrap(); 11 | assert_eq!(WIFEXITED(status), true); 12 | assert_eq!(WIFSIGNALED(status), false); 13 | assert_eq!(WIFSTOPPED(status), false); 14 | assert_eq!(WIFCONTINUED(status), false); 15 | assert_eq!(WEXITSTATUS(status), 1); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /uapi/tests/process4.rs: -------------------------------------------------------------------------------- 1 | use testutils::*; 2 | use uapi::*; 3 | 4 | #[test] 5 | fn process4() { 6 | match unsafe { fork().unwrap() } { 7 | 0 => in_fork(|| { 8 | raise(c::SIGINT).unwrap(); 9 | unreachable!(); 10 | }), 11 | _ => { 12 | let (_, status) = wait().unwrap(); 13 | assert_eq!(WIFEXITED(status), false); 14 | assert_eq!(WIFSIGNALED(status), true); 15 | assert_eq!(WIFSTOPPED(status), false); 16 | assert_eq!(WIFCONTINUED(status), false); 17 | assert_eq!(WTERMSIG(status), c::SIGINT); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /uapi/tests/process5.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | #[test] 6 | fn process5() { 7 | match unsafe { fork().unwrap() } { 8 | 0 => in_fork(|| { 9 | raise(c::SIGSTOP).unwrap(); 10 | exit(1); 11 | }), 12 | n => { 13 | let (pid, status) = waitpid(n, c::WUNTRACED).unwrap(); 14 | assert_eq!(pid, n); 15 | assert_eq!(WIFEXITED(status), false); 16 | assert_eq!(WIFSIGNALED(status), false); 17 | assert_eq!(WIFSTOPPED(status), true); 18 | assert_eq!(WIFCONTINUED(status), false); 19 | assert_eq!(WSTOPSIG(status), c::SIGSTOP); 20 | 21 | kill(n, c::SIGCONT).unwrap(); 22 | 23 | let (pid, status) = waitpid(n, c::WCONTINUED).unwrap(); 24 | assert_eq!(pid, n); 25 | assert_eq!(WIFEXITED(status), false); 26 | // https://github.com/rust-lang/libc/issues/1782 27 | #[cfg(not(target_os = "freebsd"))] 28 | assert_eq!(WIFSIGNALED(status), false); 29 | // https://github.com/rust-lang/libc/issues/1784 30 | #[cfg(not(target_os = "openbsd"))] 31 | assert_eq!(WIFSTOPPED(status), false); 32 | assert_eq!(WIFCONTINUED(status), true); 33 | 34 | let (pid, status) = waitpid(n, 0).unwrap(); 35 | assert_eq!(pid, n); 36 | assert_eq!(WIFEXITED(status), true); 37 | // https://github.com/rust-lang/libc/issues/1782 38 | #[cfg(not(target_os = "freebsd"))] 39 | assert_eq!(WIFSIGNALED(status), false); 40 | // https://github.com/rust-lang/libc/issues/1784 41 | #[cfg(not(target_os = "openbsd"))] 42 | assert_eq!(WIFSTOPPED(status), false); 43 | assert_eq!(WIFCONTINUED(status), false); 44 | assert_eq!(WEXITSTATUS(status), 1); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /uapi/tests/process6.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | use testutils::*; 3 | use uapi::*; 4 | 5 | #[test] 6 | fn process6() { 7 | match unsafe { fork().unwrap() } { 8 | 0 => in_fork(|| { 9 | raise(c::SIGILL).unwrap(); 10 | exit(1); 11 | }), 12 | n => { 13 | let (pid, status) = wait().unwrap(); 14 | assert_eq!(pid, n); 15 | assert_eq!(WIFEXITED(status), false); 16 | assert_eq!(WIFSIGNALED(status), true); 17 | assert_eq!(WIFSTOPPED(status), false); 18 | assert_eq!(WIFCONTINUED(status), false); 19 | assert_eq!(WTERMSIG(status), c::SIGILL); 20 | #[cfg(not(any(target_os = "macos", target_os = "freebsd")))] 21 | assert_eq!(WCOREDUMP(status), true); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /uapi/tests/sched.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | #[test] 4 | fn test() { 5 | let pc = c::PRIO_PROCESS as _; 6 | setpriority(pc, getpid() as _, 1).unwrap(); 7 | let proc = getpriority(pc, getpid() as _).unwrap(); 8 | assert_eq!(proc, 1); 9 | setpriority(pc, 0, 2).unwrap(); 10 | let proc = getpriority(pc, 0).unwrap(); 11 | assert_eq!(proc, 2); 12 | let proc = nice(1).unwrap(); 13 | #[cfg(not(target_os = "freebsd"))] 14 | assert_eq!(proc, 3); 15 | let proc = getpriority(pc, 0).unwrap(); 16 | assert_eq!(proc, 3); 17 | } 18 | -------------------------------------------------------------------------------- /uapi/tests/sched2.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | #[cfg(target_os = "linux")] 4 | mod wrapper { 5 | use proc::test_if; 6 | use uapi::*; 7 | 8 | #[test_if(root)] 9 | fn test_setsched() { 10 | for &sched in &[c::SCHED_FIFO, c::SCHED_RR] { 11 | for &pid in &[0, getpid()] { 12 | for i in 1..3 { 13 | let mut param: c::sched_param = pod_zeroed(); 14 | param.sched_priority = i; 15 | sched_setscheduler(pid, sched, ¶m).unwrap(); 16 | assert_eq!(sched_getscheduler(pid).unwrap(), sched); 17 | let mut param = sched_getparam(pid).unwrap(); 18 | assert_eq!(param.sched_priority, i); 19 | param.sched_priority += 10; 20 | sched_setparam(pid, ¶m).unwrap(); 21 | let param = sched_getparam(pid).unwrap(); 22 | assert_eq!(param.sched_priority, i + 10); 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /uapi/tests/sched3.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | #[cfg(target_os = "linux")] 4 | mod wrapper { 5 | use proc::test_if; 6 | use uapi::*; 7 | 8 | #[test_if(root)] 9 | fn test_rr() { 10 | let mut param: c::sched_param = pod_zeroed(); 11 | param.sched_priority = 1; 12 | sched_setscheduler(0, c::SCHED_RR, ¶m).unwrap(); 13 | let iv = sched_rr_get_interval(0).unwrap(); 14 | assert!(iv.tv_sec > 0 || iv.tv_nsec > 0) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /uapi/tests/sched4.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | #[cfg(target_os = "linux")] 4 | mod wrapper { 5 | use proc::test_if; 6 | use std::mem; 7 | use uapi::*; 8 | 9 | fn test_affinity(pid: c::pid_t) { 10 | let mut orig = [0; 128]; 11 | sched_getaffinity(pid, &mut orig).unwrap(); 12 | let min_cpu = orig 13 | .iter() 14 | .copied() 15 | .enumerate() 16 | .filter(|v| v.1 != 0) 17 | .map(|v| (v.0, v.1.trailing_zeros() as usize)) 18 | .next() 19 | .unwrap(); 20 | let max_cpu = orig 21 | .iter() 22 | .copied() 23 | .enumerate() 24 | .rev() 25 | .filter(|v| v.1 != 0) 26 | .map(|v| { 27 | ( 28 | v.0, 29 | mem::size_of_val(&v.1) * 8 - v.1.leading_zeros() as usize - 1, 30 | ) 31 | }) 32 | .next() 33 | .unwrap(); 34 | if min_cpu != max_cpu { 35 | for &cpu in &[min_cpu, max_cpu] { 36 | let mut buf2 = [0; 128]; 37 | let mut buf3 = buf2; 38 | buf2[cpu.0] = 1 << cpu.1; 39 | sched_setaffinity(pid, &buf2).unwrap(); 40 | sched_getaffinity(pid, &mut buf3).unwrap(); 41 | assert_eq!(buf2, buf3); 42 | } 43 | } 44 | sched_setaffinity(pid, &orig).unwrap(); 45 | } 46 | 47 | #[test_if(root)] 48 | fn test() { 49 | for &pid in &[0, getpid()] { 50 | test_affinity(pid); 51 | for &(policy, nice, priority) in 52 | &[(c::SCHED_OTHER, 2, 0), (c::SCHED_FIFO, 0, 3)] 53 | { 54 | for &flags in &[0, c::SCHED_FLAG_RESET_ON_FORK] { 55 | let mut attr: c::sched_attr = pod_zeroed(); 56 | attr.sched_policy = policy as _; 57 | attr.sched_nice = nice; 58 | attr.sched_priority = priority; 59 | attr.sched_flags = flags as _; 60 | sched_setattr(pid, &attr, 0).unwrap(); 61 | let other = sched_getattr(pid, 0).unwrap(); 62 | assert_eq!(attr.sched_policy, other.sched_policy); 63 | assert_eq!(attr.sched_nice, other.sched_nice); 64 | assert_eq!(attr.sched_flags, other.sched_flags); 65 | assert_eq!(attr.sched_priority, other.sched_priority); 66 | } 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /uapi/tests/sethostname.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | #[cfg(target_os = "linux")] 4 | mod wrapper { 5 | use proc::*; 6 | use uapi::*; 7 | 8 | #[test_if(root)] 9 | fn sethostname1() { 10 | unshare(c::CLONE_NEWUTS).unwrap(); 11 | let name = b"hello world"; 12 | sethostname(name).unwrap(); 13 | assert_eq!(gethostname(&mut [0; 128][..]).unwrap().as_ustr(), &name[..]); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /uapi/tests/setrlimit.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | use uapi::*; 4 | 5 | #[test] 6 | fn setrlimit_() { 7 | let mut limit = uapi::getrlimit(c::RLIMIT_NOFILE as _).unwrap(); 8 | assert!(limit.rlim_cur > 0); 9 | assert!(limit.rlim_cur <= limit.rlim_max); 10 | limit.rlim_cur -= 1; 11 | uapi::setrlimit(c::RLIMIT_NOFILE as _, &limit).unwrap(); 12 | let new = uapi::getrlimit(c::RLIMIT_NOFILE as _).unwrap(); 13 | assert_eq!(new.rlim_cur, limit.rlim_cur); 14 | } 15 | -------------------------------------------------------------------------------- /uapi/tests/setuid1.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | use proc::*; 4 | use uapi::*; 5 | 6 | #[test_if(root)] 7 | fn setpid() { 8 | setgid(2).unwrap(); 9 | assert_eq!(getgid(), 2); 10 | 11 | seteuid(3).unwrap(); 12 | assert_eq!(geteuid(), 3); 13 | 14 | seteuid(0).unwrap(); 15 | assert_eq!(geteuid(), 0); 16 | 17 | setegid(4).unwrap(); 18 | assert_eq!(getegid(), 4); 19 | 20 | setgroups(&[5, 6]).unwrap(); 21 | let mut buf = [0; 128]; 22 | assert_eq!(getgroups(&mut buf).unwrap(), &[5, 6]); 23 | 24 | setuid(1).unwrap(); 25 | assert_eq!(getuid(), 1); 26 | } 27 | -------------------------------------------------------------------------------- /uapi/tests/setuid2.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | #[allow(unused_imports)] 4 | use proc::*; 5 | 6 | #[test_if(root)] 7 | #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd")))] 8 | fn setpid() { 9 | use uapi::*; 10 | 11 | setresgid(3, 4, 5).unwrap(); 12 | assert_eq!(getresgid().unwrap(), (3, 4, 5)); 13 | 14 | setresuid(1, 2, 0).unwrap(); 15 | assert_eq!(getresuid().unwrap(), (1, 2, 0)); 16 | } 17 | -------------------------------------------------------------------------------- /uapi/tests/setuid3.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(target_os = "openbsd"))] // todo 2 | mod wrapper { 3 | use std::process::exit; 4 | use testutils::*; 5 | use uapi::*; 6 | 7 | #[test] 8 | fn setpid() { 9 | match unsafe { fork().unwrap() } { 10 | 0 => in_fork(|| { 11 | assert_ne!(getsid(0), getpid()); 12 | assert_ne!(getpgrp(), getpid()); 13 | setsid().unwrap(); 14 | assert_eq!(getsid(0), getpid()); 15 | assert_eq!(getpgrp(), getpid()); 16 | 17 | raise(c::SIGSTOP).unwrap(); 18 | 19 | let c1 = match unsafe { fork().unwrap() } { 20 | 0 => { 21 | assert_eq!(getpgrp(), getppid()); 22 | assert_eq!(getsid(getppid()), getppid()); 23 | assert_eq!(getsid(0), getppid()); 24 | raise(c::SIGSTOP).unwrap(); 25 | assert_eq!(getpgrp(), getpid()); 26 | exit(0); 27 | } 28 | n => n, 29 | }; 30 | 31 | let r1 = waitpid(c1, c::WUNTRACED).unwrap().1; 32 | assert!(WIFSTOPPED(r1)); 33 | 34 | setpgid(c1, c1).unwrap(); 35 | 36 | kill(c1, c::SIGCONT).unwrap(); 37 | 38 | let r1 = waitpid(c1, 0).unwrap().1; 39 | assert!(WIFEXITED(r1)); 40 | assert_eq!(WEXITSTATUS(r1), 0); 41 | exit(0); 42 | }), 43 | n => { 44 | let r1 = waitpid(n, c::WUNTRACED).unwrap().1; 45 | assert!(WIFSTOPPED(r1)); 46 | 47 | assert_eq!(getsid(n), n); 48 | 49 | kill(n, c::SIGCONT).unwrap(); 50 | 51 | let r1 = waitpid(n, 0).unwrap().1; 52 | assert!(WIFEXITED(r1)); 53 | assert_eq!(WEXITSTATUS(r1), 0); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /uapi/tests/signal.rs: -------------------------------------------------------------------------------- 1 | use uapi::*; 2 | 3 | #[test] 4 | fn signal_() { 5 | let mut set = empty_sig_set().unwrap(); 6 | 7 | pthread_sigmask(c::SIG_SETMASK, Some(&set), None).unwrap(); 8 | 9 | sigaddset(&mut set, c::SIGUSR1).unwrap(); 10 | let mut oldset = empty_sig_set().unwrap(); 11 | 12 | pthread_sigmask(c::SIG_SETMASK, Some(&set), Some(&mut oldset)).unwrap(); 13 | 14 | assert!(!sigismember(&oldset, c::SIGUSR1).unwrap()); 15 | 16 | sigemptyset(&mut set).unwrap(); 17 | sigemptyset(&mut oldset).unwrap(); 18 | 19 | sigaddset(&mut set, c::SIGUSR2).unwrap(); 20 | 21 | pthread_sigmask(c::SIG_BLOCK, Some(&set), Some(&mut oldset)).unwrap(); 22 | 23 | assert!(sigismember(&oldset, c::SIGUSR1).unwrap()); 24 | assert!(!sigismember(&oldset, c::SIGUSR2).unwrap()); 25 | 26 | raise(c::SIGUSR2).unwrap(); 27 | 28 | assert_eq!(sigwait(&set), Ok(c::SIGUSR2)); 29 | } 30 | 31 | #[test] 32 | #[cfg(not(any(target_os = "macos", target_os = "openbsd")))] 33 | fn signal2_() { 34 | let mut set = empty_sig_set().unwrap(); 35 | 36 | sigaddset(&mut set, c::SIGUSR2).unwrap(); 37 | 38 | pthread_sigmask(c::SIG_SETMASK, Some(&set), None).unwrap(); 39 | 40 | raise(c::SIGUSR2).unwrap(); 41 | 42 | let mut info = pod_zeroed(); 43 | assert_eq!(sigwaitinfo(&set, Some(&mut info)), Ok(c::SIGUSR2)); 44 | assert_eq!(info.si_signo, c::SIGUSR2); 45 | 46 | raise(c::SIGUSR2).unwrap(); 47 | 48 | let timeout = c::timespec { 49 | tv_sec: 1, 50 | tv_nsec: 0, 51 | }; 52 | 53 | let mut info = pod_zeroed(); 54 | assert_eq!( 55 | sigtimedwait(&set, Some(&mut info), &timeout), 56 | Ok(c::SIGUSR2) 57 | ); 58 | assert_eq!(info.si_signo, c::SIGUSR2); 59 | } 60 | -------------------------------------------------------------------------------- /uapi/tests/signalfd.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | #[cfg(target_os = "linux")] 3 | fn signalfd_() { 4 | use uapi::*; 5 | 6 | let sfd = signalfd_new(&empty_sig_set().unwrap(), 0).unwrap(); 7 | assert_ne!(fcntl_getfd(*sfd).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 8 | 9 | let sfd = signalfd_new(&empty_sig_set().unwrap(), c::SFD_CLOEXEC | c::SFD_NONBLOCK) 10 | .unwrap(); 11 | assert_eq!(fcntl_getfd(*sfd).unwrap() & c::FD_CLOEXEC, c::FD_CLOEXEC); 12 | 13 | let mut set = empty_sig_set().unwrap(); 14 | sigaddset(&mut set, c::SIGUSR1).unwrap(); 15 | 16 | pthread_sigmask(c::SIG_BLOCK, Some(&set), None).unwrap(); 17 | 18 | let mut buf = [pod_zeroed(); 5]; 19 | assert_eq!( 20 | signalfd_read(*sfd, &mut buf[..]).err().unwrap(), 21 | Errno(c::EAGAIN) 22 | ); 23 | 24 | raise(c::SIGUSR1).unwrap(); 25 | 26 | assert_eq!( 27 | signalfd_read(*sfd, &mut buf[..]).err().unwrap(), 28 | Errno(c::EAGAIN) 29 | ); 30 | 31 | signalfd_mod(*sfd, &set).unwrap(); 32 | 33 | let res = signalfd_read(*sfd, &mut buf[..]).unwrap(); 34 | assert_eq!(res.len(), 1); 35 | assert_eq!(res[0].ssi_signo, c::SIGUSR1 as _); 36 | } 37 | -------------------------------------------------------------------------------- /uapi/tests/unshare.rs: -------------------------------------------------------------------------------- 1 | extern crate proc; // https://github.com/rust-lang/rust/issues/64450 2 | 3 | #[cfg(target_os = "linux")] 4 | mod wrapper { 5 | use proc::*; 6 | use uapi::*; 7 | 8 | #[test_if(root)] 9 | fn unshare_() { 10 | let hn = "abc123abc"; 11 | 12 | let old_ns = open("/proc/self/ns/uts", c::O_RDONLY, 0).unwrap(); 13 | 14 | unshare(c::CLONE_NEWUTS).unwrap(); 15 | 16 | assert_ne!(gethostname(&mut [0; 128][..]).unwrap().as_ustr(), hn); 17 | 18 | sethostname(hn.as_bytes()).unwrap(); 19 | 20 | assert_eq!(gethostname(&mut [0; 128][..]).unwrap().as_ustr(), hn); 21 | 22 | assert!(setns(*old_ns, c::CLONE_NEWPID).is_err()); 23 | 24 | setns(*old_ns, 0).unwrap(); 25 | 26 | assert_ne!(gethostname(&mut [0; 128][..]).unwrap().as_ustr(), hn); 27 | } 28 | } 29 | --------------------------------------------------------------------------------