From cd39c1dfd894bc1e2e428a2cff9bbfe8cb21bc12 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 May 2019 14:28:22 +0200 Subject: [PATCH] Extract common interface from lucet-wasi --- Cargo.toml | 22 + LICENSE | 220 +++++++ LICENSE.cloudabi-utils | 24 + LICENSE.wasmtime | 219 +++++++ README.md | 44 ++ build.rs | 96 +++ src/ctx.rs | 257 ++++++++ src/fdentry.rs | 143 +++++ src/host.rs | 335 ++++++++++ src/hostcalls.rs | 1360 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 + src/memory.rs | 479 ++++++++++++++ src/wasm32.rs | 1367 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 4574 insertions(+) create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 LICENSE.cloudabi-utils create mode 100644 LICENSE.wasmtime create mode 100644 README.md create mode 100644 build.rs create mode 100644 src/ctx.rs create mode 100644 src/fdentry.rs create mode 100644 src/host.rs create mode 100644 src/hostcalls.rs create mode 100644 src/lib.rs create mode 100644 src/memory.rs create mode 100644 src/wasm32.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..4fe49bfcb2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "wasi-common" +version = "0.1.0" +authors = ["Adam C. Foltzer ", "Jakub Konka "] +edition = "2018" +license = "Apache-2.0 WITH LLVM-exception" + +[dependencies] +cast = "0.2" +clap = "2.23" +failure = "0.1" +human-size = "0.4" +libc = "0.2" +nix = "0.13" +rand = "0.6" + +[build-dependencies] +bindgen = "0.49.0" + +[lib] +name = "wasi_common" +crate-type = ["rlib", "staticlib", "cdylib"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..f9d81955f4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/LICENSE.cloudabi-utils b/LICENSE.cloudabi-utils new file mode 100644 index 0000000000..fef83ffb83 --- /dev/null +++ b/LICENSE.cloudabi-utils @@ -0,0 +1,24 @@ +All code is distributed under the following license: + + Copyright (c) 2015 Nuxi, https://nuxi.nl/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. \ No newline at end of file diff --git a/LICENSE.wasmtime b/LICENSE.wasmtime new file mode 100644 index 0000000000..be1d7c438a --- /dev/null +++ b/LICENSE.wasmtime @@ -0,0 +1,219 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..5aab07b8b8 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# wasi-common + +This repo strips away those bits of [lucet-wasi](https://github.com/fastly/lucet/tree/master/lucet-wasi) +which can potentially be encapsulated in a separated crate with potential plug'n'play use in both +[Lucet](https://github.com/fastly/lucet) +and [Wasmtime](https://github.com/CraneStation/wasmtime) projects. + +This repo is strictly experimental. + +## Supported syscalls + +We support a subset of the [WASI +API](https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-api.md), though we are adding +new syscalls on a regular basis. We currently implement: + +- `__wasi_args_get` +- `__wasi_args_sizes_get` +- `__wasi_clock_res_get` +- `__wasi_clock_time_get` +- `__wasi_environ_get` +- `__wasi_environ_sizes_get` +- `__wasi_fd_close` +- `__wasi_fd_fdstat_get` +- `__wasi_fd_fdstat_set_flags` +- `__wasi_fd_prestat_dir_name` +- `__wasi_fd_prestat_get` +- `__wasi_fd_read` +- `__wasi_fd_seek` +- `__wasi_fd_write` +- `__wasi_path_open` +- `__wasi_proc_exit` +- `__wasi_random_get` + +This is enough to run basic C and Rust programs, including those that use command-line arguments, +environment variables, stdio, and basic file operations. + +## Third-Party Code + +`src/wasm32.rs` is copied from +[wasmtime](https://github.com/CraneStation/wasmtime/blob/master/wasmtime-wasi/src/wasm32.rs), along +with the associated `LICENSE.wasmtime` file. + +Significant parts of our syscall implementations are derived from the C implementations in +`cloudabi-utils`. See `LICENSE.cloudabi-utils` for license information. diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000..0e39fb4899 --- /dev/null +++ b/build.rs @@ -0,0 +1,96 @@ +use std::env; +use std::fs::File; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; + +fn wasi_sdk() -> PathBuf { + Path::new(&env::var("WASI_SDK").unwrap_or("/opt/wasi-sdk".to_owned())).to_path_buf() +} + +fn wasi_sysroot() -> PathBuf { + match env::var("WASI_SYSROOT") { + Ok(wasi_sysroot) => Path::new(&wasi_sysroot).to_path_buf(), + Err(_) => { + let mut path = wasi_sdk(); + path.push("share"); + path.push("sysroot"); + path + } + } +} + +fn wasm_clang_root() -> PathBuf { + match env::var("CLANG_ROOT") { + Ok(clang) => Path::new(&clang).to_path_buf(), + Err(_) => { + let mut path = wasi_sdk(); + path.push("lib"); + path.push("clang"); + path.push("8.0.0"); + path + } + } +} + +fn main() { + let wasi_sysroot = wasi_sysroot(); + let wasm_clang_root = wasm_clang_root(); + assert!( + wasi_sysroot.exists(), + "wasi-sysroot not present at {:?}", + wasi_sysroot + ); + assert!( + wasm_clang_root.exists(), + "clang-root not present at {:?}", + wasm_clang_root + ); + + let wasi_sysroot_core_h = wasi_sysroot.join("include/wasi/core.h"); + + assert!( + wasi_sysroot_core_h.exists(), + "wasi-sysroot core.h not present at {:?}", + wasi_sysroot_core_h + ); + + println!("cargo:rerun-if-changed={}", wasi_sysroot_core_h.display()); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let core_h_path = out_path.join("core.h"); + let core_h = File::create(&core_h_path).unwrap(); + + // `bindgen` doesn't understand typed constant macros like `UINT8_C(123)`, so this fun regex + // strips them off to yield a copy of `wasi/core.h` with bare constants. + let sed_result = Command::new("sed") + .arg("-E") + .arg(r#"s/U?INT[0-9]+_C\(((0x)?[0-9]+)\)/\1/g"#) + .arg(wasi_sysroot_core_h) + .stdout(Stdio::from(core_h)) + .status() + .expect("can execute sed"); + + if !sed_result.success() { + // something failed, but how? + match sed_result.code() { + Some(code) => panic!("sed failed with code {}", code), + None => panic!("sed exited abnormally"), + } + } + + let host_builder = bindgen::Builder::default() + .clang_arg("-nostdinc") + .clang_arg("-D__wasi__") + .clang_arg(format!("-isystem={}/include/", wasi_sysroot.display())) + .clang_arg(format!("-I{}/include/", wasm_clang_root.display())) + .header(core_h_path.to_str().unwrap()) + .whitelist_type("__wasi_.*") + .whitelist_var("__WASI_.*"); + + host_builder + .generate() + .expect("can generate host bindings") + .write_to_file(out_path.join("wasi_host.rs")) + .expect("can write host bindings"); +} diff --git a/src/ctx.rs b/src/ctx.rs new file mode 100644 index 0000000000..6bbd734028 --- /dev/null +++ b/src/ctx.rs @@ -0,0 +1,257 @@ +use crate::fdentry::FdEntry; +use crate::host; +use crate::wasm32; + +use failure::{bail, format_err, Error}; +use nix::unistd::dup; +use std::collections::HashMap; +use std::ffi::{CStr, CString}; +use std::fs::File; +use std::io::{stderr, stdin, stdout}; +use std::os::unix::prelude::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use std::path::{Path, PathBuf}; + +pub trait VmContext { + fn as_wasi_ctx(&self) -> &WasiCtx; + fn as_wasi_ctx_mut(&mut self) -> &mut WasiCtx; + + unsafe fn dec_ptr( + &mut self, + ptr: wasm32::uintptr_t, + len: usize, + ) -> Result<*mut u8, host::__wasi_errno_t>; +} + +pub struct WasiCtxBuilder { + fds: HashMap, + preopens: HashMap, + args: Vec, + env: HashMap, +} + +impl WasiCtxBuilder { + /// Builder for a new `WasiCtx`. + pub fn new() -> Self { + let null = dev_null(); + WasiCtxBuilder { + fds: HashMap::new(), + preopens: HashMap::new(), + args: vec![], + env: HashMap::new(), + } + .fd_dup(0, &null) + .fd_dup(1, &null) + .fd_dup(2, &null) + } + + pub fn args(mut self, args: &[&str]) -> Self { + self.args = args + .into_iter() + .map(|arg| CString::new(*arg).expect("argument can be converted to a CString")) + .collect(); + self + } + + pub fn arg(mut self, arg: &str) -> Self { + self.args + .push(CString::new(arg).expect("argument can be converted to a CString")); + self + } + + pub fn c_args>(mut self, args: &[S]) -> Self { + self.args = args + .into_iter() + .map(|arg| arg.as_ref().to_owned()) + .collect(); + self + } + + pub fn c_arg>(mut self, arg: S) -> Self { + self.args.push(arg.as_ref().to_owned()); + self + } + + pub fn inherit_stdio(self) -> Self { + self.fd_dup(0, &stdin()) + .fd_dup(1, &stdout()) + .fd_dup(2, &stderr()) + } + + pub fn inherit_env(mut self) -> Self { + self.env = std::env::vars() + .map(|(k, v)| { + // TODO: handle errors, and possibly assert that the key is valid per POSIX + ( + CString::new(k).expect("environment key can be converted to a CString"), + CString::new(v).expect("environment value can be converted to a CString"), + ) + }) + .collect(); + self + } + + pub fn env(mut self, k: &str, v: &str) -> Self { + self.env.insert( + // TODO: handle errors, and possibly assert that the key is valid per POSIX + CString::new(k).expect("environment key can be converted to a CString"), + CString::new(v).expect("environment value can be converted to a CString"), + ); + self + } + + pub fn c_env(mut self, k: S, v: T) -> Self + where + S: AsRef, + T: AsRef, + { + self.env + .insert(k.as_ref().to_owned(), v.as_ref().to_owned()); + self + } + + /// Add an existing file-like object as a file descriptor in the context. + /// + /// When the `WasiCtx` is dropped, all of its associated file descriptors are `close`d. If you + /// do not want this to close the existing object, use `WasiCtxBuilder::fd_dup()`. + pub fn fd(self, wasm_fd: host::__wasi_fd_t, fd: F) -> Self { + // safe because we're getting a valid RawFd from the F directly + unsafe { self.raw_fd(wasm_fd, fd.into_raw_fd()) } + } + + /// Add an existing file-like object as a duplicate file descriptor in the context. + /// + /// The underlying file descriptor of this object will be duplicated before being added to the + /// context, so it will not be closed when the `WasiCtx` is dropped. + /// + /// TODO: handle `dup` errors + pub fn fd_dup(self, wasm_fd: host::__wasi_fd_t, fd: &F) -> Self { + // safe because we're getting a valid RawFd from the F directly + unsafe { self.raw_fd(wasm_fd, dup(fd.as_raw_fd()).unwrap()) } + } + + /// Add an existing file descriptor to the context. + /// + /// When the `WasiCtx` is dropped, this file descriptor will be `close`d. If you do not want to + /// close the existing descriptor, use `WasiCtxBuilder::raw_fd_dup()`. + pub unsafe fn raw_fd(mut self, wasm_fd: host::__wasi_fd_t, fd: RawFd) -> Self { + self.fds.insert(wasm_fd, FdEntry::from_raw_fd(fd)); + self + } + + /// Add a duplicate of an existing file descriptor to the context. + /// + /// The file descriptor will be duplicated before being added to the context, so it will not be + /// closed when the `WasiCtx` is dropped. + /// + /// TODO: handle `dup` errors + pub unsafe fn raw_fd_dup(self, wasm_fd: host::__wasi_fd_t, fd: RawFd) -> Self { + self.raw_fd(wasm_fd, dup(fd).unwrap()) + } + + pub fn preopened_dir>(mut self, dir: File, guest_path: P) -> Self { + self.preopens.insert(guest_path.as_ref().to_owned(), dir); + self + } + + pub fn build(mut self) -> Result { + // startup code starts looking at fd 3 for preopens + let mut preopen_fd = 3; + for (guest_path, dir) in self.preopens { + if !dir.metadata()?.is_dir() { + bail!("preopened file is not a directory"); + } + while self.fds.contains_key(&preopen_fd) { + preopen_fd = preopen_fd + .checked_add(1) + .ok_or(format_err!("not enough file handles"))?; + } + let mut fe = FdEntry::from_file(dir); + fe.preopen_path = Some(guest_path); + self.fds.insert(preopen_fd, fe); + preopen_fd += 1; + } + + let env = self + .env + .into_iter() + .map(|(k, v)| { + let mut pair = k.into_bytes(); + pair.extend_from_slice(b"="); + pair.extend_from_slice(v.to_bytes_with_nul()); + // constructing a new CString from existing CStrings is safe + unsafe { CString::from_vec_unchecked(pair) } + }) + .collect(); + + Ok(WasiCtx { + fds: self.fds, + args: self.args, + env, + }) + } +} + +#[derive(Debug)] +pub struct WasiCtx { + pub fds: HashMap, + pub args: Vec, + pub env: Vec, +} + +impl WasiCtx { + /// Make a new `WasiCtx` with some default settings. + /// + /// - File descriptors 0, 1, and 2 inherit stdin, stdout, and stderr from the host process. + /// + /// - Environment variables are inherited from the host process. + /// + /// To override these behaviors, use `WasiCtxBuilder`. + pub fn new(args: &[&str]) -> WasiCtx { + WasiCtxBuilder::new() + .args(args) + .inherit_stdio() + .inherit_env() + .build() + .expect("default options don't fail") + } + + pub fn get_fd_entry( + &self, + fd: host::__wasi_fd_t, + rights_base: host::__wasi_rights_t, + rights_inheriting: host::__wasi_rights_t, + ) -> Result<&FdEntry, host::__wasi_errno_t> { + if let Some(fe) = self.fds.get(&fd) { + // validate rights + if !fe.rights_base & rights_base != 0 || !fe.rights_inheriting & rights_inheriting != 0 + { + Err(host::__WASI_ENOTCAPABLE as host::__wasi_errno_t) + } else { + Ok(fe) + } + } else { + Err(host::__WASI_EBADF as host::__wasi_errno_t) + } + } + + pub fn insert_fd_entry( + &mut self, + fe: FdEntry, + ) -> Result { + // never insert where stdio handles usually are + let mut fd = 3; + while self.fds.contains_key(&fd) { + if let Some(next_fd) = fd.checked_add(1) { + fd = next_fd; + } else { + return Err(host::__WASI_EMFILE as host::__wasi_errno_t); + } + } + self.fds.insert(fd, fe); + Ok(fd) + } +} + +fn dev_null() -> File { + File::open("/dev/null").expect("failed to open /dev/null") +} diff --git a/src/fdentry.rs b/src/fdentry.rs new file mode 100644 index 0000000000..c64ea9b6af --- /dev/null +++ b/src/fdentry.rs @@ -0,0 +1,143 @@ +use crate::host; +use std::fs::File; +use std::os::unix::prelude::{FileTypeExt, FromRawFd, IntoRawFd, RawFd}; +use std::path::PathBuf; + +#[derive(Clone, Debug)] +pub struct FdEntry { + pub fd_object: FdObject, + pub rights_base: host::__wasi_rights_t, + pub rights_inheriting: host::__wasi_rights_t, + pub preopen_path: Option, +} + +impl FdEntry { + pub fn from_file(file: File) -> FdEntry { + unsafe { FdEntry::from_raw_fd(file.into_raw_fd()) } + } +} + +impl FromRawFd for FdEntry { + // TODO: make this a different function with error handling, rather than using the trait method + unsafe fn from_raw_fd(rawfd: RawFd) -> FdEntry { + let (ty, mut rights_base, rights_inheriting) = + determine_type_rights(rawfd).expect("can determine file rights"); + + use nix::fcntl::{fcntl, OFlag, F_GETFL}; + let flags_bits = fcntl(rawfd, F_GETFL).expect("fcntl succeeds"); + let flags = OFlag::from_bits_truncate(flags_bits); + let accmode = flags & OFlag::O_ACCMODE; + if accmode == OFlag::O_RDONLY { + rights_base &= !host::__WASI_RIGHT_FD_WRITE as host::__wasi_rights_t; + } else if accmode == OFlag::O_WRONLY { + rights_base &= !host::__WASI_RIGHT_FD_READ as host::__wasi_rights_t; + } + + FdEntry { + fd_object: FdObject { + ty: ty as u8, + rawfd, + needs_close: true, + }, + rights_base, + rights_inheriting, + preopen_path: None, + } + } +} + +// TODO: can probably make this safe by using fcntl directly rather than going through `File` +pub unsafe fn determine_type_rights( + rawfd: RawFd, +) -> Result< + ( + host::__wasi_filetype_t, + host::__wasi_rights_t, + host::__wasi_rights_t, + ), + host::__wasi_errno_t, +> { + let (ty, rights_base, rights_inheriting) = { + let file = File::from_raw_fd(rawfd); + let ft = file.metadata().unwrap().file_type(); + // we just make a `File` here for convenience; we don't want it to close when it drops + std::mem::forget(file); + if ft.is_block_device() { + ( + host::__WASI_FILETYPE_BLOCK_DEVICE, + host::RIGHTS_BLOCK_DEVICE_BASE, + host::RIGHTS_BLOCK_DEVICE_INHERITING, + ) + } else if ft.is_char_device() { + if nix::unistd::isatty(rawfd).unwrap() { + ( + host::__WASI_FILETYPE_CHARACTER_DEVICE, + host::RIGHTS_TTY_BASE, + host::RIGHTS_TTY_BASE, + ) + } else { + ( + host::__WASI_FILETYPE_CHARACTER_DEVICE, + host::RIGHTS_CHARACTER_DEVICE_BASE, + host::RIGHTS_CHARACTER_DEVICE_INHERITING, + ) + } + } else if ft.is_dir() { + ( + host::__WASI_FILETYPE_DIRECTORY, + host::RIGHTS_DIRECTORY_BASE, + host::RIGHTS_DIRECTORY_INHERITING, + ) + } else if ft.is_file() { + ( + host::__WASI_FILETYPE_REGULAR_FILE, + host::RIGHTS_REGULAR_FILE_BASE, + host::RIGHTS_REGULAR_FILE_INHERITING, + ) + } else if ft.is_socket() { + use nix::sys::socket; + match socket::getsockopt(rawfd, socket::sockopt::SockType).unwrap() { + socket::SockType::Datagram => ( + host::__WASI_FILETYPE_SOCKET_DGRAM, + host::RIGHTS_SOCKET_BASE, + host::RIGHTS_SOCKET_INHERITING, + ), + socket::SockType::Stream => ( + host::__WASI_FILETYPE_SOCKET_STREAM, + host::RIGHTS_SOCKET_BASE, + host::RIGHTS_SOCKET_INHERITING, + ), + _ => return Err(host::__WASI_EINVAL as host::__wasi_errno_t), + } + } else if ft.is_fifo() { + ( + host::__WASI_FILETYPE_SOCKET_STREAM, + host::RIGHTS_SOCKET_BASE, + host::RIGHTS_SOCKET_INHERITING, + ) + } else { + return Err(host::__WASI_EINVAL as host::__wasi_errno_t); + } + }; + Ok(( + ty as host::__wasi_filetype_t, + rights_base, + rights_inheriting, + )) +} + +#[derive(Clone, Debug)] +pub struct FdObject { + pub ty: host::__wasi_filetype_t, + pub rawfd: RawFd, + pub needs_close: bool, + // TODO: directories +} + +impl Drop for FdObject { + fn drop(&mut self) { + if self.needs_close { + nix::unistd::close(self.rawfd).unwrap_or_else(|e| eprintln!("FdObject::drop(): {}", e)); + } + } +} diff --git a/src/host.rs b/src/host.rs new file mode 100644 index 0000000000..b06b745743 --- /dev/null +++ b/src/host.rs @@ -0,0 +1,335 @@ +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/wasi_host.rs")); + +pub type void = ::std::os::raw::c_void; + +pub unsafe fn ciovec_to_nix<'a>(ciovec: &'a __wasi_ciovec_t) -> nix::sys::uio::IoVec<&'a [u8]> { + let slice = std::slice::from_raw_parts(ciovec.buf as *const u8, ciovec.buf_len); + nix::sys::uio::IoVec::from_slice(slice) +} + +pub unsafe fn ciovec_to_nix_mut<'a>( + ciovec: &'a mut __wasi_ciovec_t, +) -> nix::sys::uio::IoVec<&'a mut [u8]> { + let slice = std::slice::from_raw_parts_mut(ciovec.buf as *mut u8, ciovec.buf_len); + nix::sys::uio::IoVec::from_mut_slice(slice) +} + +pub fn errno_from_nix(errno: nix::errno::Errno) -> __wasi_errno_t { + let e = match errno { + nix::errno::Errno::EPERM => __WASI_EPERM, + nix::errno::Errno::ENOENT => __WASI_ENOENT, + nix::errno::Errno::ESRCH => __WASI_ESRCH, + nix::errno::Errno::EINTR => __WASI_EINTR, + nix::errno::Errno::EIO => __WASI_EIO, + nix::errno::Errno::ENXIO => __WASI_ENXIO, + nix::errno::Errno::E2BIG => __WASI_E2BIG, + nix::errno::Errno::ENOEXEC => __WASI_ENOEXEC, + nix::errno::Errno::EBADF => __WASI_EBADF, + nix::errno::Errno::ECHILD => __WASI_ECHILD, + nix::errno::Errno::EAGAIN => __WASI_EAGAIN, + nix::errno::Errno::ENOMEM => __WASI_ENOMEM, + nix::errno::Errno::EACCES => __WASI_EACCES, + nix::errno::Errno::EFAULT => __WASI_EFAULT, + nix::errno::Errno::EBUSY => __WASI_EBUSY, + nix::errno::Errno::EEXIST => __WASI_EEXIST, + nix::errno::Errno::EXDEV => __WASI_EXDEV, + nix::errno::Errno::ENODEV => __WASI_ENODEV, + nix::errno::Errno::ENOTDIR => __WASI_ENOTDIR, + nix::errno::Errno::EISDIR => __WASI_EISDIR, + nix::errno::Errno::EINVAL => __WASI_EINVAL, + nix::errno::Errno::ENFILE => __WASI_ENFILE, + nix::errno::Errno::EMFILE => __WASI_EMFILE, + nix::errno::Errno::ENOTTY => __WASI_ENOTTY, + nix::errno::Errno::ETXTBSY => __WASI_ETXTBSY, + nix::errno::Errno::EFBIG => __WASI_EFBIG, + nix::errno::Errno::ENOSPC => __WASI_ENOSPC, + nix::errno::Errno::ESPIPE => __WASI_ESPIPE, + nix::errno::Errno::EROFS => __WASI_EROFS, + nix::errno::Errno::EMLINK => __WASI_EMLINK, + nix::errno::Errno::EPIPE => __WASI_EPIPE, + nix::errno::Errno::EDOM => __WASI_EDOM, + nix::errno::Errno::ERANGE => __WASI_ERANGE, + nix::errno::Errno::EDEADLK => __WASI_EDEADLK, + nix::errno::Errno::ENAMETOOLONG => __WASI_ENAMETOOLONG, + nix::errno::Errno::ENOLCK => __WASI_ENOLCK, + nix::errno::Errno::ENOSYS => __WASI_ENOSYS, + nix::errno::Errno::ENOTEMPTY => __WASI_ENOTEMPTY, + nix::errno::Errno::ELOOP => __WASI_ELOOP, + nix::errno::Errno::ENOMSG => __WASI_ENOMSG, + nix::errno::Errno::EIDRM => __WASI_EIDRM, + nix::errno::Errno::ENOLINK => __WASI_ENOLINK, + nix::errno::Errno::EPROTO => __WASI_EPROTO, + nix::errno::Errno::EMULTIHOP => __WASI_EMULTIHOP, + nix::errno::Errno::EBADMSG => __WASI_EBADMSG, + nix::errno::Errno::EOVERFLOW => __WASI_EOVERFLOW, + nix::errno::Errno::EILSEQ => __WASI_EILSEQ, + nix::errno::Errno::ENOTSOCK => __WASI_ENOTSOCK, + nix::errno::Errno::EDESTADDRREQ => __WASI_EDESTADDRREQ, + nix::errno::Errno::EMSGSIZE => __WASI_EMSGSIZE, + nix::errno::Errno::EPROTOTYPE => __WASI_EPROTOTYPE, + nix::errno::Errno::ENOPROTOOPT => __WASI_ENOPROTOOPT, + nix::errno::Errno::EPROTONOSUPPORT => __WASI_EPROTONOSUPPORT, + nix::errno::Errno::EAFNOSUPPORT => __WASI_EAFNOSUPPORT, + nix::errno::Errno::EADDRINUSE => __WASI_EADDRINUSE, + nix::errno::Errno::EADDRNOTAVAIL => __WASI_EADDRNOTAVAIL, + nix::errno::Errno::ENETDOWN => __WASI_ENETDOWN, + nix::errno::Errno::ENETUNREACH => __WASI_ENETUNREACH, + nix::errno::Errno::ENETRESET => __WASI_ENETRESET, + nix::errno::Errno::ECONNABORTED => __WASI_ECONNABORTED, + nix::errno::Errno::ECONNRESET => __WASI_ECONNRESET, + nix::errno::Errno::ENOBUFS => __WASI_ENOBUFS, + nix::errno::Errno::EISCONN => __WASI_EISCONN, + nix::errno::Errno::ENOTCONN => __WASI_ENOTCONN, + nix::errno::Errno::ETIMEDOUT => __WASI_ETIMEDOUT, + nix::errno::Errno::ECONNREFUSED => __WASI_ECONNREFUSED, + nix::errno::Errno::EHOSTUNREACH => __WASI_EHOSTUNREACH, + nix::errno::Errno::EALREADY => __WASI_EALREADY, + nix::errno::Errno::EINPROGRESS => __WASI_EINPROGRESS, + nix::errno::Errno::ESTALE => __WASI_ESTALE, + nix::errno::Errno::EDQUOT => __WASI_EDQUOT, + nix::errno::Errno::ECANCELED => __WASI_ECANCELED, + nix::errno::Errno::EOWNERDEAD => __WASI_EOWNERDEAD, + nix::errno::Errno::ENOTRECOVERABLE => __WASI_ENOTRECOVERABLE, + _ => __WASI_ENOSYS, + }; + e as __wasi_errno_t +} + +#[cfg(target_os = "linux")] +const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC; + +#[cfg(not(target_os = "linux"))] +const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC; + +pub fn nix_from_fdflags(fdflags: __wasi_fdflags_t) -> nix::fcntl::OFlag { + use nix::fcntl::OFlag; + let mut nix_flags = OFlag::empty(); + if fdflags & (__WASI_FDFLAG_APPEND as __wasi_fdflags_t) != 0 { + nix_flags.insert(OFlag::O_APPEND); + } + if fdflags & (__WASI_FDFLAG_DSYNC as __wasi_fdflags_t) != 0 { + nix_flags.insert(OFlag::O_DSYNC); + } + if fdflags & (__WASI_FDFLAG_NONBLOCK as __wasi_fdflags_t) != 0 { + nix_flags.insert(OFlag::O_NONBLOCK); + } + if fdflags & (__WASI_FDFLAG_RSYNC as __wasi_fdflags_t) != 0 { + nix_flags.insert(O_RSYNC); + } + if fdflags & (__WASI_FDFLAG_SYNC as __wasi_fdflags_t) != 0 { + nix_flags.insert(OFlag::O_SYNC); + } + nix_flags +} + +pub fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> __wasi_fdflags_t { + use nix::fcntl::OFlag; + let mut fdflags = 0; + if oflags.contains(OFlag::O_APPEND) { + fdflags |= __WASI_FDFLAG_APPEND; + } + if oflags.contains(OFlag::O_DSYNC) { + fdflags |= __WASI_FDFLAG_DSYNC; + } + if oflags.contains(OFlag::O_NONBLOCK) { + fdflags |= __WASI_FDFLAG_NONBLOCK; + } + if oflags.contains(O_RSYNC) { + fdflags |= __WASI_FDFLAG_RSYNC; + } + if oflags.contains(OFlag::O_SYNC) { + fdflags |= __WASI_FDFLAG_SYNC; + } + fdflags as __wasi_fdflags_t +} + +pub fn nix_from_oflags(oflags: __wasi_oflags_t) -> nix::fcntl::OFlag { + use nix::fcntl::OFlag; + let mut nix_flags = OFlag::empty(); + if oflags & (__WASI_O_CREAT as __wasi_oflags_t) != 0 { + nix_flags.insert(OFlag::O_CREAT); + } + if oflags & (__WASI_O_DIRECTORY as __wasi_oflags_t) != 0 { + nix_flags.insert(OFlag::O_DIRECTORY); + } + if oflags & (__WASI_O_EXCL as __wasi_oflags_t) != 0 { + nix_flags.insert(OFlag::O_EXCL); + } + if oflags & (__WASI_O_TRUNC as __wasi_oflags_t) != 0 { + nix_flags.insert(OFlag::O_TRUNC); + } + nix_flags +} + +pub fn filetype_from_nix(sflags: nix::sys::stat::SFlag) -> __wasi_filetype_t { + use nix::sys::stat::SFlag; + if sflags.contains(SFlag::S_IFCHR) { + __WASI_FILETYPE_CHARACTER_DEVICE as __wasi_filetype_t + } else if sflags.contains(SFlag::S_IFBLK) { + __WASI_FILETYPE_BLOCK_DEVICE as __wasi_filetype_t + } else if sflags.contains(SFlag::S_IFIFO) | sflags.contains(SFlag::S_IFSOCK) { + __WASI_FILETYPE_SOCKET_STREAM as __wasi_filetype_t + } else if sflags.contains(SFlag::S_IFDIR) { + __WASI_FILETYPE_DIRECTORY as __wasi_filetype_t + } else if sflags.contains(SFlag::S_IFREG) { + __WASI_FILETYPE_REGULAR_FILE as __wasi_filetype_t + } else if sflags.contains(SFlag::S_IFLNK) { + __WASI_FILETYPE_SYMBOLIC_LINK as __wasi_filetype_t + } else { + __WASI_FILETYPE_UNKNOWN as __wasi_filetype_t + } +} + +pub fn nix_from_filetype(sflags: __wasi_filetype_t) -> nix::sys::stat::SFlag { + use nix::sys::stat::SFlag; + let mut nix_sflags = SFlag::empty(); + if sflags & (__WASI_FILETYPE_CHARACTER_DEVICE as __wasi_filetype_t) != 0 { + nix_sflags.insert(SFlag::S_IFCHR); + } + if sflags & (__WASI_FILETYPE_BLOCK_DEVICE as __wasi_filetype_t) != 0 { + nix_sflags.insert(SFlag::S_IFBLK); + } + if sflags & (__WASI_FILETYPE_SOCKET_STREAM as __wasi_filetype_t) != 0 { + nix_sflags.insert(SFlag::S_IFIFO); + nix_sflags.insert(SFlag::S_IFSOCK); + } + if sflags & (__WASI_FILETYPE_DIRECTORY as __wasi_filetype_t) != 0 { + nix_sflags.insert(SFlag::S_IFDIR); + } + if sflags & (__WASI_FILETYPE_REGULAR_FILE as __wasi_filetype_t) != 0 { + nix_sflags.insert(SFlag::S_IFREG); + } + if sflags & (__WASI_FILETYPE_SYMBOLIC_LINK as __wasi_filetype_t) != 0 { + nix_sflags.insert(SFlag::S_IFLNK); + } + nix_sflags +} + +pub fn filestat_from_nix(filestat: nix::sys::stat::FileStat) -> __wasi_filestat_t { + let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode); + __wasi_filestat_t { + st_dev: filestat.st_dev as __wasi_device_t, + st_ino: filestat.st_ino as __wasi_inode_t, + st_nlink: filestat.st_nlink as __wasi_linkcount_t, + st_size: filestat.st_size as __wasi_filesize_t, + st_atim: filestat.st_atime as __wasi_timestamp_t, + st_ctim: filestat.st_ctime as __wasi_timestamp_t, + st_mtim: filestat.st_mtime as __wasi_timestamp_t, + st_filetype: filetype_from_nix(filetype), + } +} + +// Rights sets from wasmtime-wasi sandboxed system primitives. Transcribed because bindgen can't +// parse the #defines. + +pub const RIGHTS_ALL: __wasi_rights_t = (__WASI_RIGHT_FD_DATASYNC + | __WASI_RIGHT_FD_READ + | __WASI_RIGHT_FD_SEEK + | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS + | __WASI_RIGHT_FD_SYNC + | __WASI_RIGHT_FD_TELL + | __WASI_RIGHT_FD_WRITE + | __WASI_RIGHT_FD_ADVISE + | __WASI_RIGHT_FD_ALLOCATE + | __WASI_RIGHT_PATH_CREATE_DIRECTORY + | __WASI_RIGHT_PATH_CREATE_FILE + | __WASI_RIGHT_PATH_LINK_SOURCE + | __WASI_RIGHT_PATH_LINK_TARGET + | __WASI_RIGHT_PATH_OPEN + | __WASI_RIGHT_FD_READDIR + | __WASI_RIGHT_PATH_READLINK + | __WASI_RIGHT_PATH_RENAME_SOURCE + | __WASI_RIGHT_PATH_RENAME_TARGET + | __WASI_RIGHT_PATH_FILESTAT_GET + | __WASI_RIGHT_PATH_FILESTAT_SET_SIZE + | __WASI_RIGHT_PATH_FILESTAT_SET_TIMES + | __WASI_RIGHT_FD_FILESTAT_GET + | __WASI_RIGHT_FD_FILESTAT_SET_SIZE + | __WASI_RIGHT_FD_FILESTAT_SET_TIMES + | __WASI_RIGHT_PATH_SYMLINK + | __WASI_RIGHT_PATH_UNLINK_FILE + | __WASI_RIGHT_PATH_REMOVE_DIRECTORY + | __WASI_RIGHT_POLL_FD_READWRITE + | __WASI_RIGHT_SOCK_SHUTDOWN) as __wasi_rights_t; + +// Block and character device interaction is outside the scope of +// CloudABI. Simply allow everything. +pub const RIGHTS_BLOCK_DEVICE_BASE: __wasi_rights_t = RIGHTS_ALL; +pub const RIGHTS_BLOCK_DEVICE_INHERITING: __wasi_rights_t = RIGHTS_ALL; +pub const RIGHTS_CHARACTER_DEVICE_BASE: __wasi_rights_t = RIGHTS_ALL; +pub const RIGHTS_CHARACTER_DEVICE_INHERITING: __wasi_rights_t = RIGHTS_ALL; + +// Only allow directory operations on directories. Directories can only +// yield file descriptors to other directories and files. +pub const RIGHTS_DIRECTORY_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_FDSTAT_SET_FLAGS + | __WASI_RIGHT_FD_SYNC + | __WASI_RIGHT_FD_ADVISE + | __WASI_RIGHT_PATH_CREATE_DIRECTORY + | __WASI_RIGHT_PATH_CREATE_FILE + | __WASI_RIGHT_PATH_LINK_SOURCE + | __WASI_RIGHT_PATH_LINK_TARGET + | __WASI_RIGHT_PATH_OPEN + | __WASI_RIGHT_FD_READDIR + | __WASI_RIGHT_PATH_READLINK + | __WASI_RIGHT_PATH_RENAME_SOURCE + | __WASI_RIGHT_PATH_RENAME_TARGET + | __WASI_RIGHT_PATH_FILESTAT_GET + | __WASI_RIGHT_PATH_FILESTAT_SET_SIZE + | __WASI_RIGHT_PATH_FILESTAT_SET_TIMES + | __WASI_RIGHT_FD_FILESTAT_GET + | __WASI_RIGHT_FD_FILESTAT_SET_SIZE + | __WASI_RIGHT_FD_FILESTAT_SET_TIMES + | __WASI_RIGHT_PATH_SYMLINK + | __WASI_RIGHT_PATH_UNLINK_FILE + | __WASI_RIGHT_PATH_REMOVE_DIRECTORY + | __WASI_RIGHT_POLL_FD_READWRITE) + as __wasi_rights_t; +pub const RIGHTS_DIRECTORY_INHERITING: __wasi_rights_t = + (RIGHTS_DIRECTORY_BASE | RIGHTS_REGULAR_FILE_BASE); + +// Operations that apply to regular files. +pub const RIGHTS_REGULAR_FILE_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_DATASYNC + | __WASI_RIGHT_FD_READ + | __WASI_RIGHT_FD_SEEK + | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS + | __WASI_RIGHT_FD_SYNC + | __WASI_RIGHT_FD_TELL + | __WASI_RIGHT_FD_WRITE + | __WASI_RIGHT_FD_ADVISE + | __WASI_RIGHT_FD_ALLOCATE + | __WASI_RIGHT_FD_FILESTAT_GET + | __WASI_RIGHT_FD_FILESTAT_SET_SIZE + | __WASI_RIGHT_FD_FILESTAT_SET_TIMES + | __WASI_RIGHT_POLL_FD_READWRITE) + as __wasi_rights_t; +pub const RIGHTS_REGULAR_FILE_INHERITING: __wasi_rights_t = 0; + +// Operations that apply to shared memory objects. +pub const RIGHTS_SHARED_MEMORY_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_READ + | __WASI_RIGHT_FD_WRITE + | __WASI_RIGHT_FD_FILESTAT_GET + | __WASI_RIGHT_FD_FILESTAT_SET_SIZE) + as __wasi_rights_t; +pub const RIGHTS_SHARED_MEMORY_INHERITING: __wasi_rights_t = 0; + +// Operations that apply to sockets and socket pairs. +pub const RIGHTS_SOCKET_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_READ + | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS + | __WASI_RIGHT_FD_WRITE + | __WASI_RIGHT_FD_FILESTAT_GET + | __WASI_RIGHT_POLL_FD_READWRITE + | __WASI_RIGHT_SOCK_SHUTDOWN) + as __wasi_rights_t; +pub const RIGHTS_SOCKET_INHERITING: __wasi_rights_t = RIGHTS_ALL; + +// Operations that apply to TTYs. +pub const RIGHTS_TTY_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_READ + | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS + | __WASI_RIGHT_FD_WRITE + | __WASI_RIGHT_FD_FILESTAT_GET + | __WASI_RIGHT_POLL_FD_READWRITE) + as __wasi_rights_t; +pub const RIGHTS_TTY_INHERITING: __wasi_rights_t = 0; diff --git a/src/hostcalls.rs b/src/hostcalls.rs new file mode 100644 index 0000000000..68359ae3f0 --- /dev/null +++ b/src/hostcalls.rs @@ -0,0 +1,1360 @@ +//! Hostcalls that implement +//! [WASI](https://github.com/CraneStation/wasmtime-wasi/blob/wasi/docs/WASI-overview.md). +//! +//! This code borrows heavily from [wasmtime-wasi](https://github.com/CraneStation/wasmtime-wasi), +//! which in turn borrows from cloudabi-utils. See `LICENSE.wasmtime-wasi` for license information. +//! +//! This is currently a very incomplete prototype, only supporting the hostcalls required to run +//! `/examples/hello.c`, and using a bare-bones translation of the capabilities system rather than +//! something nice. + +#![allow(non_camel_case_types)] +#![allow(unused_unsafe)] +use crate::ctx::VmContext; +use crate::fdentry::{determine_type_rights, FdEntry}; +use crate::memory::*; +use crate::{host, wasm32}; + +use cast::From as _0; + +use nix::convert_ioctl_res; +use nix::libc::c_int; +use std::ffi::{OsStr, OsString}; +use std::os::unix::prelude::{FromRawFd, OsStrExt, OsStringExt, RawFd}; +use std::time::SystemTime; +use std::{cmp, slice}; + +#[cfg(target_os = "linux")] +const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC; + +#[cfg(not(target_os = "linux"))] +const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC; + +#[no_mangle] +pub unsafe extern "C" fn __wasi_proc_exit( + _vmctx: &mut VmContext, + rval: wasm32::__wasi_exitcode_t, +) -> () { + std::process::exit(dec_exitcode(rval) as i32); +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_args_get( + vmctx: *mut VmContext, + argv_ptr: wasm32::uintptr_t, + argv_buf: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let ctx = (*vmctx).as_wasi_ctx(); + + let mut argv_buf_offset = 0; + let mut argv = vec![]; + + for arg in ctx.args.iter() { + let arg_bytes = arg.as_bytes_with_nul(); + let arg_ptr = argv_buf + argv_buf_offset; + + if let Err(e) = unsafe { enc_slice_of(vmctx, arg_bytes, arg_ptr) } { + return enc_errno(e); + } + + argv.push(arg_ptr); + + argv_buf_offset = if let Some(new_offset) = argv_buf_offset.checked_add( + wasm32::uintptr_t::cast(arg_bytes.len()) + .expect("cast overflow would have been caught by `enc_slice_of` above"), + ) { + new_offset + } else { + return wasm32::__WASI_EOVERFLOW; + } + } + + unsafe { + enc_slice_of(vmctx, argv.as_slice(), argv_ptr) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_args_sizes_get( + vmctx: *mut VmContext, + argc_ptr: wasm32::uintptr_t, + argv_buf_size_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let ctx = (*vmctx).as_wasi_ctx(); + + let argc = ctx.args.len(); + let argv_size = ctx + .args + .iter() + .map(|arg| arg.as_bytes_with_nul().len()) + .sum(); + + unsafe { + if let Err(e) = enc_usize_byref(vmctx, argc_ptr, argc) { + return enc_errno(e); + } + if let Err(e) = enc_usize_byref(vmctx, argv_buf_size_ptr, argv_size) { + return enc_errno(e); + } + } + wasm32::__WASI_ESUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_clock_res_get( + vmctx: *mut VmContext, + clock_id: wasm32::__wasi_clockid_t, + resolution_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + // convert the supported clocks to the libc types, or return EINVAL + let clock_id = match dec_clockid(clock_id) { + host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME, + host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC, + host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID, + host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID, + _ => return wasm32::__WASI_EINVAL, + }; + + // no `nix` wrapper for clock_getres, so we do it ourselves + let mut timespec = unsafe { std::mem::uninitialized::() }; + let res = unsafe { libc::clock_getres(clock_id, &mut timespec as *mut libc::timespec) }; + if res != 0 { + return wasm32::errno_from_nix(nix::errno::Errno::last()); + } + + // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit + // from the spec but seems like it'll be an unusual situation to hit + (timespec.tv_sec as host::__wasi_timestamp_t) + .checked_mul(1_000_000_000) + .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) + .map(|resolution| { + // a supported clock can never return zero; this case will probably never get hit, but + // make sure we follow the spec + if resolution == 0 { + wasm32::__WASI_EINVAL + } else { + unsafe { + enc_timestamp_byref(vmctx, resolution_ptr, resolution) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } + } + }) + .unwrap_or(wasm32::__WASI_EOVERFLOW) +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_clock_time_get( + vmctx: *mut VmContext, + clock_id: wasm32::__wasi_clockid_t, + // ignored for now, but will be useful once we put optional limits on precision to reduce side + // channels + _precision: wasm32::__wasi_timestamp_t, + time_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + // convert the supported clocks to the libc types, or return EINVAL + let clock_id = match dec_clockid(clock_id) { + host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME, + host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC, + host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID, + host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID, + _ => return wasm32::__WASI_EINVAL, + }; + + // no `nix` wrapper for clock_getres, so we do it ourselves + let mut timespec = unsafe { std::mem::uninitialized::() }; + let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; + if res != 0 { + return wasm32::errno_from_nix(nix::errno::Errno::last()); + } + + // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit + // from the spec but seems like it'll be an unusual situation to hit + (timespec.tv_sec as host::__wasi_timestamp_t) + .checked_mul(1_000_000_000) + .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) + .map(|time| unsafe { + enc_timestamp_byref(vmctx, time_ptr, time) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + }) + .unwrap_or(wasm32::__WASI_EOVERFLOW) +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_environ_get( + vmctx: *mut VmContext, + environ_ptr: wasm32::uintptr_t, + environ_buf: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let ctx = (*vmctx).as_wasi_ctx(); + + let mut environ_buf_offset = 0; + let mut environ = vec![]; + + for pair in ctx.env.iter() { + let env_bytes = pair.as_bytes_with_nul(); + let env_ptr = environ_buf + environ_buf_offset; + + if let Err(e) = unsafe { enc_slice_of(vmctx, env_bytes, env_ptr) } { + return enc_errno(e); + } + + environ.push(env_ptr); + + environ_buf_offset = if let Some(new_offset) = environ_buf_offset.checked_add( + wasm32::uintptr_t::cast(env_bytes.len()) + .expect("cast overflow would have been caught by `enc_slice_of` above"), + ) { + new_offset + } else { + return wasm32::__WASI_EOVERFLOW; + } + } + + unsafe { + enc_slice_of(vmctx, environ.as_slice(), environ_ptr) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_environ_sizes_get( + vmctx: *mut VmContext, + environ_count_ptr: wasm32::uintptr_t, + environ_size_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let ctx = (*vmctx).as_wasi_ctx(); + + let environ_count = ctx.env.len(); + if let Some(environ_size) = ctx.env.iter().try_fold(0, |acc: u32, pair| { + acc.checked_add(pair.as_bytes_with_nul().len() as u32) + }) { + unsafe { + if let Err(e) = enc_usize_byref(vmctx, environ_count_ptr, environ_count) { + return enc_errno(e); + } + if let Err(e) = enc_usize_byref(vmctx, environ_size_ptr, environ_size as usize) { + return enc_errno(e); + } + } + wasm32::__WASI_ESUCCESS + } else { + wasm32::__WASI_EOVERFLOW + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_fd_close( + vmctx: *mut VmContext, + fd: wasm32::__wasi_fd_t, +) -> wasm32::__wasi_errno_t { + let mut ctx = (*vmctx).as_wasi_ctx_mut(); + let fd = dec_fd(fd); + if let Some(fdent) = ctx.fds.get(&fd) { + // can't close preopened files + if fdent.preopen_path.is_some() { + return wasm32::__WASI_ENOTSUP; + } + } + if let Some(mut fdent) = ctx.fds.remove(&fd) { + fdent.fd_object.needs_close = false; + match nix::unistd::close(fdent.fd_object.rawfd) { + Ok(_) => wasm32::__WASI_ESUCCESS, + Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), + } + } else { + wasm32::__WASI_EBADF + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_fd_fdstat_get( + vmctx: *mut VmContext, + fd: wasm32::__wasi_fd_t, + fdstat_ptr: wasm32::uintptr_t, // *mut wasm32::__wasi_fdstat_t +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let mut host_fdstat = match unsafe { dec_fdstat_byref(vmctx, fdstat_ptr) } { + Ok(host_fdstat) => host_fdstat, + Err(e) => return enc_errno(e), + }; + + let ctx = (*vmctx).as_wasi_ctx_mut(); + let errno = if let Some(fe) = ctx.fds.get(&host_fd) { + host_fdstat.fs_filetype = fe.fd_object.ty; + host_fdstat.fs_rights_base = fe.rights_base; + host_fdstat.fs_rights_inheriting = fe.rights_inheriting; + use nix::fcntl::{fcntl, OFlag, F_GETFL}; + match fcntl(fe.fd_object.rawfd, F_GETFL).map(OFlag::from_bits_truncate) { + Ok(flags) => { + host_fdstat.fs_flags = host::fdflags_from_nix(flags); + wasm32::__WASI_ESUCCESS + } + Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), + } + } else { + wasm32::__WASI_EBADF + }; + + unsafe { + enc_fdstat_byref(vmctx, fdstat_ptr, host_fdstat) + .expect("can write back into the pointer we read from"); + } + + errno +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_fd_fdstat_set_flags( + vmctx: &mut VmContext, + fd: wasm32::__wasi_fd_t, + fdflags: wasm32::__wasi_fdflags_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let host_fdflags = dec_fdflags(fdflags); + let nix_flags = host::nix_from_fdflags(host_fdflags); + + let ctx = vmctx.as_wasi_ctx_mut(); + + if let Some(fe) = ctx.fds.get(&host_fd) { + match nix::fcntl::fcntl(fe.fd_object.rawfd, nix::fcntl::F_SETFL(nix_flags)) { + Ok(_) => wasm32::__WASI_ESUCCESS, + Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), + } + } else { + wasm32::__WASI_EBADF + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_fd_seek( + vmctx: *mut VmContext, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filedelta_t, + whence: wasm32::__wasi_whence_t, + newoffset: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let ctx = (*vmctx).as_wasi_ctx_mut(); + let fd = dec_fd(fd); + let offset = dec_filedelta(offset); + let whence = dec_whence(whence); + + let host_newoffset = { + use nix::unistd::{lseek, Whence}; + let nwhence = match whence as u32 { + host::__WASI_WHENCE_CUR => Whence::SeekCur, + host::__WASI_WHENCE_END => Whence::SeekEnd, + host::__WASI_WHENCE_SET => Whence::SeekSet, + _ => return wasm32::__WASI_EINVAL, + }; + + let rights = if offset == 0 && whence as u32 == host::__WASI_WHENCE_CUR { + host::__WASI_RIGHT_FD_TELL + } else { + host::__WASI_RIGHT_FD_SEEK | host::__WASI_RIGHT_FD_TELL + }; + match ctx.get_fd_entry(fd, rights.into(), 0) { + Ok(fe) => match lseek(fe.fd_object.rawfd, offset, nwhence) { + Ok(newoffset) => newoffset, + Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), + }, + Err(e) => return enc_errno(e), + } + }; + + unsafe { + enc_filesize_byref(vmctx, newoffset, host_newoffset as u64) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_fd_prestat_get( + vmctx: *mut VmContext, + fd: wasm32::__wasi_fd_t, + prestat_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let ctx = (*vmctx).as_wasi_ctx(); + let fd = dec_fd(fd); + // TODO: is this the correct right for this? + match ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { + Ok(fe) => { + if let Some(po_path) = &fe.preopen_path { + if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY as host::__wasi_filetype_t { + return wasm32::__WASI_ENOTDIR; + } + unsafe { + enc_prestat_byref( + vmctx, + prestat_ptr, + host::__wasi_prestat_t { + pr_type: host::__WASI_PREOPENTYPE_DIR as host::__wasi_preopentype_t, + u: host::__wasi_prestat_t___wasi_prestat_u { + dir: + host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { + pr_name_len: po_path.as_os_str().as_bytes().len(), + }, + }, + }, + ) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } + } else { + wasm32::__WASI_ENOTSUP + } + } + Err(e) => enc_errno(e), + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_fd_prestat_dir_name( + vmctx: *mut VmContext, + fd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + + match (*vmctx) + .as_wasi_ctx() + .get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) + { + Ok(fe) => { + if let Some(po_path) = &fe.preopen_path { + if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY as host::__wasi_filetype_t { + return wasm32::__WASI_ENOTDIR; + } + let path_bytes = po_path.as_os_str().as_bytes(); + if path_bytes.len() > dec_usize(path_len) { + return wasm32::__WASI_ENAMETOOLONG; + } + unsafe { + enc_slice_of(vmctx, path_bytes, path_ptr) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } + } else { + wasm32::__WASI_ENOTSUP + } + } + Err(e) => enc_errno(e), + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_fd_read( + vmctx: *mut VmContext, + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nread: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::uio::{readv, IoVec}; + + let fd = dec_fd(fd); + let mut iovs = match unsafe { dec_ciovec_slice(vmctx, iovs_ptr, iovs_len) } { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + + let mut ctx = (*vmctx).as_wasi_ctx_mut(); + let fe = match ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_READ.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + + let mut iovs: Vec> = iovs + .iter_mut() + .map(|iov| unsafe { host::ciovec_to_nix_mut(iov) }) + .collect(); + + let host_nread = match readv(fe.fd_object.rawfd, &mut iovs) { + Ok(len) => len, + Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), + }; + + if host_nread == 0 { + // we hit eof, so remove the fdentry from the context + let mut fe = ctx.fds.remove(&fd).expect("file entry is still there"); + fe.fd_object.needs_close = false; + } + + unsafe { + enc_usize_byref(vmctx, nread, host_nread) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_fd_write( + vmctx: *mut VmContext, + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nwritten: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::uio::{writev, IoVec}; + + let fd = dec_fd(fd); + let iovs = match unsafe { dec_ciovec_slice(vmctx, iovs_ptr, iovs_len) } { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + + let ctx = (*vmctx).as_wasi_ctx(); + let fe = match ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_WRITE.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + + let iovs: Vec> = iovs + .iter() + .map(|iov| unsafe { host::ciovec_to_nix(iov) }) + .collect(); + + let host_nwritten = match writev(fe.fd_object.rawfd, &iovs) { + Ok(len) => len, + Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), + }; + + unsafe { + enc_usize_byref(vmctx, nwritten, host_nwritten) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_path_open( + vmctx: *mut VmContext, + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + oflags: wasm32::__wasi_oflags_t, + fs_rights_base: wasm32::__wasi_rights_t, + fs_rights_inheriting: wasm32::__wasi_rights_t, + fs_flags: wasm32::__wasi_fdflags_t, + fd_out_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::errno::Errno; + use nix::fcntl::{openat, AtFlags, OFlag}; + use nix::sys::stat::{fstatat, Mode, SFlag}; + + let dirfd = dec_fd(dirfd); + let dirflags = dec_lookupflags(dirflags); + let oflags = dec_oflags(oflags); + let fs_rights_base = dec_rights(fs_rights_base); + let fs_rights_inheriting = dec_rights(fs_rights_inheriting); + let fs_flags = dec_fdflags(fs_flags); + + // which open mode do we need? + let read = fs_rights_base + & ((host::__WASI_RIGHT_FD_READ | host::__WASI_RIGHT_FD_READDIR) as host::__wasi_rights_t) + != 0; + let write = fs_rights_base + & ((host::__WASI_RIGHT_FD_DATASYNC + | host::__WASI_RIGHT_FD_WRITE + | host::__WASI_RIGHT_FD_ALLOCATE + | host::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE) as host::__wasi_rights_t) + != 0; + + let mut nix_all_oflags = if read && write { + OFlag::O_RDWR + } else if read { + OFlag::O_RDONLY + } else { + OFlag::O_WRONLY + }; + + // on non-Capsicum systems, we always want nofollow + nix_all_oflags.insert(OFlag::O_NOFOLLOW); + + // which rights are needed on the dirfd? + let mut needed_base = host::__WASI_RIGHT_PATH_OPEN as host::__wasi_rights_t; + let mut needed_inheriting = fs_rights_base | fs_rights_inheriting; + + // convert open flags + let nix_oflags = host::nix_from_oflags(oflags); + nix_all_oflags.insert(nix_oflags); + if nix_all_oflags.contains(OFlag::O_CREAT) { + needed_base |= host::__WASI_RIGHT_PATH_CREATE_FILE as host::__wasi_rights_t; + } + if nix_all_oflags.contains(OFlag::O_TRUNC) { + needed_inheriting |= host::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE as host::__wasi_rights_t; + } + + // convert file descriptor flags + nix_all_oflags.insert(host::nix_from_fdflags(fs_flags)); + if nix_all_oflags.contains(OFlag::O_DSYNC) { + needed_inheriting |= host::__WASI_RIGHT_FD_DATASYNC as host::__wasi_rights_t; + } + if nix_all_oflags.intersects(O_RSYNC | OFlag::O_SYNC) { + needed_inheriting |= host::__WASI_RIGHT_FD_SYNC as host::__wasi_rights_t; + } + + let path = match unsafe { dec_slice_of::(vmctx, path_ptr, path_len) } { + Ok((ptr, len)) => OsStr::from_bytes(unsafe { std::slice::from_raw_parts(ptr, len) }), + Err(e) => return enc_errno(e), + }; + + let (dir, path) = match path_get( + &*vmctx, + dirfd, + dirflags, + path, + needed_base, + needed_inheriting, + nix_oflags.contains(OFlag::O_CREAT), + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + + let new_fd = match openat( + dir, + path.as_os_str(), + nix_all_oflags, + Mode::from_bits_truncate(0o777), + ) { + Ok(fd) => fd, + Err(e) => { + match e.as_errno() { + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket + Some(Errno::ENXIO) => { + if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { + return wasm32::__WASI_ENOTSUP; + } else { + return wasm32::__WASI_ENXIO; + } + } else { + return wasm32::__WASI_ENXIO; + } + } + Some(e) => return wasm32::errno_from_nix(e), + None => return wasm32::__WASI_ENOSYS, + } + } + }; + + // Determine the type of the new file descriptor and which rights contradict with this type + let guest_fd = match unsafe { determine_type_rights(new_fd) } { + Err(e) => { + // if `close` fails, note it but do not override the underlying errno + nix::unistd::close(new_fd).unwrap_or_else(|e| { + dbg!(e); + }); + return enc_errno(e); + } + Ok((_ty, max_base, max_inheriting)) => { + let mut fe = unsafe { FdEntry::from_raw_fd(new_fd) }; + fe.rights_base &= max_base; + fe.rights_inheriting &= max_inheriting; + match (*vmctx).as_wasi_ctx_mut().insert_fd_entry(fe) { + Ok(fd) => fd, + Err(e) => return enc_errno(e), + } + } + }; + + unsafe { + enc_fd_byref(vmctx, fd_out_ptr, guest_fd) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_random_get( + vmctx: *mut VmContext, + buf_ptr: wasm32::uintptr_t, + buf_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + use rand::{thread_rng, RngCore}; + + let buf_len = dec_usize(buf_len); + let buf_ptr = match unsafe { (*vmctx).dec_ptr(buf_ptr, buf_len) } { + Ok(ptr) => ptr, + Err(e) => return enc_errno(e), + }; + + let buf = unsafe { std::slice::from_raw_parts_mut(buf_ptr, buf_len) }; + + thread_rng().fill_bytes(buf); + + return wasm32::__WASI_ESUCCESS; +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_poll_oneoff( + vmctx: *mut VmContext, + input: wasm32::uintptr_t, + output: wasm32::uintptr_t, + nsubscriptions: wasm32::size_t, + nevents: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + if nsubscriptions as u64 > wasm32::__wasi_filesize_t::max_value() { + return wasm32::__WASI_EINVAL; + } + unsafe { enc_pointee(vmctx, nevents, 0) }.unwrap(); + let input_slice_ = + unsafe { dec_slice_of::(vmctx, input, nsubscriptions) } + .unwrap(); + let input_slice = unsafe { slice::from_raw_parts(input_slice_.0, input_slice_.1) }; + + let output_slice_ = + unsafe { dec_slice_of::(vmctx, output, nsubscriptions) }.unwrap(); + let output_slice = unsafe { slice::from_raw_parts_mut(output_slice_.0, output_slice_.1) }; + + let input: Vec<_> = input_slice.iter().map(|x| dec_subscription(x)).collect(); + + let timeout = input + .iter() + .filter_map(|event| match event { + Ok(event) if event.type_ == wasm32::__WASI_EVENTTYPE_CLOCK => Some(ClockEventData { + delay: wasi_clock_to_relative_ns_delay(unsafe { event.u.clock }) / 1_000_000, + userdata: event.userdata, + }), + _ => None, + }) + .min_by_key(|event| event.delay); + let fd_events: Vec<_> = input + .iter() + .filter_map(|event| match event { + Ok(event) + if event.type_ == wasm32::__WASI_EVENTTYPE_FD_READ + || event.type_ == wasm32::__WASI_EVENTTYPE_FD_WRITE => + { + Some(FdEventData { + fd: unsafe { event.u.fd_readwrite.fd } as c_int, + type_: event.type_, + userdata: event.userdata, + }) + } + _ => None, + }) + .collect(); + if fd_events.is_empty() && timeout.is_none() { + return wasm32::__WASI_ESUCCESS; + } + let mut poll_fds: Vec<_> = fd_events + .iter() + .map(|event| { + let mut flags = nix::poll::EventFlags::empty(); + match event.type_ { + wasm32::__WASI_EVENTTYPE_FD_READ => flags.insert(nix::poll::EventFlags::POLLIN), + wasm32::__WASI_EVENTTYPE_FD_WRITE => flags.insert(nix::poll::EventFlags::POLLOUT), + // An event on a file descriptor can currently only be of type FD_READ or FD_WRITE + // Nothing else has been defined in the specification, and these are also the only two + // events we filtered before. If we get something else here, the code has a serious bug. + _ => unreachable!(), + }; + nix::poll::PollFd::new(event.fd, flags) + }) + .collect(); + let timeout = timeout.map(|ClockEventData { delay, userdata }| ClockEventData { + delay: cmp::min(delay, c_int::max_value() as u128), + userdata, + }); + let poll_timeout = timeout.map(|timeout| timeout.delay as c_int).unwrap_or(-1); + let ready = loop { + match nix::poll::poll(&mut poll_fds, poll_timeout) { + Err(_) => { + if nix::errno::Errno::last() == nix::errno::Errno::EINTR { + continue; + } + return wasm32::errno_from_nix(nix::errno::Errno::last()); + } + Ok(ready) => break ready as usize, + } + }; + if ready == 0 { + return __wasi_poll_oneoff_handle_timeout_event( + &mut *vmctx, + output_slice, + nevents, + timeout, + ); + } + let events = fd_events.iter().zip(poll_fds.iter()).take(ready); + __wasi_poll_oneoff_handle_fd_event(&mut *vmctx, output_slice, nevents, events) +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_fd_filestat_get( + vmctx: *mut VmContext, + fd: wasm32::__wasi_fd_t, + filestat_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::stat::fstat; + + let host_fd = dec_fd(fd); + let ctx = (*vmctx).as_wasi_ctx_mut(); + + let errno = if let Some(fe) = ctx.fds.get(&host_fd) { + match fstat(fe.fd_object.rawfd) { + Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), + Ok(filestat) => { + let host_filestat = host::filestat_from_nix(filestat); + unsafe { + enc_filestat_byref(vmctx, filestat_ptr, host_filestat) + .expect("can write into the pointer"); + } + wasm32::__WASI_ESUCCESS + } + } + } else { + wasm32::__WASI_EBADF + }; + errno +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_path_filestat_get( + vmctx: *mut VmContext, + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + filestat_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::fcntl::AtFlags; + use nix::sys::stat::fstatat; + + let dirfd = dec_fd(dirfd); + let dirflags = dec_lookupflags(dirflags); + let path = match unsafe { dec_slice_of::(vmctx, path_ptr, path_len) } { + Ok((ptr, len)) => OsStr::from_bytes(unsafe { std::slice::from_raw_parts(ptr, len) }), + Err(e) => return enc_errno(e), + }; + let (dir, path) = match path_get( + &*vmctx, + dirfd, + dirflags, + path, + host::__WASI_RIGHT_PATH_FILESTAT_GET as host::__wasi_rights_t, + 0, + false, + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let atflags = match dirflags { + 0 => AtFlags::empty(), + _ => AtFlags::AT_SYMLINK_NOFOLLOW, + }; + match fstatat(dir, path.as_os_str(), atflags) { + Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), + Ok(filestat) => { + let host_filestat = host::filestat_from_nix(filestat); + unsafe { + enc_filestat_byref(vmctx, filestat_ptr, host_filestat) + .expect("can write into the pointer"); + } + wasm32::__WASI_ESUCCESS + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_path_create_directory( + vmctx: *mut VmContext, + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + use nix::errno; + use nix::libc::mkdirat; + + let dirfd = dec_fd(dirfd); + let path = match unsafe { dec_slice_of::(vmctx, path_ptr, path_len) } { + Ok((ptr, len)) => OsStr::from_bytes(unsafe { std::slice::from_raw_parts(ptr, len) }), + Err(e) => return enc_errno(e), + }; + let (dir, path) = match path_get( + &*vmctx, + dirfd, + 0, + path, + (host::__WASI_RIGHT_PATH_OPEN | host::__WASI_RIGHT_PATH_CREATE_DIRECTORY) + as host::__wasi_rights_t, + 0, + false, + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + // nix doesn't expose mkdirat() yet + match unsafe { mkdirat(dir, path_cstr.as_ptr(), 0o777) } { + 0 => wasm32::__WASI_ESUCCESS, + _ => wasm32::errno_from_nix(errno::Errno::last()), + } +} + +#[no_mangle] +pub unsafe extern "C" fn __wasi_path_unlink_file( + vmctx: *mut VmContext, + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + use nix::errno; + use nix::libc::unlinkat; + + let dirfd = dec_fd(dirfd); + let path = match unsafe { dec_slice_of::(vmctx, path_ptr, path_len) } { + Ok((ptr, len)) => OsStr::from_bytes(unsafe { std::slice::from_raw_parts(ptr, len) }), + Err(e) => return enc_errno(e), + }; + let (dir, path) = match path_get( + &*vmctx, + dirfd, + 0, + path, + host::__WASI_RIGHT_PATH_UNLINK_FILE as host::__wasi_rights_t, + 0, + false, + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + // nix doesn't expose unlinkat() yet + match unsafe { unlinkat(dir, path_cstr.as_ptr(), 0) } { + 0 => wasm32::__WASI_ESUCCESS, + _ => wasm32::errno_from_nix(errno::Errno::last()), + } +} + +// define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)` +nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int); + +fn wasi_clock_to_relative_ns_delay( + wasi_clock: host::__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, +) -> u128 { + if wasi_clock.flags != wasm32::__WASI_SUBSCRIPTION_CLOCK_ABSTIME { + return wasi_clock.timeout as u128; + } + let now: u128 = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Current date is before the epoch") + .as_nanos(); + let deadline = wasi_clock.timeout as u128; + deadline.saturating_sub(now) +} + +#[derive(Debug, Copy, Clone)] +struct ClockEventData { + delay: u128, + userdata: host::__wasi_userdata_t, +} +#[derive(Debug, Copy, Clone)] +struct FdEventData { + fd: c_int, + type_: host::__wasi_eventtype_t, + userdata: host::__wasi_userdata_t, +} + +fn __wasi_poll_oneoff_handle_timeout_event( + vmctx: *mut VmContext, + output_slice: &mut [wasm32::__wasi_event_t], + nevents: wasm32::uintptr_t, + timeout: Option, +) -> wasm32::__wasi_errno_t { + if let Some(ClockEventData { userdata, .. }) = timeout { + let output_event = host::__wasi_event_t { + userdata, + type_: wasm32::__WASI_EVENTTYPE_CLOCK, + error: wasm32::__WASI_ESUCCESS, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: 0, + flags: 0, + }, + }, + }; + output_slice[0] = enc_event(output_event); + if let Err(e) = unsafe { enc_pointee(vmctx, nevents, 1) } { + return enc_errno(e); + } + } else { + // shouldn't happen + if let Err(e) = unsafe { enc_pointee(vmctx, nevents, 0) } { + return enc_errno(e); + } + } + wasm32::__WASI_ESUCCESS +} + +fn __wasi_poll_oneoff_handle_fd_event<'t>( + vmctx: *mut VmContext, + output_slice: &mut [wasm32::__wasi_event_t], + nevents: wasm32::uintptr_t, + events: impl Iterator, +) -> wasm32::__wasi_errno_t { + let mut output_slice_cur = output_slice.iter_mut(); + let mut revents_count = 0; + for (fd_event, poll_fd) in events { + let revents = match poll_fd.revents() { + Some(revents) => revents, + None => continue, + }; + let mut nbytes = 0; + if fd_event.type_ == wasm32::__WASI_EVENTTYPE_FD_READ { + let _ = unsafe { fionread(fd_event.fd, &mut nbytes) }; + } + let output_event = if revents.contains(nix::poll::EventFlags::POLLNVAL) { + host::__wasi_event_t { + userdata: fd_event.userdata, + type_: fd_event.type_, + error: wasm32::__WASI_EBADF, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: + host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: 0, + flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP, + }, + }, + } + } else if revents.contains(nix::poll::EventFlags::POLLERR) { + host::__wasi_event_t { + userdata: fd_event.userdata, + type_: fd_event.type_, + error: wasm32::__WASI_EIO, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: + host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: 0, + flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP, + }, + }, + } + } else if revents.contains(nix::poll::EventFlags::POLLHUP) { + host::__wasi_event_t { + userdata: fd_event.userdata, + type_: fd_event.type_, + error: wasm32::__WASI_ESUCCESS, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: + host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: 0, + flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP, + }, + }, + } + } else if revents.contains(nix::poll::EventFlags::POLLIN) + | revents.contains(nix::poll::EventFlags::POLLOUT) + { + host::__wasi_event_t { + userdata: fd_event.userdata, + type_: fd_event.type_, + error: wasm32::__WASI_ESUCCESS, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: + host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: nbytes as host::__wasi_filesize_t, + flags: 0, + }, + }, + } + } else { + continue; + }; + *output_slice_cur.next().unwrap() = enc_event(output_event); + revents_count += 1; + } + if let Err(e) = unsafe { enc_pointee(vmctx, nevents, revents_count) } { + return enc_errno(e); + } + wasm32::__WASI_ESUCCESS +} + +/// Normalizes a path to ensure that the target path is located under the directory provided. +/// +/// This is a workaround for not having Capsicum support in the OS. +pub fn path_get>( + vmctx: &VmContext, + dirfd: host::__wasi_fd_t, + dirflags: host::__wasi_lookupflags_t, + path: P, + needed_base: host::__wasi_rights_t, + needed_inheriting: host::__wasi_rights_t, + needs_final_component: bool, +) -> Result<(RawFd, OsString), host::__wasi_errno_t> { + use nix::errno::Errno; + use nix::fcntl::{openat, readlinkat, OFlag}; + use nix::sys::stat::Mode; + + const MAX_SYMLINK_EXPANSIONS: usize = 128; + + /// close all the intermediate file descriptors, but make sure not to drop either the original + /// dirfd or the one we return (which may be the same dirfd) + fn ret_dir_success(dir_stack: &mut Vec) -> RawFd { + let ret_dir = dir_stack.pop().expect("there is always a dirfd to return"); + if let Some(dirfds) = dir_stack.get(1..) { + for dirfd in dirfds { + nix::unistd::close(*dirfd).unwrap_or_else(|e| { + dbg!(e); + }); + } + } + ret_dir + } + + /// close all file descriptors other than the base directory, and return the errno for + /// convenience with `return` + fn ret_error( + dir_stack: &mut Vec, + errno: host::__wasi_errno_t, + ) -> Result<(RawFd, OsString), host::__wasi_errno_t> { + if let Some(dirfds) = dir_stack.get(1..) { + for dirfd in dirfds { + nix::unistd::close(*dirfd).unwrap_or_else(|e| { + dbg!(e); + }); + } + } + Err(errno) + } + + let ctx = vmctx.as_wasi_ctx(); + + let dirfe = ctx.get_fd_entry(dirfd, needed_base, needed_inheriting)?; + + // Stack of directory file descriptors. Index 0 always corresponds with the directory provided + // to this function. Entering a directory causes a file descriptor to be pushed, while handling + // ".." entries causes an entry to be popped. Index 0 cannot be popped, as this would imply + // escaping the base directory. + let mut dir_stack = vec![dirfe.fd_object.rawfd]; + + // Stack of paths left to process. This is initially the `path` argument to this function, but + // any symlinks we encounter are processed by pushing them on the stack. + let mut path_stack = vec![path.as_ref().to_owned().into_vec()]; + + // Track the number of symlinks we've expanded, so we can return `ELOOP` after too many. + let mut symlink_expansions = 0; + + // Buffer to read links into; defined outside of the loop so we don't reallocate it constantly. + let mut readlink_buf = vec![0u8; libc::PATH_MAX as usize + 1]; + + // TODO: rewrite this using a custom posix path type, with a component iterator that respects + // trailing slashes. This version does way too much allocation, and is way too fiddly. + loop { + let component = if let Some(cur_path) = path_stack.pop() { + // eprintln!( + // "cur_path = {:?}", + // std::str::from_utf8(cur_path.as_slice()).unwrap() + // ); + let mut split = cur_path.splitn(2, |&c| c == '/' as u8); + let head = split.next(); + let tail = split.next(); + match (head, tail) { + (None, _) => { + // split always returns at least a singleton iterator with an empty slice + panic!("unreachable"); + } + // path is empty + (Some([]), None) => { + return ret_error(&mut dir_stack, host::__WASI_ENOENT as host::__wasi_errno_t); + } + // path starts with `/`, is absolute + (Some([]), Some(_)) => { + return ret_error( + &mut dir_stack, + host::__WASI_ENOTCAPABLE as host::__wasi_errno_t, + ); + } + // the final component of the path with no trailing slash + (Some(component), None) => component.to_vec(), + (Some(component), Some(rest)) => { + if rest.iter().all(|&c| c == '/' as u8) { + // the final component of the path with trailing slashes; put one trailing + // slash back on + let mut component = component.to_vec(); + component.push('/' as u8); + component + } else { + // non-final component; push the rest back on the stack + path_stack.push(rest.to_vec()); + component.to_vec() + } + } + } + } else { + // if the path stack is ever empty, we return rather than going through the loop again + panic!("unreachable"); + }; + + // eprintln!( + // "component = {:?}", + // std::str::from_utf8(component.as_slice()).unwrap() + // ); + + match component.as_slice() { + b"." => { + // skip component + } + b".." => { + // pop a directory + let dirfd = dir_stack.pop().expect("dir_stack is never empty"); + + // we're not allowed to pop past the original directory + if dir_stack.is_empty() { + return ret_error( + &mut dir_stack, + host::__WASI_ENOTCAPABLE as host::__wasi_errno_t, + ); + } else { + nix::unistd::close(dirfd).unwrap_or_else(|e| { + dbg!(e); + }); + } + } + // should the component be a directory? it should if there is more path left to process, or + // if it has a trailing slash and `needs_final_component` is not set + component + if !path_stack.is_empty() + || (component.ends_with(b"/") && !needs_final_component) => + { + match openat( + *dir_stack.first().expect("dir_stack is never empty"), + component, + OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW, + Mode::empty(), + ) { + Ok(new_dir) => { + dir_stack.push(new_dir); + continue; + } + Err(e) + if e.as_errno() == Some(Errno::ELOOP) + || e.as_errno() == Some(Errno::EMLINK) => + { + // attempt symlink expansion + match readlinkat( + *dir_stack.last().expect("dir_stack is never empty"), + component, + readlink_buf.as_mut_slice(), + ) { + Ok(link_path) => { + symlink_expansions += 1; + if symlink_expansions > MAX_SYMLINK_EXPANSIONS { + return ret_error( + &mut dir_stack, + host::__WASI_ELOOP as host::__wasi_errno_t, + ); + } + + let mut link_path = link_path.as_bytes().to_vec(); + + // append a trailing slash if the component leading to it has one, so + // that we preserve any ENOTDIR that might come from trying to open a + // non-directory + if component.ends_with(b"/") { + link_path.push('/' as u8); + } + + path_stack.push(link_path); + continue; + } + Err(e) => { + return ret_error( + &mut dir_stack, + host::errno_from_nix(e.as_errno().unwrap()), + ); + } + } + } + Err(e) => { + return ret_error( + &mut dir_stack, + host::errno_from_nix(e.as_errno().unwrap()), + ); + } + } + } + // the final component + component => { + // if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt + // symlink expansion + if component.ends_with(b"/") || (dirflags & host::__WASI_LOOKUP_SYMLINK_FOLLOW) != 0 + { + match readlinkat( + *dir_stack.last().expect("dir_stack is never empty"), + component, + readlink_buf.as_mut_slice(), + ) { + Ok(link_path) => { + symlink_expansions += 1; + if symlink_expansions > MAX_SYMLINK_EXPANSIONS { + return ret_error( + &mut dir_stack, + host::__WASI_ELOOP as host::__wasi_errno_t, + ); + } + + let mut link_path = link_path.as_bytes().to_vec(); + + // append a trailing slash if the component leading to it has one, so + // that we preserve any ENOTDIR that might come from trying to open a + // non-directory + if component.ends_with(b"/") { + link_path.push('/' as u8); + } + + path_stack.push(link_path); + continue; + } + Err(e) => { + let errno = e.as_errno().unwrap(); + if errno != Errno::EINVAL && errno != Errno::ENOENT { + // only return an error if this path is not actually a symlink + return ret_error(&mut dir_stack, host::errno_from_nix(errno)); + } + } + } + } + + // not a symlink, so we're done; + return Ok(( + ret_dir_success(&mut dir_stack), + OsStr::from_bytes(component).to_os_string(), + )); + } + } + + if path_stack.is_empty() { + // no further components to process. means we've hit a case like "." or "a/..", or if the + // input path has trailing slashes and `needs_final_component` is not set + return Ok(( + ret_dir_success(&mut dir_stack), + OsStr::new(".").to_os_string(), + )); + } else { + continue; + } + } +} + +#[doc(hidden)] +pub fn ensure_linked() { + unsafe { + std::ptr::read_volatile(__wasi_proc_exit as *const extern "C" fn()); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000..71c68f480d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,8 @@ +pub mod ctx; +pub mod fdentry; +pub mod host; +pub mod hostcalls; +pub mod memory; +pub mod wasm32; + +pub use ctx::{WasiCtx, WasiCtxBuilder}; diff --git a/src/memory.rs b/src/memory.rs new file mode 100644 index 0000000000..7d7492d8dd --- /dev/null +++ b/src/memory.rs @@ -0,0 +1,479 @@ +//! Functions to go back and forth between WASI types in host and wasm32 representations. +//! +//! This module is an adaptation of the `wasmtime-wasi` module +//! [`translate.rs`](https://github.com/CraneStation/wasmtime-wasi/blob/1a6ecf3a0378d71f3fc1ba25ce76a2b43e4166b8/lib/wasi/src/translate.rs); +//! its license file `LICENSE.wasmtime-wasi` is included in this project. +//! +//! Any of these functions that take a `Vmctx` argument are only meant to be called from within a +//! hostcall. +//! +//! This sort of manual encoding will hopefully be obsolete once the IDL is developed. + +use crate::ctx::VmContext; +use crate::{host, wasm32}; + +use cast; +use cast::From as _0; +use std::mem::{align_of, size_of}; +use std::slice; + +macro_rules! bail_errno { + ( $errno:ident ) => { + return Err(host::$errno as host::__wasi_errno_t); + }; +} + +pub unsafe fn dec_ptr_to( + vmctx: *mut VmContext, + ptr: wasm32::uintptr_t, +) -> Result<*mut T, host::__wasi_errno_t> { + // check that the ptr is aligned + if ptr as usize % align_of::() != 0 { + bail_errno!(__WASI_EINVAL); + } + (*vmctx).dec_ptr(ptr, size_of::()).map(|p| p as *mut T) +} + +pub unsafe fn dec_pointee( + vmctx: *mut VmContext, + ptr: wasm32::uintptr_t, +) -> Result { + dec_ptr_to::(vmctx, ptr).map(|p| p.read()) +} + +pub unsafe fn enc_pointee( + vmctx: *mut VmContext, + ptr: wasm32::uintptr_t, + t: T, +) -> Result<(), host::__wasi_errno_t> { + dec_ptr_to::(vmctx, ptr).map(|p| p.write(t)) +} + +pub unsafe fn dec_slice_of( + vmctx: *mut VmContext, + ptr: wasm32::uintptr_t, + len: wasm32::size_t, +) -> Result<(*mut T, usize), host::__wasi_errno_t> { + // check alignment, and that length doesn't overflow + if ptr as usize % align_of::() != 0 { + return Err(host::__WASI_EINVAL as host::__wasi_errno_t); + } + let len = dec_usize(len); + let len_bytes = if let Some(len) = size_of::().checked_mul(len) { + len + } else { + return Err(host::__WASI_EOVERFLOW as host::__wasi_errno_t); + }; + + let ptr = (*vmctx).dec_ptr(ptr, len_bytes)? as *mut T; + + Ok((ptr, len)) +} + +pub unsafe fn enc_slice_of( + vmctx: *mut VmContext, + slice: &[T], + ptr: wasm32::uintptr_t, +) -> Result<(), host::__wasi_errno_t> { + // check alignment + if ptr as usize % align_of::() != 0 { + return Err(host::__WASI_EINVAL as host::__wasi_errno_t); + } + // check that length doesn't overflow + let len_bytes = if let Some(len) = size_of::().checked_mul(slice.len()) { + len + } else { + return Err(host::__WASI_EOVERFLOW as host::__wasi_errno_t); + }; + + // get the pointer into guest memory, and copy the bytes + let ptr = (*vmctx).dec_ptr(ptr, len_bytes)? as *mut libc::c_void; + libc::memcpy(ptr, slice.as_ptr() as *const libc::c_void, len_bytes); + + Ok(()) +} + +macro_rules! dec_enc_scalar { + ( $ty:ident, $dec:ident, $dec_byref:ident, $enc:ident, $enc_byref:ident) => { + pub fn $dec(x: wasm32::$ty) -> host::$ty { + host::$ty::from_le(x) + } + + pub unsafe fn $dec_byref( + vmctx: *mut VmContext, + ptr: wasm32::uintptr_t, + ) -> Result { + dec_pointee::(vmctx, ptr).map($dec) + } + + pub fn $enc(x: host::$ty) -> wasm32::$ty { + x.to_le() + } + + pub unsafe fn $enc_byref( + vmctx: *mut VmContext, + ptr: wasm32::uintptr_t, + x: host::$ty, + ) -> Result<(), host::__wasi_errno_t> { + enc_pointee::(vmctx, ptr, $enc(x)) + } + }; +} + +pub unsafe fn dec_ciovec( + vmctx: *mut VmContext, + ciovec: &wasm32::__wasi_ciovec_t, +) -> Result { + let len = dec_usize(ciovec.buf_len); + Ok(host::__wasi_ciovec_t { + buf: (*vmctx).dec_ptr(ciovec.buf, len)? as *const host::void, + buf_len: len, + }) +} + +pub unsafe fn dec_ciovec_slice( + vmctx: *mut VmContext, + ptr: wasm32::uintptr_t, + len: wasm32::size_t, +) -> Result, host::__wasi_errno_t> { + let slice = dec_slice_of::(vmctx, ptr, len)?; + let slice = slice::from_raw_parts(slice.0, slice.1); + slice.iter().map(|iov| dec_ciovec(vmctx, iov)).collect() +} + +dec_enc_scalar!( + __wasi_clockid_t, + dec_clockid, + dec_clockid_byref, + enc_clockid, + enc_clockid_byref +); +dec_enc_scalar!( + __wasi_errno_t, + dec_errno, + dec_errno_byref, + enc_errno, + enc_errno_byref +); +dec_enc_scalar!( + __wasi_exitcode_t, + dec_exitcode, + dec_exitcode_byref, + enc_exitcode, + enc_exitcode_byref +); +dec_enc_scalar!(__wasi_fd_t, dec_fd, dec_fd_byref, enc_fd, enc_fd_byref); +dec_enc_scalar!( + __wasi_fdflags_t, + dec_fdflags, + dec_fdflags_byref, + enc_fdflags, + enc_fdflags_byref +); +dec_enc_scalar!( + __wasi_device_t, + dec_device, + dev_device_byref, + enc_device, + enc_device_byref +); +dec_enc_scalar!( + __wasi_inode_t, + dec_inode, + dev_inode_byref, + enc_inode, + enc_inode_byref +); +dec_enc_scalar!( + __wasi_linkcount_t, + dec_linkcount, + dev_linkcount_byref, + enc_linkcount, + enc_linkcount_byref +); + +pub fn dec_filestat(filestat: wasm32::__wasi_filestat_t) -> host::__wasi_filestat_t { + host::__wasi_filestat_t { + st_dev: dec_device(filestat.st_dev), + st_ino: dec_inode(filestat.st_ino), + st_filetype: dec_filetype(filestat.st_filetype), + st_nlink: dec_linkcount(filestat.st_nlink), + st_size: dec_filesize(filestat.st_size), + st_atim: dec_timestamp(filestat.st_atim), + st_mtim: dec_timestamp(filestat.st_mtim), + st_ctim: dec_timestamp(filestat.st_ctim), + } +} + +pub unsafe fn dec_filestat_byref( + vmctx: *mut VmContext, + filestat_ptr: wasm32::uintptr_t, +) -> Result { + dec_pointee::(vmctx, filestat_ptr).map(dec_filestat) +} + +pub fn enc_filestat(filestat: host::__wasi_filestat_t) -> wasm32::__wasi_filestat_t { + wasm32::__wasi_filestat_t { + st_dev: enc_device(filestat.st_dev), + st_ino: enc_inode(filestat.st_ino), + st_filetype: enc_filetype(filestat.st_filetype), + st_nlink: enc_linkcount(filestat.st_nlink), + st_size: enc_filesize(filestat.st_size), + st_atim: enc_timestamp(filestat.st_atim), + st_mtim: enc_timestamp(filestat.st_mtim), + st_ctim: enc_timestamp(filestat.st_ctim), + } +} + +pub unsafe fn enc_filestat_byref( + vmctx: *mut VmContext, + filestat_ptr: wasm32::uintptr_t, + host_filestat: host::__wasi_filestat_t, +) -> Result<(), host::__wasi_errno_t> { + let filestat = enc_filestat(host_filestat); + enc_pointee::(vmctx, filestat_ptr, filestat) +} + +pub fn dec_fdstat(fdstat: wasm32::__wasi_fdstat_t) -> host::__wasi_fdstat_t { + host::__wasi_fdstat_t { + fs_filetype: dec_filetype(fdstat.fs_filetype), + fs_flags: dec_fdflags(fdstat.fs_flags), + fs_rights_base: dec_rights(fdstat.fs_rights_base), + fs_rights_inheriting: dec_rights(fdstat.fs_rights_inheriting), + } +} + +pub unsafe fn dec_fdstat_byref( + vmctx: *mut VmContext, + fdstat_ptr: wasm32::uintptr_t, +) -> Result { + dec_pointee::(vmctx, fdstat_ptr).map(dec_fdstat) +} + +pub fn enc_fdstat(fdstat: host::__wasi_fdstat_t) -> wasm32::__wasi_fdstat_t { + wasm32::__wasi_fdstat_t { + fs_filetype: enc_filetype(fdstat.fs_filetype), + fs_flags: enc_fdflags(fdstat.fs_flags), + __bindgen_padding_0: 0, + fs_rights_base: enc_rights(fdstat.fs_rights_base), + fs_rights_inheriting: enc_rights(fdstat.fs_rights_inheriting), + } +} + +pub unsafe fn enc_fdstat_byref( + vmctx: *mut VmContext, + fdstat_ptr: wasm32::uintptr_t, + host_fdstat: host::__wasi_fdstat_t, +) -> Result<(), host::__wasi_errno_t> { + let fdstat = enc_fdstat(host_fdstat); + enc_pointee::(vmctx, fdstat_ptr, fdstat) +} + +dec_enc_scalar!( + __wasi_filedelta_t, + dec_filedelta, + dec_filedelta_byref, + enc_filedelta, + enc_filedelta_byref +); +dec_enc_scalar!( + __wasi_filesize_t, + dec_filesize, + dec_filesize_byref, + enc_filesize, + enc_filesize_byref +); + +dec_enc_scalar!( + __wasi_filetype_t, + dec_filetype, + dec_filetype_byref, + enc_filetype, + enc_filetype_byref +); + +dec_enc_scalar!( + __wasi_lookupflags_t, + dec_lookupflags, + dec_lookupflags_byref, + enc_lookupflags, + enc_lookupflags_byref +); + +dec_enc_scalar!( + __wasi_oflags_t, + dec_oflags, + dec_oflags_byref, + enc_oflags, + enc_oflags_byref +); + +pub fn dec_prestat( + prestat: wasm32::__wasi_prestat_t, +) -> Result { + match prestat.pr_type { + wasm32::__WASI_PREOPENTYPE_DIR => { + let u = host::__wasi_prestat_t___wasi_prestat_u { + dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { + pr_name_len: dec_usize(unsafe { prestat.u.dir.pr_name_len }), + }, + }; + Ok(host::__wasi_prestat_t { + pr_type: host::__WASI_PREOPENTYPE_DIR as host::__wasi_preopentype_t, + u, + }) + } + _ => Err(host::__WASI_EINVAL as host::__wasi_errno_t), + } +} + +pub unsafe fn dec_prestat_byref( + vmctx: *mut VmContext, + prestat_ptr: wasm32::uintptr_t, +) -> Result { + dec_pointee::(vmctx, prestat_ptr).and_then(dec_prestat) +} + +pub fn enc_prestat( + prestat: host::__wasi_prestat_t, +) -> Result { + match prestat.pr_type as u32 { + host::__WASI_PREOPENTYPE_DIR => { + let u = wasm32::__wasi_prestat_t___wasi_prestat_u { + dir: wasm32::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { + pr_name_len: enc_usize(unsafe { prestat.u.dir.pr_name_len }), + }, + }; + Ok(wasm32::__wasi_prestat_t { + pr_type: wasm32::__WASI_PREOPENTYPE_DIR as wasm32::__wasi_preopentype_t, + u, + }) + } + _ => Err(host::__WASI_EINVAL as host::__wasi_errno_t), + } +} + +pub unsafe fn enc_prestat_byref( + vmctx: *mut VmContext, + prestat_ptr: wasm32::uintptr_t, + host_prestat: host::__wasi_prestat_t, +) -> Result<(), host::__wasi_errno_t> { + let prestat = enc_prestat(host_prestat)?; + enc_pointee::(vmctx, prestat_ptr, prestat) +} + +dec_enc_scalar!( + __wasi_rights_t, + dec_rights, + dec_rights_byref, + enc_rights, + enc_rights_byref +); +dec_enc_scalar!( + __wasi_timestamp_t, + dec_timestamp, + dec_timestamp_byref, + enc_timestamp, + enc_timestamp_byref +); + +pub fn dec_usize(size: wasm32::size_t) -> usize { + cast::usize(u32::from_le(size)) +} + +pub fn enc_usize(size: usize) -> wasm32::size_t { + wasm32::size_t::cast(size).unwrap() +} + +pub unsafe fn enc_usize_byref( + vmctx: *mut VmContext, + usize_ptr: wasm32::uintptr_t, + host_usize: usize, +) -> Result<(), host::__wasi_errno_t> { + enc_pointee::(vmctx, usize_ptr, enc_usize(host_usize)) +} + +dec_enc_scalar!( + __wasi_whence_t, + dec_whence, + dec_whence_byref, + enc_whence, + enc_whence_byref +); + +dec_enc_scalar!( + __wasi_subclockflags_t, + dec_subclockflags, + dec_subclockflags_byref, + enc_subclockflags, + enc_subclockflags_byref +); + +dec_enc_scalar!( + __wasi_eventrwflags_t, + dec_eventrwflags, + dec_eventrwflags_byref, + enc_eventrwflags, + enc_eventrwflags_byref +); + +dec_enc_scalar!( + __wasi_eventtype_t, + dec_eventtype, + dec_eventtype_byref, + enc_eventtype, + enc_eventtype_byref +); + +dec_enc_scalar!( + __wasi_userdata_t, + dec_userdata, + dec_userdata_byref, + enc_userdata, + enc_userdata_byref +); + +pub fn dec_subscription( + subscription: &wasm32::__wasi_subscription_t, +) -> Result { + let userdata = dec_userdata(subscription.userdata); + let type_ = dec_eventtype(subscription.type_); + let u_orig = subscription.__bindgen_anon_1; + let u = match type_ { + wasm32::__WASI_EVENTTYPE_CLOCK => host::__wasi_subscription_t___wasi_subscription_u { + clock: unsafe { + host::__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t { + identifier: dec_userdata(u_orig.clock.identifier), + clock_id: dec_clockid(u_orig.clock.clock_id), + timeout: dec_timestamp(u_orig.clock.timeout), + precision: dec_timestamp(u_orig.clock.precision), + flags: dec_subclockflags(u_orig.clock.flags), + } + }, + }, + wasm32::__WASI_EVENTTYPE_FD_READ | wasm32::__WASI_EVENTTYPE_FD_WRITE => host::__wasi_subscription_t___wasi_subscription_u { + fd_readwrite: host::__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t { + fd: dec_fd(unsafe{u_orig.fd_readwrite.fd}) + } + }, + _ => return Err(wasm32::__WASI_EINVAL) + }; + Ok(host::__wasi_subscription_t { userdata, type_, u }) +} + +pub fn enc_event(event: host::__wasi_event_t) -> wasm32::__wasi_event_t { + let fd_readwrite = unsafe { event.u.fd_readwrite }; + wasm32::__wasi_event_t { + userdata: enc_userdata(event.userdata), + type_: enc_eventtype(event.type_), + error: enc_errno(event.error), + __bindgen_anon_1: wasm32::__wasi_event_t__bindgen_ty_1 { + fd_readwrite: wasm32::__wasi_event_t__bindgen_ty_1__bindgen_ty_1 { + nbytes: enc_filesize(fd_readwrite.nbytes), + flags: enc_eventrwflags(fd_readwrite.flags), + __bindgen_padding_0: [0; 3], + }, + }, + __bindgen_padding_0: 0, + } +} diff --git a/src/wasm32.rs b/src/wasm32.rs new file mode 100644 index 0000000000..9983b37907 --- /dev/null +++ b/src/wasm32.rs @@ -0,0 +1,1367 @@ +//! WASI types as defined in wasm32. This file was originally generated +//! by running bindgen over wasi/core.h with a wasm32 target, and the content +//! still largely reflects that, however it's been heavily modified, to +//! be host-independent, to avoid exposing libc implementation details, +//! to clean up cases where the headers use complex preprocessor macros, +//! and to + +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] + +// C types +pub type char = i8; +pub type schar = i8; +pub type uchar = u8; +pub type short = i16; +pub type ushort = u16; +pub type int = i32; +pub type uint = u32; +pub type long = i32; +pub type ulong = u32; +pub type longlong = i64; +pub type ulonglong = u64; + +// libc stdint types +pub type int8_t = i8; +pub type uint8_t = u8; +pub type int16_t = i16; +pub type uint16_t = u16; +pub type int32_t = i32; +pub type uint32_t = u32; +pub type int64_t = i64; +pub type uint64_t = u64; +pub type intmax_t = i64; +pub type uintmax_t = u64; +pub type int_least8_t = i8; +pub type int_least16_t = i16; +pub type int_least32_t = i32; +pub type int_least64_t = i64; +pub type uint_least8_t = u8; +pub type uint_least16_t = u16; +pub type uint_least32_t = u32; +pub type uint_least64_t = u64; +pub type int_fast8_t = i8; +pub type int_fast16_t = i32; +pub type int_fast32_t = i32; +pub type int_fast64_t = i64; +pub type uint_fast8_t = u8; +pub type uint_fast16_t = u32; +pub type uint_fast32_t = u32; +pub type uint_fast64_t = u64; +pub type size_t = ulong; +pub type intptr_t = long; +pub type uintptr_t = ulong; +pub type wchar_t = i32; + +// libc types +pub type dev_t = u64; +pub type uid_t = u32; +pub type gid_t = u32; +pub type ino_t = u64; +pub type ino64_t = u64; +pub type mode_t = u32; +pub type nlink_t = u64; +pub type off_t = i64; +pub type off64_t = i64; +pub type pid_t = i32; +pub type clock_t = i64; +pub type rlim_t = u64; +pub type rlim64_t = u64; +pub type id_t = u32; +pub type time_t = i64; +pub type useconds_t = u32; +pub type suseconds_t = i64; +pub type daddr_t = i32; +pub type key_t = i32; +pub type clockid_t = i32; +pub type timer_t = uintptr_t; // *mut ::std::os::raw::c_void +pub type blksize_t = i64; +pub type blkcnt_t = i64; +pub type blkcnt64_t = i64; +pub type fsblkcnt_t = u64; +pub type fsblkcnt64_t = u64; +pub type fsfilcnt_t = u64; +pub type fsfilcnt64_t = u64; +pub type fsword_t = i64; +pub type ssize_t = i32; +pub type loff_t = off64_t; +pub type caddr_t = uintptr_t; // *mut i8 +pub type socklen_t = u32; +pub type sig_atomic_t = i32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fsid_t { + pub __val: [i32; 2usize], +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_fsid_t() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(fsid_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(fsid_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__val as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(fsid_t), + "::", + stringify!(__val) + ) + ); +} + +// WASI types +pub type __wasi_advice_t = u8; +pub type __wasi_clockid_t = u32; +pub type __wasi_device_t = u64; +pub type __wasi_dircookie_t = u64; +pub type __wasi_errno_t = u16; +pub type __wasi_eventrwflags_t = u16; +pub type __wasi_eventtype_t = u8; +pub type __wasi_exitcode_t = u32; +pub type __wasi_fd_t = u32; +pub type __wasi_fdflags_t = u16; +pub type __wasi_fdsflags_t = u16; +pub type __wasi_filedelta_t = i64; +pub type __wasi_filesize_t = u64; +pub type __wasi_filetype_t = u8; +pub type __wasi_preopentype_t = u8; +pub type __wasi_fstflags_t = u16; +pub type __wasi_inode_t = u64; +pub type __wasi_linkcount_t = u32; +pub type __wasi_lookupflags_t = u32; +pub type __wasi_oflags_t = u16; +pub type __wasi_riflags_t = u16; +pub type __wasi_rights_t = u64; +pub type __wasi_roflags_t = u16; +pub type __wasi_sdflags_t = u8; +pub type __wasi_siflags_t = u16; +pub type __wasi_signal_t = u8; +pub type __wasi_subclockflags_t = u16; +pub type __wasi_timestamp_t = u64; +pub type __wasi_userdata_t = u64; +pub type __wasi_whence_t = u8; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_dirent_t { + pub d_next: __wasi_dircookie_t, + pub d_ino: __wasi_inode_t, + pub d_namlen: u32, + pub d_type: __wasi_filetype_t, + pub __bindgen_padding_0: [u8; 3usize], +} +#[test] +fn bindgen_test_layout_wasi_dirent_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_dirent_t>(), + 24usize, + concat!("Size of: ", stringify!(__wasi_dirent_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_next as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_next) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_ino as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_ino) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_namlen as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_namlen) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_type as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_type) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __wasi_event_t { + pub userdata: __wasi_userdata_t, + pub error: __wasi_errno_t, + pub type_: __wasi_eventtype_t, + pub __bindgen_padding_0: u32, + pub __bindgen_anon_1: __wasi_event_t__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __wasi_prestat_t { + pub pr_type: __wasi_preopentype_t, + pub u: __wasi_prestat_t___wasi_prestat_u, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union __wasi_prestat_t___wasi_prestat_u { + pub dir: __wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { + pub pr_name_len: size_t, +} +#[test] +fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>(), + 4usize, + concat!( + "Size of: ", + stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>())) + .pr_name_len as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t), + "::", + stringify!(pr_name_len) + ) + ); +} +#[test] +fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u() { + assert_eq!( + ::std::mem::size_of::<__wasi_prestat_t___wasi_prestat_u>(), + 4usize, + concat!("Size of: ", stringify!(__wasi_prestat_t___wasi_prestat_u)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_prestat_t___wasi_prestat_u>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_prestat_t___wasi_prestat_u) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_prestat_t___wasi_prestat_u>())).dir as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t___wasi_prestat_u), + "::", + stringify!(dir) + ) + ); +} +#[test] +fn bindgen_test_layout___wasi_prestat_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_prestat_t>(), + 8usize, + concat!("Size of: ", stringify!(__wasi_prestat_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_prestat_t>(), + 4usize, + concat!("Alignment of ", stringify!(__wasi_prestat_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_prestat_t>())).pr_type as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t), + "::", + stringify!(pr_type) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_prestat_t>())).u as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t), + "::", + stringify!(u) + ) + ); +} +#[allow(non_snake_case)] +#[repr(C)] +#[derive(Copy, Clone)] +pub union __wasi_event_t__bindgen_ty_1 { + pub fd_readwrite: __wasi_event_t__bindgen_ty_1__bindgen_ty_1, + _bindgen_union_align: [u64; 2usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_event_t__bindgen_ty_1__bindgen_ty_1 { + pub nbytes: __wasi_filesize_t, + pub flags: __wasi_eventrwflags_t, + pub __bindgen_padding_0: [u16; 3usize], +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_event_t__bindgen_ty_1__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>(), + 16usize, + concat!( + "Size of: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>())).nbytes + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(nbytes) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>())).flags as *const _ + as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(flags) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_event_t__bindgen_ty_1__bindgen_ty_2 { + pub signal: __wasi_signal_t, + pub exitcode: __wasi_exitcode_t, +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_event_t__bindgen_ty_1__bindgen_ty_2() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>(), + 8usize, + concat!( + "Size of: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>())).signal + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(signal) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>())).exitcode + as *const _ as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(exitcode) + ) + ); +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_event_t__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1>(), + 16usize, + concat!("Size of: ", stringify!(__wasi_event_t__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1>())).fd_readwrite as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1), + "::", + stringify!(fd_readwrite) + ) + ); +} +#[test] +fn bindgen_test_layout_wasi_event_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t>(), + 32usize, + concat!("Size of: ", stringify!(__wasi_event_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).userdata as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(userdata) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).error as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(error) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).type_ as *const _ as usize }, + 10usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(type_) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_fdstat_t { + pub fs_filetype: __wasi_filetype_t, + pub fs_flags: __wasi_fdflags_t, + pub __bindgen_padding_0: u32, + pub fs_rights_base: __wasi_rights_t, + pub fs_rights_inheriting: __wasi_rights_t, +} +#[test] +fn bindgen_test_layout_wasi_fdstat_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_fdstat_t>(), + 24usize, + concat!("Size of: ", stringify!(__wasi_fdstat_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_filetype as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_filetype) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_flags as *const _ as usize }, + 2usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_rights_base as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_rights_base) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_rights_inheriting as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_rights_inheriting) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_filestat_t { + pub st_dev: __wasi_device_t, + pub st_ino: __wasi_inode_t, + pub st_filetype: __wasi_filetype_t, + pub st_nlink: __wasi_linkcount_t, + pub st_size: __wasi_filesize_t, + pub st_atim: __wasi_timestamp_t, + pub st_mtim: __wasi_timestamp_t, + pub st_ctim: __wasi_timestamp_t, +} +#[test] +fn bindgen_test_layout_wasi_filestat_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_filestat_t>(), + 56usize, + concat!("Size of: ", stringify!(__wasi_filestat_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_dev as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_dev) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ino as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_ino) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_filetype as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_filetype) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_nlink as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_nlink) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_size as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_atim as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_atim) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_mtim as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_mtim) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ctim as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_ctim) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_ciovec_t { + pub buf: uintptr_t, // *const ::std::os::raw::c_void + pub buf_len: size_t, +} +#[test] +fn bindgen_test_layout_wasi_ciovec_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_ciovec_t>(), + 8usize, + concat!("Size of: ", stringify!(__wasi_ciovec_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_ciovec_t>(), + 4usize, + concat!("Alignment of ", stringify!(__wasi_ciovec_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_ciovec_t>())).buf as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_ciovec_t), + "::", + stringify!(buf) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_ciovec_t>())).buf_len as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_ciovec_t), + "::", + stringify!(buf_len) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_iovec_t { + pub buf: uintptr_t, // *mut ::std::os::raw::c_void + pub buf_len: size_t, +} +#[test] +fn bindgen_test_layout_wasi_iovec_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_iovec_t>(), + 8usize, + concat!("Size of: ", stringify!(__wasi_iovec_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_iovec_t>(), + 4usize, + concat!("Alignment of ", stringify!(__wasi_iovec_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_iovec_t>())).buf as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_iovec_t), + "::", + stringify!(buf) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_iovec_t>())).buf_len as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_iovec_t), + "::", + stringify!(buf_len) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __wasi_subscription_t { + pub userdata: __wasi_userdata_t, + pub type_: __wasi_eventtype_t, + pub __bindgen_padding_0: u32, + pub __bindgen_anon_1: __wasi_subscription_t__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union __wasi_subscription_t__bindgen_ty_1 { + pub clock: __wasi_subscription_t__bindgen_ty_1__bindgen_ty_1, + pub fd_readwrite: __wasi_subscription_t__bindgen_ty_1__bindgen_ty_3, + _bindgen_union_align: [u64; 5usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_subscription_t__bindgen_ty_1__bindgen_ty_1 { + pub identifier: __wasi_userdata_t, + pub clock_id: __wasi_clockid_t, + pub __bindgen_padding_0: u32, + pub timeout: __wasi_timestamp_t, + pub precision: __wasi_timestamp_t, + pub flags: __wasi_subclockflags_t, + pub __bindgen_padding_1: [u16; 3usize], +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t__bindgen_ty_1__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>(), + 40usize, + concat!( + "Size of: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).identifier + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(identifier) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).clock_id + as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(clock_id) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).timeout + as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(timeout) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).precision + as *const _ as usize + }, + 24usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(precision) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).flags + as *const _ as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(flags) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_subscription_t__bindgen_ty_1__bindgen_ty_3 { + pub fd: __wasi_fd_t, +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t__bindgen_ty_1__bindgen_ty_3() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3>(), + 4usize, + concat!( + "Size of: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3>())).fd + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3), + "::", + stringify!(fd) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_subscription_t__bindgen_ty_1__bindgen_ty_5 { + pub fd: __wasi_fd_t, +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t__bindgen_ty_1__bindgen_ty_5() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5>(), + 4usize, + concat!( + "Size of: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5>())).fd + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5), + "::", + stringify!(fd) + ) + ); +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t__bindgen_ty_1>(), + 40usize, + concat!("Size of: ", stringify!(__wasi_subscription_t__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1>())).clock as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1), + "::", + stringify!(clock) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1>())).fd_readwrite as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1), + "::", + stringify!(fd_readwrite) + ) + ); +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t>(), + 56usize, + concat!("Size of: ", stringify!(__wasi_subscription_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_subscription_t>())).userdata as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t), + "::", + stringify!(userdata) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_subscription_t>())).type_ as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t), + "::", + stringify!(type_) + ) + ); +} + +pub fn strerror(errno: __wasi_errno_t) -> &'static str { + match errno { + __WASI_ESUCCESS => "__WASI_ESUCCESS", + __WASI_E2BIG => "__WASI_E2BIG", + __WASI_EACCES => "__WASI_EACCES", + __WASI_EADDRINUSE => "__WASI_EADDRINUSE", + __WASI_EADDRNOTAVAIL => "__WASI_EADDRNOTAVAIL", + __WASI_EAFNOSUPPORT => "__WASI_EAFNOSUPPORT", + __WASI_EAGAIN => "__WASI_EAGAIN", + __WASI_EALREADY => "__WASI_EALREADY", + __WASI_EBADF => "__WASI_EBADF", + __WASI_EBADMSG => "__WASI_EBADMSG", + __WASI_EBUSY => "__WASI_EBUSY", + __WASI_ECANCELED => "__WASI_ECANCELED", + __WASI_ECHILD => "__WASI_ECHILD", + __WASI_ECONNABORTED => "__WASI_ECONNABORTED", + __WASI_ECONNREFUSED => "__WASI_ECONNREFUSED", + __WASI_ECONNRESET => "__WASI_ECONNRESET", + __WASI_EDEADLK => "__WASI_EDEADLK", + __WASI_EDESTADDRREQ => "__WASI_EDESTADDRREQ", + __WASI_EDOM => "__WASI_EDOM", + __WASI_EDQUOT => "__WASI_EDQUOT", + __WASI_EEXIST => "__WASI_EEXIST", + __WASI_EFAULT => "__WASI_EFAULT", + __WASI_EFBIG => "__WASI_EFBIG", + __WASI_EHOSTUNREACH => "__WASI_EHOSTUNREACH", + __WASI_EIDRM => "__WASI_EIDRM", + __WASI_EILSEQ => "__WASI_EILSEQ", + __WASI_EINPROGRESS => "__WASI_EINPROGRESS", + __WASI_EINTR => "__WASI_EINTR", + __WASI_EINVAL => "__WASI_EINVAL", + __WASI_EIO => "__WASI_EIO", + __WASI_EISCONN => "__WASI_EISCONN", + __WASI_EISDIR => "__WASI_EISDIR", + __WASI_ELOOP => "__WASI_ELOOP", + __WASI_EMFILE => "__WASI_EMFILE", + __WASI_EMLINK => "__WASI_EMLINK", + __WASI_EMSGSIZE => "__WASI_EMSGSIZE", + __WASI_EMULTIHOP => "__WASI_EMULTIHOP", + __WASI_ENAMETOOLONG => "__WASI_ENAMETOOLONG", + __WASI_ENETDOWN => "__WASI_ENETDOWN", + __WASI_ENETRESET => "__WASI_ENETRESET", + __WASI_ENETUNREACH => "__WASI_ENETUNREACH", + __WASI_ENFILE => "__WASI_ENFILE", + __WASI_ENOBUFS => "__WASI_ENOBUFS", + __WASI_ENODEV => "__WASI_ENODEV", + __WASI_ENOENT => "__WASI_ENOENT", + __WASI_ENOEXEC => "__WASI_ENOEXEC", + __WASI_ENOLCK => "__WASI_ENOLCK", + __WASI_ENOLINK => "__WASI_ENOLINK", + __WASI_ENOMEM => "__WASI_ENOMEM", + __WASI_ENOMSG => "__WASI_ENOMSG", + __WASI_ENOPROTOOPT => "__WASI_ENOPROTOOPT", + __WASI_ENOSPC => "__WASI_ENOSPC", + __WASI_ENOSYS => "__WASI_ENOSYS", + __WASI_ENOTCONN => "__WASI_ENOTCONN", + __WASI_ENOTDIR => "__WASI_ENOTDIR", + __WASI_ENOTEMPTY => "__WASI_ENOTEMPTY", + __WASI_ENOTRECOVERABLE => "__WASI_ENOTRECOVERABLE", + __WASI_ENOTSOCK => "__WASI_ENOTSOCK", + __WASI_ENOTSUP => "__WASI_ENOTSUP", + __WASI_ENOTTY => "__WASI_ENOTTY", + __WASI_ENXIO => "__WASI_ENXIO", + __WASI_EOVERFLOW => "__WASI_EOVERFLOW", + __WASI_EOWNERDEAD => "__WASI_EOWNERDEAD", + __WASI_EPERM => "__WASI_EPERM", + __WASI_EPIPE => "__WASI_EPIPE", + __WASI_EPROTO => "__WASI_EPROTO", + __WASI_EPROTONOSUPPORT => "__WASI_EPROTONOSUPPORT", + __WASI_EPROTOTYPE => "__WASI_EPROTOTYPE", + __WASI_ERANGE => "__WASI_ERANGE", + __WASI_EROFS => "__WASI_EROFS", + __WASI_ESPIPE => "__WASI_ESPIPE", + __WASI_ESRCH => "__WASI_ESRCH", + __WASI_ESTALE => "__WASI_ESTALE", + __WASI_ETIMEDOUT => "__WASI_ETIMEDOUT", + __WASI_ETXTBSY => "__WASI_ETXTBSY", + __WASI_EXDEV => "__WASI_EXDEV", + __WASI_ENOTCAPABLE => "__WASI_ENOTCAPABLE", + other => panic!("Undefined errno value {:?}", other), + } +} + +pub fn whence_to_str(whence: __wasi_whence_t) -> &'static str { + match whence { + __WASI_WHENCE_CUR => "__WASI_WHENCE_CUR", + __WASI_WHENCE_END => "__WASI_WHENCE_END", + __WASI_WHENCE_SET => "__WASI_WHENCE_SET", + other => panic!("Undefined whence value {:?}", other), + } +} + +// libc constants +pub const INT8_MIN: i32 = -128; +pub const INT16_MIN: i32 = -32768; +pub const INT32_MIN: i32 = -2147483648; +pub const INT8_MAX: u32 = 127; +pub const INT16_MAX: u32 = 32767; +pub const INT32_MAX: u32 = 2147483647; +pub const UINT8_MAX: u32 = 255; +pub const UINT16_MAX: u32 = 65535; +pub const UINT32_MAX: u32 = 4294967295; +pub const INT_LEAST8_MIN: i32 = -128; +pub const INT_LEAST16_MIN: i32 = -32768; +pub const INT_LEAST32_MIN: i32 = -2147483648; +pub const INT_LEAST8_MAX: u32 = 127; +pub const INT_LEAST16_MAX: u32 = 32767; +pub const INT_LEAST32_MAX: u32 = 2147483647; +pub const UINT_LEAST8_MAX: u32 = 255; +pub const UINT_LEAST16_MAX: u32 = 65535; +pub const UINT_LEAST32_MAX: u32 = 4294967295; +pub const INT_FAST8_MIN: i32 = -128; +pub const INT_FAST16_MIN: i32 = -2147483648; +pub const INT_FAST32_MIN: i32 = -2147483648; +pub const INT_FAST8_MAX: u32 = 127; +pub const INT_FAST16_MAX: u32 = 2147483647; +pub const INT_FAST32_MAX: u32 = 2147483647; +pub const UINT_FAST8_MAX: u32 = 255; +pub const UINT_FAST16_MAX: u32 = 4294967295; +pub const UINT_FAST32_MAX: u32 = 4294967295; +pub const INTPTR_MIN: i32 = -2147483648; +pub const INTPTR_MAX: u32 = 2147483647; +pub const UINTPTR_MAX: u32 = 4294967295; +pub const PTRDIFF_MIN: i32 = -2147483648; +pub const PTRDIFF_MAX: u32 = 2147483647; +pub const SIG_ATOMIC_MIN: i32 = -2147483648; +pub const SIG_ATOMIC_MAX: u32 = 2147483647; +pub const SIZE_MAX: u32 = 4294967295; +pub const WINT_MIN: i32 = -2147483648; +pub const WINT_MAX: i32 = 2147483647; + +// WASI constants +pub const __WASI_ADVICE_NORMAL: __wasi_advice_t = 0; +pub const __WASI_ADVICE_SEQUENTIAL: __wasi_advice_t = 1; +pub const __WASI_ADVICE_RANDOM: __wasi_advice_t = 2; +pub const __WASI_ADVICE_WILLNEED: __wasi_advice_t = 3; +pub const __WASI_ADVICE_DONTNEED: __wasi_advice_t = 4; +pub const __WASI_ADVICE_NOREUSE: __wasi_advice_t = 5; +pub const __WASI_CLOCK_REALTIME: __wasi_clockid_t = 0; +pub const __WASI_CLOCK_MONOTONIC: __wasi_clockid_t = 1; +pub const __WASI_CLOCK_PROCESS_CPUTIME_ID: __wasi_clockid_t = 2; +pub const __WASI_CLOCK_THREAD_CPUTIME_ID: __wasi_clockid_t = 3; +pub const __WASI_DIRCOOKIE_START: __wasi_dircookie_t = 0; +pub const __WASI_ESUCCESS: __wasi_errno_t = 0; +pub const __WASI_E2BIG: __wasi_errno_t = 1; +pub const __WASI_EACCES: __wasi_errno_t = 2; +pub const __WASI_EADDRINUSE: __wasi_errno_t = 3; +pub const __WASI_EADDRNOTAVAIL: __wasi_errno_t = 4; +pub const __WASI_EAFNOSUPPORT: __wasi_errno_t = 5; +pub const __WASI_EAGAIN: __wasi_errno_t = 6; +pub const __WASI_EALREADY: __wasi_errno_t = 7; +pub const __WASI_EBADF: __wasi_errno_t = 8; +pub const __WASI_EBADMSG: __wasi_errno_t = 9; +pub const __WASI_EBUSY: __wasi_errno_t = 10; +pub const __WASI_ECANCELED: __wasi_errno_t = 11; +pub const __WASI_ECHILD: __wasi_errno_t = 12; +pub const __WASI_ECONNABORTED: __wasi_errno_t = 13; +pub const __WASI_ECONNREFUSED: __wasi_errno_t = 14; +pub const __WASI_ECONNRESET: __wasi_errno_t = 15; +pub const __WASI_EDEADLK: __wasi_errno_t = 16; +pub const __WASI_EDESTADDRREQ: __wasi_errno_t = 17; +pub const __WASI_EDOM: __wasi_errno_t = 18; +pub const __WASI_EDQUOT: __wasi_errno_t = 19; +pub const __WASI_EEXIST: __wasi_errno_t = 20; +pub const __WASI_EFAULT: __wasi_errno_t = 21; +pub const __WASI_EFBIG: __wasi_errno_t = 22; +pub const __WASI_EHOSTUNREACH: __wasi_errno_t = 23; +pub const __WASI_EIDRM: __wasi_errno_t = 24; +pub const __WASI_EILSEQ: __wasi_errno_t = 25; +pub const __WASI_EINPROGRESS: __wasi_errno_t = 26; +pub const __WASI_EINTR: __wasi_errno_t = 27; +pub const __WASI_EINVAL: __wasi_errno_t = 28; +pub const __WASI_EIO: __wasi_errno_t = 29; +pub const __WASI_EISCONN: __wasi_errno_t = 30; +pub const __WASI_EISDIR: __wasi_errno_t = 31; +pub const __WASI_ELOOP: __wasi_errno_t = 32; +pub const __WASI_EMFILE: __wasi_errno_t = 33; +pub const __WASI_EMLINK: __wasi_errno_t = 34; +pub const __WASI_EMSGSIZE: __wasi_errno_t = 35; +pub const __WASI_EMULTIHOP: __wasi_errno_t = 36; +pub const __WASI_ENAMETOOLONG: __wasi_errno_t = 37; +pub const __WASI_ENETDOWN: __wasi_errno_t = 38; +pub const __WASI_ENETRESET: __wasi_errno_t = 39; +pub const __WASI_ENETUNREACH: __wasi_errno_t = 40; +pub const __WASI_ENFILE: __wasi_errno_t = 41; +pub const __WASI_ENOBUFS: __wasi_errno_t = 42; +pub const __WASI_ENODEV: __wasi_errno_t = 43; +pub const __WASI_ENOENT: __wasi_errno_t = 44; +pub const __WASI_ENOEXEC: __wasi_errno_t = 45; +pub const __WASI_ENOLCK: __wasi_errno_t = 46; +pub const __WASI_ENOLINK: __wasi_errno_t = 47; +pub const __WASI_ENOMEM: __wasi_errno_t = 48; +pub const __WASI_ENOMSG: __wasi_errno_t = 49; +pub const __WASI_ENOPROTOOPT: __wasi_errno_t = 50; +pub const __WASI_ENOSPC: __wasi_errno_t = 51; +pub const __WASI_ENOSYS: __wasi_errno_t = 52; +pub const __WASI_ENOTCONN: __wasi_errno_t = 53; +pub const __WASI_ENOTDIR: __wasi_errno_t = 54; +pub const __WASI_ENOTEMPTY: __wasi_errno_t = 55; +pub const __WASI_ENOTRECOVERABLE: __wasi_errno_t = 56; +pub const __WASI_ENOTSOCK: __wasi_errno_t = 57; +pub const __WASI_ENOTSUP: __wasi_errno_t = 58; +pub const __WASI_ENOTTY: __wasi_errno_t = 59; +pub const __WASI_ENXIO: __wasi_errno_t = 60; +pub const __WASI_EOVERFLOW: __wasi_errno_t = 61; +pub const __WASI_EOWNERDEAD: __wasi_errno_t = 62; +pub const __WASI_EPERM: __wasi_errno_t = 63; +pub const __WASI_EPIPE: __wasi_errno_t = 64; +pub const __WASI_EPROTO: __wasi_errno_t = 65; +pub const __WASI_EPROTONOSUPPORT: __wasi_errno_t = 66; +pub const __WASI_EPROTOTYPE: __wasi_errno_t = 67; +pub const __WASI_ERANGE: __wasi_errno_t = 68; +pub const __WASI_EROFS: __wasi_errno_t = 69; +pub const __WASI_ESPIPE: __wasi_errno_t = 70; +pub const __WASI_ESRCH: __wasi_errno_t = 71; +pub const __WASI_ESTALE: __wasi_errno_t = 72; +pub const __WASI_ETIMEDOUT: __wasi_errno_t = 73; +pub const __WASI_ETXTBSY: __wasi_errno_t = 74; +pub const __WASI_EXDEV: __wasi_errno_t = 75; +pub const __WASI_ENOTCAPABLE: __wasi_errno_t = 76; +pub const __WASI_EVENT_FD_READWRITE_HANGUP: __wasi_eventrwflags_t = 1; +pub const __WASI_EVENTTYPE_CLOCK: __wasi_eventtype_t = 0; +pub const __WASI_EVENTTYPE_FD_READ: __wasi_eventtype_t = 1; +pub const __WASI_EVENTTYPE_FD_WRITE: __wasi_eventtype_t = 2; +pub const __WASI_FDFLAG_APPEND: __wasi_fdflags_t = 1; +pub const __WASI_FDFLAG_DSYNC: __wasi_fdflags_t = 2; +pub const __WASI_FDFLAG_NONBLOCK: __wasi_fdflags_t = 4; +pub const __WASI_FDFLAG_RSYNC: __wasi_fdflags_t = 8; +pub const __WASI_FDFLAG_SYNC: __wasi_fdflags_t = 16; +pub const __WASI_PREOPENTYPE_DIR: __wasi_preopentype_t = 0; +pub const __WASI_FILETYPE_UNKNOWN: __wasi_filetype_t = 0; +pub const __WASI_FILETYPE_BLOCK_DEVICE: __wasi_filetype_t = 1; +pub const __WASI_FILETYPE_CHARACTER_DEVICE: __wasi_filetype_t = 2; +pub const __WASI_FILETYPE_DIRECTORY: __wasi_filetype_t = 3; +pub const __WASI_FILETYPE_REGULAR_FILE: __wasi_filetype_t = 4; +pub const __WASI_FILETYPE_SOCKET_DGRAM: __wasi_filetype_t = 5; +pub const __WASI_FILETYPE_SOCKET_STREAM: __wasi_filetype_t = 6; +pub const __WASI_FILETYPE_SYMBOLIC_LINK: __wasi_filetype_t = 7; +pub const __WASI_FILESTAT_SET_ATIM: __wasi_fstflags_t = 1; +pub const __WASI_FILESTAT_SET_ATIM_NOW: __wasi_fstflags_t = 2; +pub const __WASI_FILESTAT_SET_MTIM: __wasi_fstflags_t = 4; +pub const __WASI_FILESTAT_SET_MTIM_NOW: __wasi_fstflags_t = 8; +pub const __WASI_LOOKUP_SYMLINK_FOLLOW: __wasi_lookupflags_t = 1; +pub const __WASI_O_CREAT: __wasi_oflags_t = 1; +pub const __WASI_O_DIRECTORY: __wasi_oflags_t = 2; +pub const __WASI_O_EXCL: __wasi_oflags_t = 4; +pub const __WASI_O_TRUNC: __wasi_oflags_t = 8; +pub const __WASI_SOCK_RECV_PEEK: __wasi_riflags_t = 1; +pub const __WASI_SOCK_RECV_WAITALL: __wasi_riflags_t = 2; +pub const __WASI_RIGHT_FD_DATASYNC: __wasi_rights_t = 1; +pub const __WASI_RIGHT_FD_READ: __wasi_rights_t = 2; +pub const __WASI_RIGHT_FD_SEEK: __wasi_rights_t = 4; +pub const __WASI_RIGHT_FD_FDSTAT_SET_FLAGS: __wasi_rights_t = 8; +pub const __WASI_RIGHT_FD_SYNC: __wasi_rights_t = 16; +pub const __WASI_RIGHT_FD_TELL: __wasi_rights_t = 32; +pub const __WASI_RIGHT_FD_WRITE: __wasi_rights_t = 64; +pub const __WASI_RIGHT_FD_ADVISE: __wasi_rights_t = 128; +pub const __WASI_RIGHT_FD_ALLOCATE: __wasi_rights_t = 256; +pub const __WASI_RIGHT_PATH_CREATE_DIRECTORY: __wasi_rights_t = 512; +pub const __WASI_RIGHT_PATH_CREATE_FILE: __wasi_rights_t = 1024; +pub const __WASI_RIGHT_PATH_LINK_SOURCE: __wasi_rights_t = 2048; +pub const __WASI_RIGHT_PATH_LINK_TARGET: __wasi_rights_t = 4096; +pub const __WASI_RIGHT_PATH_OPEN: __wasi_rights_t = 8192; +pub const __WASI_RIGHT_FD_READDIR: __wasi_rights_t = 16384; +pub const __WASI_RIGHT_PATH_READLINK: __wasi_rights_t = 32768; +pub const __WASI_RIGHT_PATH_RENAME_SOURCE: __wasi_rights_t = 65536; +pub const __WASI_RIGHT_PATH_RENAME_TARGET: __wasi_rights_t = 131072; +pub const __WASI_RIGHT_PATH_FILESTAT_GET: __wasi_rights_t = 262144; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_SIZE: __wasi_rights_t = 524288; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_TIMES: __wasi_rights_t = 1048576; +pub const __WASI_RIGHT_FD_FILESTAT_GET: __wasi_rights_t = 2097152; +pub const __WASI_RIGHT_FD_FILESTAT_SET_SIZE: __wasi_rights_t = 4194304; +pub const __WASI_RIGHT_FD_FILESTAT_SET_TIMES: __wasi_rights_t = 8388608; +pub const __WASI_RIGHT_PATH_SYMLINK: __wasi_rights_t = 16777216; +pub const __WASI_RIGHT_PATH_REMOVE_DIRECTORY: __wasi_rights_t = 33554432; +pub const __WASI_RIGHT_PATH_UNLINK_FILE: __wasi_rights_t = 67108864; +pub const __WASI_RIGHT_POLL_FD_READWRITE: __wasi_rights_t = 134217728; +pub const __WASI_RIGHT_SOCK_SHUTDOWN: __wasi_rights_t = 268435456; +pub const __WASI_SOCK_RECV_DATA_TRUNCATED: __wasi_roflags_t = 1; +pub const __WASI_SHUT_RD: __wasi_sdflags_t = 1; +pub const __WASI_SHUT_WR: __wasi_sdflags_t = 2; +pub const __WASI_SIGHUP: __wasi_signal_t = 1; +pub const __WASI_SIGINT: __wasi_signal_t = 2; +pub const __WASI_SIGQUIT: __wasi_signal_t = 3; +pub const __WASI_SIGILL: __wasi_signal_t = 4; +pub const __WASI_SIGTRAP: __wasi_signal_t = 5; +pub const __WASI_SIGABRT: __wasi_signal_t = 6; +pub const __WASI_SIGBUS: __wasi_signal_t = 7; +pub const __WASI_SIGFPE: __wasi_signal_t = 8; +pub const __WASI_SIGKILL: __wasi_signal_t = 9; +pub const __WASI_SIGUSR1: __wasi_signal_t = 10; +pub const __WASI_SIGSEGV: __wasi_signal_t = 11; +pub const __WASI_SIGUSR2: __wasi_signal_t = 12; +pub const __WASI_SIGPIPE: __wasi_signal_t = 13; +pub const __WASI_SIGALRM: __wasi_signal_t = 14; +pub const __WASI_SIGTERM: __wasi_signal_t = 15; +pub const __WASI_SIGCHLD: __wasi_signal_t = 16; +pub const __WASI_SIGCONT: __wasi_signal_t = 17; +pub const __WASI_SIGSTOP: __wasi_signal_t = 18; +pub const __WASI_SIGTSTP: __wasi_signal_t = 19; +pub const __WASI_SIGTTIN: __wasi_signal_t = 20; +pub const __WASI_SIGTTOU: __wasi_signal_t = 21; +pub const __WASI_SIGURG: __wasi_signal_t = 22; +pub const __WASI_SIGXCPU: __wasi_signal_t = 23; +pub const __WASI_SIGXFSZ: __wasi_signal_t = 24; +pub const __WASI_SIGVTALRM: __wasi_signal_t = 25; +pub const __WASI_SIGPROF: __wasi_signal_t = 26; +pub const __WASI_SIGWINCH: __wasi_signal_t = 27; +pub const __WASI_SIGPOLL: __wasi_signal_t = 28; +pub const __WASI_SIGPWR: __wasi_signal_t = 29; +pub const __WASI_SIGSYS: __wasi_signal_t = 30; +pub const __WASI_SUBSCRIPTION_CLOCK_ABSTIME: __wasi_subclockflags_t = 1; +pub const __WASI_WHENCE_CUR: __wasi_whence_t = 0; +pub const __WASI_WHENCE_END: __wasi_whence_t = 1; +pub const __WASI_WHENCE_SET: __wasi_whence_t = 2; + +pub fn errno_from_nix(errno: nix::errno::Errno) -> __wasi_errno_t { + match errno { + nix::errno::Errno::EPERM => __WASI_EPERM, + nix::errno::Errno::ENOENT => __WASI_ENOENT, + nix::errno::Errno::ESRCH => __WASI_ESRCH, + nix::errno::Errno::EINTR => __WASI_EINTR, + nix::errno::Errno::EIO => __WASI_EIO, + nix::errno::Errno::ENXIO => __WASI_ENXIO, + nix::errno::Errno::E2BIG => __WASI_E2BIG, + nix::errno::Errno::ENOEXEC => __WASI_ENOEXEC, + nix::errno::Errno::EBADF => __WASI_EBADF, + nix::errno::Errno::ECHILD => __WASI_ECHILD, + nix::errno::Errno::EAGAIN => __WASI_EAGAIN, + nix::errno::Errno::ENOMEM => __WASI_ENOMEM, + nix::errno::Errno::EACCES => __WASI_EACCES, + nix::errno::Errno::EFAULT => __WASI_EFAULT, + nix::errno::Errno::EBUSY => __WASI_EBUSY, + nix::errno::Errno::EEXIST => __WASI_EEXIST, + nix::errno::Errno::EXDEV => __WASI_EXDEV, + nix::errno::Errno::ENODEV => __WASI_ENODEV, + nix::errno::Errno::ENOTDIR => __WASI_ENOTDIR, + nix::errno::Errno::EISDIR => __WASI_EISDIR, + nix::errno::Errno::EINVAL => __WASI_EINVAL, + nix::errno::Errno::ENFILE => __WASI_ENFILE, + nix::errno::Errno::EMFILE => __WASI_EMFILE, + nix::errno::Errno::ENOTTY => __WASI_ENOTTY, + nix::errno::Errno::ETXTBSY => __WASI_ETXTBSY, + nix::errno::Errno::EFBIG => __WASI_EFBIG, + nix::errno::Errno::ENOSPC => __WASI_ENOSPC, + nix::errno::Errno::ESPIPE => __WASI_ESPIPE, + nix::errno::Errno::EROFS => __WASI_EROFS, + nix::errno::Errno::EMLINK => __WASI_EMLINK, + nix::errno::Errno::EPIPE => __WASI_EPIPE, + nix::errno::Errno::EDOM => __WASI_EDOM, + nix::errno::Errno::ERANGE => __WASI_ERANGE, + nix::errno::Errno::EDEADLK => __WASI_EDEADLK, + nix::errno::Errno::ENAMETOOLONG => __WASI_ENAMETOOLONG, + nix::errno::Errno::ENOLCK => __WASI_ENOLCK, + nix::errno::Errno::ENOSYS => __WASI_ENOSYS, + nix::errno::Errno::ENOTEMPTY => __WASI_ENOTEMPTY, + nix::errno::Errno::ELOOP => __WASI_ELOOP, + nix::errno::Errno::ENOMSG => __WASI_ENOMSG, + nix::errno::Errno::EIDRM => __WASI_EIDRM, + nix::errno::Errno::ENOLINK => __WASI_ENOLINK, + nix::errno::Errno::EPROTO => __WASI_EPROTO, + nix::errno::Errno::EMULTIHOP => __WASI_EMULTIHOP, + nix::errno::Errno::EBADMSG => __WASI_EBADMSG, + nix::errno::Errno::EOVERFLOW => __WASI_EOVERFLOW, + nix::errno::Errno::EILSEQ => __WASI_EILSEQ, + nix::errno::Errno::ENOTSOCK => __WASI_ENOTSOCK, + nix::errno::Errno::EDESTADDRREQ => __WASI_EDESTADDRREQ, + nix::errno::Errno::EMSGSIZE => __WASI_EMSGSIZE, + nix::errno::Errno::EPROTOTYPE => __WASI_EPROTOTYPE, + nix::errno::Errno::ENOPROTOOPT => __WASI_ENOPROTOOPT, + nix::errno::Errno::EPROTONOSUPPORT => __WASI_EPROTONOSUPPORT, + nix::errno::Errno::EAFNOSUPPORT => __WASI_EAFNOSUPPORT, + nix::errno::Errno::EADDRINUSE => __WASI_EADDRINUSE, + nix::errno::Errno::EADDRNOTAVAIL => __WASI_EADDRNOTAVAIL, + nix::errno::Errno::ENETDOWN => __WASI_ENETDOWN, + nix::errno::Errno::ENETUNREACH => __WASI_ENETUNREACH, + nix::errno::Errno::ENETRESET => __WASI_ENETRESET, + nix::errno::Errno::ECONNABORTED => __WASI_ECONNABORTED, + nix::errno::Errno::ECONNRESET => __WASI_ECONNRESET, + nix::errno::Errno::ENOBUFS => __WASI_ENOBUFS, + nix::errno::Errno::EISCONN => __WASI_EISCONN, + nix::errno::Errno::ENOTCONN => __WASI_ENOTCONN, + nix::errno::Errno::ETIMEDOUT => __WASI_ETIMEDOUT, + nix::errno::Errno::ECONNREFUSED => __WASI_ECONNREFUSED, + nix::errno::Errno::EHOSTUNREACH => __WASI_EHOSTUNREACH, + nix::errno::Errno::EALREADY => __WASI_EALREADY, + nix::errno::Errno::EINPROGRESS => __WASI_EINPROGRESS, + nix::errno::Errno::ESTALE => __WASI_ESTALE, + nix::errno::Errno::EDQUOT => __WASI_EDQUOT, + nix::errno::Errno::ECANCELED => __WASI_ECANCELED, + nix::errno::Errno::EOWNERDEAD => __WASI_EOWNERDEAD, + nix::errno::Errno::ENOTRECOVERABLE => __WASI_ENOTRECOVERABLE, + _ => __WASI_ENOSYS, + } +}