diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..b402f6e --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,47 @@ +name: CI +on: [push] + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - run: cargo check --all + - run: cargo fmt --all -- --check + + test: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Install wasmtime + run: | + set -e + curl -L https://github.com/bytecodealliance/wasmtime/releases/download/${WASMTIME_VERSION}/wasmtime-${WASMTIME_VERSION}-x86_64-linux.tar.xz | tar xJf - + echo "$PWD/wasmtime-${WASMTIME_VERSION}-x86_64-linux" >> $GITHUB_PATH + env: + WASMTIME_VERSION: v0.34.0 + - name: Install wasi-sdk + run: | + set -e + wasi_sdk_deb="wasi-sdk_${WASI_SDK_VERSION_MAJOR}.${WASI_SDK_VERSION_MINOR}_amd64.deb" + wget "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION_MAJOR}/${wasi_sdk_deb}" + sudo dpkg -i "$wasi_sdk_deb" + echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV + env: + WASI_SDK_VERSION_MAJOR: 14 + WASI_SDK_VERSION_MINOR: 0 + - run: CARGO_TARGET_WASM32_WASI_RUNNER=wasmtime cargo test --target wasm32-wasi + - run: cargo build --target wasm32-unknown-unknown + - run: LIB_WASI_VFS_A=$PWD/target/wasm32-unknown-unknown/debug/libwasi_vfs.a ./tools/run-make-test.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3a346c6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,145 @@ +name: Release +on: + push: + tags: + - 'v*' +jobs: + build-wasi-vfs-cli: + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu + - x86_64-pc-windows-gnu + - x86_64-apple-darwin + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + - target: x86_64-pc-windows-gnu + os: ubuntu-latest + - target: x86_64-apple-darwin + os: macos-latest + - target: aarch64-apple-darwin + os: macos-latest + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - uses: actions-rs/cargo@v1.0.1 + with: + command: build + args: --release --target=${{ matrix.target }} --package wasi-vfs-cli + use-cross: true + + - run: | + zip --junk-paths wasi-vfs-cli-${{ matrix.target }} target/${{ matrix.target }}/release/wasi-vfs{,.exe} + - uses: actions/upload-artifact@v1 + with: + name: wasi-vfs-cli-${{ matrix.target }} + path: wasi-vfs-cli-${{ matrix.target }}.zip + build-libwasi-vfs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Install wasi-sdk + run: | + set -e + wasi_sdk_deb="wasi-sdk_${WASI_SDK_VERSION_MAJOR}.${WASI_SDK_VERSION_MINOR}_amd64.deb" + wget "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION_MAJOR}/${wasi_sdk_deb}" + sudo dpkg -i "$wasi_sdk_deb" + echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV + env: + WASI_SDK_VERSION_MAJOR: 14 + WASI_SDK_VERSION_MINOR: 0 + - uses: actions-rs/cargo@v1.0.1 + with: + command: build + args: --release --target=wasm32-unknown-unknown + - run: | + zip --junk-paths libwasi_vfs-wasm32-unknown-unknown target/wasm32-unknown-unknown/release/libwasi_vfs.a + - uses: actions/upload-artifact@v1 + with: + name: libwasi_vfs-wasm32-unknown-unknown + path: libwasi_vfs-wasm32-unknown-unknown.zip + + create-release: + needs: [build-wasi-vfs-cli, build-libwasi-vfs] + runs-on: ubuntu-latest + steps: + - id: create-release + uses: actions/create-release@v1.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + - run: | + echo '${{ steps.create-release.outputs.upload_url }}' > release_upload_url.txt + - uses: actions/upload-artifact@v1 + with: + name: create-release + path: release_upload_url.txt + upload-cli-release: + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu + - x86_64-pc-windows-gnu + - x86_64-apple-darwin + - aarch64-apple-darwin + needs: [create-release] + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v1 + with: + name: create-release + - id: upload-url + run: | + echo "::set-output name=url::$(cat create-release/release_upload_url.txt)" + - uses: actions/download-artifact@v1 + with: + name: wasi-vfs-cli-${{ matrix.target }} + - uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.upload-url.outputs.url }} + asset_path: ./wasi-vfs-cli-${{ matrix.target }}/wasi-vfs-cli-${{ matrix.target }}.zip + asset_name: wasi-vfs-cli-${{ matrix.target }}.zip + asset_content_type: application/zip + + upload-lib-release: + needs: [create-release] + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v1 + with: + name: create-release + - id: upload-url + run: | + echo "::set-output name=url::$(cat create-release/release_upload_url.txt)" + - uses: actions/download-artifact@v1 + with: + name: libwasi_vfs-wasm32-unknown-unknown + - uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.upload-url.outputs.url }} + asset_path: ./libwasi_vfs-wasm32-unknown-unknown/libwasi_vfs-wasm32-unknown-unknown.zip + asset_name: libwasi_vfs-wasm32-unknown-unknown.zip + asset_content_type: application/zip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ca59734 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "crates/wasi-libc-trampoline-bindgen/WASI"] + path = crates/wasi-libc-trampoline-bindgen/WASI + url = https://github.com/WebAssembly/WASI.git diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..82b8377 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1674 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ambient-authority" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ad6edb4840b78c5c3d88de606b22252d552b55f3a4699fbb10fc070ec3049" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" + +[[package]] +name = "async-trait" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cap-fs-ext" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f8499797f7e264c83334d9fc98b2c9889ebe5839514a14d81769ca09d71fd1d" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes", + "rustc_version", + "winapi", +] + +[[package]] +name = "cap-primitives" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5998b8b3a49736500aec3c123fa3f6f605a125b41a6df725e6b7c924a612ab4" +dependencies = [ + "ambient-authority", + "errno", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustc_version", + "rustix", + "winapi", + "winapi-util", + "winx", +] + +[[package]] +name = "cap-rand" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fafda903eb4a85903b106439cf62524275f3ae0609bb9e1ae9da7e7c26d4150c" +dependencies = [ + "ambient-authority", + "rand", +] + +[[package]] +name = "cap-std" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "811de89a7ede4ba32f2b75fe5c668a534da24942d81c081248a9d2843ebd517d" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "ipnet", + "rustc_version", + "rustix", +] + +[[package]] +name = "cap-time-ext" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85f263d62447efe8829efdf947bbb4824ba2a3e2852b3be1d62f76fc05c326b0" +dependencies = [ + "cap-primitives", + "once_cell", + "rustix", + "winx", +] + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.79.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebddaa5d12cb299b0bc7c930aff12c5591d4ba9aa84eea637807e07283b900aa" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.79.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da1daf5641177162644b521b64418564b8ed5deb126275a4d91472d13e7c72df" +dependencies = [ + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "gimli", + "log", + "regalloc", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.79.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "001c1e9e540940c81596e547e732f99c2146c21ea7e82da99be961a1e86feefa" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.79.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ebaf07b5d7501cc606f41c81333bd63a5a17eb501362ccb10bc8ff5c03d0232" + +[[package]] +name = "cranelift-entity" +version = "0.79.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27ada0e3ffe5325179fc750252c18d614fa5470d595ce5c8a794c495434d80a" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.79.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2912c0eec9fd3df2dcf82b02b642caaa85d762b84ac5a3b27bc93a07eeeb64e2" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-native" +version = "0.79.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd20f78f378f55a70738a2eb9815dcd7e8455ff091b70701cfd086dd44927da" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.79.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353abcef10511d565b25bd00f3d7b1babcc040d9644c5259467c9a514dc945f0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser 0.81.0", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "file-per-thread-logger" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e16290574b39ee41c71aeb90ae960c504ebaf1e2a1c87bd52aa56ed6e1a02f" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "fs-set-times" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa838950e8e36a567ce96a945c303e88d9916ff97df27c315a0d263a72bd816f" +dependencies = [ + "io-lifetimes", + "rustix", + "winapi", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", + "serde", +] + +[[package]] +name = "io-extras" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1d9a66d8b0312e3601a04a2dcf8f0ddd873319560ddeabe2110fa1e5af781a" +dependencies = [ + "io-lifetimes", + "rustc_version", + "winapi", +] + +[[package]] +name = "io-lifetimes" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278e90d6f8a6c76a8334b336e306efa3c5f2b604048cbfd486d6f49878e3af14" +dependencies = [ + "rustc_version", + "winapi", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" + +[[package]] +name = "linux-raw-sys" +version = "0.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a261afc61b7a5e323933b402ca6a1765183687c614789b1e4db7762ed4230bca" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +dependencies = [ + "crc32fast", + "indexmap", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "paste" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eca0fa5dd7c4c96e184cec588f0b1db1ee3165e678db21c09793105acb17e6f" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom", + "redox_syscall", +] + +[[package]] +name = "regalloc" +version = "0.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d808cff91dfca7b239d40b972ba628add94892b1d9e19a842aedc5cfae8ab1a" +dependencies = [ + "log", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "region" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18c44018277ec7195538f5631b90def7ad975bb46370cb0f4eff4012de9333f8" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "itoa", + "libc", + "linux-raw-sys", + "once_cell", + "rustc_version", + "winapi", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "shellexpand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829" +dependencies = [ + "dirs-next", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "system-interface" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b5163055c386394170493ec1827cf7975035dc0bb23dcb7070bd1b1f672baa" +dependencies = [ + "atty", + "bitflags", + "cap-fs-ext", + "cap-std", + "io-lifetimes", + "rustc_version", + "rustix", + "winapi", + "winx", +] + +[[package]] +name = "target-lexicon" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "git+https://github.com/bytecodealliance/wasi.git?rev=d3c7a34193cb33d994b11104b22d234530232b5f#d3c7a34193cb33d994b11104b22d234530232b5f" + +[[package]] +name = "wasi-cap-std-sync" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0981a09e90eec8032ec7a722cf7ba04120eb10bd456bc3c108c59abfa9ba4730" +dependencies = [ + "anyhow", + "async-trait", + "cap-fs-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "io-lifetimes", + "lazy_static", + "rustix", + "system-interface", + "tracing", + "wasi-common", + "winapi", +] + +[[package]] +name = "wasi-common" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a72c05e8e9de362269d44908bf282c346281ef973450c9c6319adfebd5e096c" +dependencies = [ + "anyhow", + "bitflags", + "cap-rand", + "cap-std", + "rustix", + "thiserror", + "tracing", + "wiggle", + "winapi", +] + +[[package]] +name = "wasi-libc-trampoline-bindgen" +version = "0.1.0" +dependencies = [ + "heck", + "structopt", + "witx 0.9.1", +] + +[[package]] +name = "wasi-vfs" +version = "0.1.0" +dependencies = [ + "cc", + "wasi 0.10.2+wasi-snapshot-preview1 (git+https://github.com/bytecodealliance/wasi.git?rev=d3c7a34193cb33d994b11104b22d234530232b5f)", + "wee_alloc", +] + +[[package]] +name = "wasi-vfs-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "structopt", + "wizer", +] + +[[package]] +name = "wasm-encoder" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2caacc74c68c74f0008c4055cdf509c43e623775eaf73323bb818dcf666ed9bd" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasmparser" +version = "0.78.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52144d4c78e5cf8b055ceab8e5fa22814ce4315d6002ad32cfd914f37c12fd65" + +[[package]] +name = "wasmparser" +version = "0.81.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc" + +[[package]] +name = "wasmtime" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56ceaa60d3019887d6ba5768860fac99f5a6511453e183cb3ba2aaafd9411f3" +dependencies = [ + "anyhow", + "async-trait", + "backtrace", + "bincode", + "cfg-if 1.0.0", + "cpp_demangle", + "indexmap", + "lazy_static", + "libc", + "log", + "object", + "paste", + "psm", + "rayon", + "region", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmparser 0.81.0", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit", + "wasmtime-runtime", + "wat", + "winapi", +] + +[[package]] +name = "wasmtime-cache" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d45c2c7ed7a2700ff012e97e12324d2ba0bdd943e50c0d3a95b582ef2bfdca4" +dependencies = [ + "anyhow", + "base64", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix", + "serde", + "sha2", + "toml", + "winapi", + "zstd", +] + +[[package]] +name = "wasmtime-cranelift" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5793c2d14c7e2b962d1d79408df011190ec8f6214a01efd676f5e2266c44bc8" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli", + "log", + "more-asserts", + "object", + "target-lexicon", + "thiserror", + "wasmparser 0.81.0", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79131537408f938501b4f540ae0f61b456d9962c2bb590edefb904cf7d1e5f54" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli", + "indexmap", + "log", + "more-asserts", + "object", + "serde", + "target-lexicon", + "thiserror", + "wasmparser 0.81.0", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-fiber" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9bf33ebebf88a353be8961ed87cf2780091e0c166c3ab3a23a3d8304f964a" +dependencies = [ + "cc", + "rustix", + "winapi", +] + +[[package]] +name = "wasmtime-jit" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8031b6e83071b40b0139924024ee0d2e11f65f7677d7b028720df55610cbf994" +dependencies = [ + "addr2line", + "anyhow", + "bincode", + "cfg-if 1.0.0", + "gimli", + "object", + "region", + "rustix", + "serde", + "target-lexicon", + "thiserror", + "wasmtime-environ", + "wasmtime-runtime", + "winapi", +] + +[[package]] +name = "wasmtime-runtime" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c9412d752736938c2a57228fb95e13d40bdbc879ac741874e7f7b49c198ffa" +dependencies = [ + "anyhow", + "backtrace", + "cc", + "cfg-if 1.0.0", + "indexmap", + "lazy_static", + "libc", + "log", + "mach", + "memoffset", + "more-asserts", + "rand", + "region", + "rustix", + "thiserror", + "wasmtime-environ", + "wasmtime-fiber", + "winapi", +] + +[[package]] +name = "wasmtime-types" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1602b6ae8b901e60e8b9d51cadbf51a0421b7e67bf4cbe2e647695783fd9d45" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser 0.81.0", +] + +[[package]] +name = "wasmtime-wasi" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2a67db8d3ee8506154c9e393902a56b3ce8e3aa6f76110a91dbadd7e9dedd4" +dependencies = [ + "anyhow", + "wasi-cap-std-sync", + "wasi-common", + "wasmtime", + "wiggle", +] + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "39.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9bbbd53432b267421186feee3e52436531fa69a7cfee9403f5204352df3dd05" +dependencies = [ + "leb128", + "memchr", + "unicode-width", +] + +[[package]] +name = "wat" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab98ed25494f97c69f28758617f27c3e92e5336040b5c3a14634f2dd3fe61830" +dependencies = [ + "wast 39.0.0", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "wiggle" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40607255656042b52686d96c05852584d1b4f157a6e261096f0ce702fcf1c111" +dependencies = [ + "anyhow", + "async-trait", + "bitflags", + "thiserror", + "tracing", + "wasmtime", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12a4f79499ff19b69580f3fbcb569b92ce7372ecc83e7810e731f9edd55e8f05" +dependencies = [ + "anyhow", + "heck", + "proc-macro2", + "quote", + "shellexpand", + "syn", + "witx 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wiggle-macro" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8866a79f7a0396c1914449268dc61da65e68ff098023fccc459f7400b963c60a" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wiggle-generate", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winx" +version = "0.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afba0891d41a50943c32fcea61e124b9dd5755275054b0a3e1e1eba26e671137" +dependencies = [ + "bitflags", + "io-lifetimes", + "winapi", +] + +[[package]] +name = "witx" +version = "0.9.1" +dependencies = [ + "anyhow", + "log", + "rayon", + "thiserror", + "wast 35.0.2", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror", + "wast 35.0.2", +] + +[[package]] +name = "wizer" +version = "1.3.5" +source = "git+https://github.com/bytecodealliance/wizer.git?rev=1802237aeb8c19ff9448d67d280935bc10110d32#1802237aeb8c19ff9448d67d280935bc10110d32" +dependencies = [ + "anyhow", + "cap-std", + "log", + "rayon", + "wasi-cap-std-sync", + "wasm-encoder", + "wasmparser 0.78.2", + "wasmtime", + "wasmtime-wasi", +] + +[[package]] +name = "zstd" +version = "0.9.2+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2390ea1bf6c038c39674f22d95f0564725fc06034a47129179810b2fc58caa54" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "4.1.3+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e99d81b99fb3c2c2c794e3fe56c305c63d5173a16a46b5850b07c935ffc7db79" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.6.2+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f" +dependencies = [ + "cc", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..64974da --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "wasi-vfs" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[lib] +crate-type = ["staticlib", "cdylib"] + +[dependencies] +wasi = { git = "https://github.com/bytecodealliance/wasi.git", rev = "d3c7a34193cb33d994b11104b22d234530232b5f" } +wee_alloc = { version = "0.4.5", optional = true } + + +[build-dependencies] +cc = "1.0" + +[workspace] +members = [ + "crates/wasi-libc-trampoline-bindgen", + "crates/wasi-vfs-cli", +] + +[features] +trace-syscall = [] +legacy-wasi-libc = [] +module-linking = ["wee_alloc"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..12dbc37 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Yuta Saito + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..caf49d3 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +TRAMPOLINE_GEN = WASI_REPO=./crates/wasi-libc-trampoline-bindgen/WASI cargo run --package wasi-libc-trampoline-bindgen -- + +generate-trampoline: + $(TRAMPOLINE_GEN) wrapper > ./src/trampoline_generated.rs + $(TRAMPOLINE_GEN) object-link latest > ./src/trampoline_generated.c + $(TRAMPOLINE_GEN) object-link legacy > ./src/trampoline_generated_legacy_wasi_libc.c diff --git a/README.md b/README.md new file mode 100644 index 0000000..6c20ea9 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# wasi-vfs + +A virtual filesystem layer for WASI. + +> **NOTICE**: This project currently supports only WASI applications on the top of [wasi-libc](https://github.com/webassembly/wasi-libc) + +This project provides a language and host-agnostic virtual filesystem layer for WASI. + +
+ +
+ +## Supported filesystems + +- **Embedded file system**: a read only file system embedded in the `.wasm` binary. +- to be implemented more... + +## Building + +To build the project, you need to install the [`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk) version 14.0 or later. + +```console +$ export WASI_SDK_PATH=/path/to/wasi-sdk +$ cargo build --target wasm32-unknown-unknown +``` + +## Demo + +### Prerequisites + +Set `$WASI_SDK_PATH` environment variable to the path of the `wasi-sdk` (version 14 or later). + +### Build and run WASI application + +```console +$ git clone https://github.com/kateinoigakukun/wasi-vfs.git +$ cd wasi-vfs + +# Build libwasi_vfs.a +$ cargo build --target wasm32-unknown-unknown + +# Build a WASI app with libwasi_vfs.a +$ $WASI_SDK_PATH/bin/clang -target wasm32-unknown-wasi -o getline.wasm examples/getline.c ./target/wasm32-unknown-unknown/debug/libwasi_vfs.a + +# Run the WASI app with --mapdir +$ wasmtime run --mapdir /::./examples/mnt getline.wasm -- /hello.txt +Hello + +# Pack ./examples/mnt directory into a WASM binary +$ cargo run -p wasi-vfs-cli -- pack getline.wasm --mapdir /::./examples/mnt -o getline.packed.wasm + +# Run the WASM binary again without --mapdir +$ wasmtime run getline.packed.wasm -- /hello.txt +Hello +``` + +## Testing + +### Unit tests + +```console +$ CARGO_TARGET_WASM32_WASI_RUNNER=wasmtime cargo test --target wasm32-wasi +``` + +### End-to-end tests + +```console +$ cargo build --target wasm32-unknown-unknown +$ LIB_WASI_VFS_A=$PWD/target/wasm32-unknown-unknown/debug/libwasi_vfs.a ./tools/run-make-test.sh +``` + + +## How does it work? + +`wasi-vfs pack` command is a wrapper of [`wizer`](https://github.com/bytecodealliance/wizer/), which is a pre-initializer for Wasm applications. +The initialization process scans the mapped directories, then copies them into in-memory virtual filesystem. + +## Limitations + +Currently, this project only supports WASI applications on the top of wasi-libc because of the following reasons: + +This project depends on `wasm-ld` and `wasi-libc`'s imported symbol behavior. + `wasi-libc` declares some external symbols to import WASI functions in C like below. When `__imported_wasi_snapshot_preview1_fd_read` is not defined in any input object files, `wasm-ld` produces a `(import "wasi_snapshot_preview1" "fd_read")` entry. This is how `wasi-libc` calls WASI functions. + +```c +int32_t __imported_wasi_snapshot_preview1_fd_read(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) __attribute__(( + __import_module__("wasi_snapshot_preview1"), + __import_name__("fd_read") +)); +``` + +This project exploits that external symbols to hook WASI function calls by defining them in `libwasi_vfs.a`. If those symbols are defined, `wasm-ld` doesn't produce import entries, and it links symbols normally. + +Therefore, this project currently doesn't support Rust application, which calls WASI functions directly without using `wasi-libc`. + + +After [module-linking](https://github.com/WebAssembly/module-linking/blob/main/design/proposals/module-linking/Explainer.md) and [interface-types](https://github.com/WebAssembly/interface-types) will be merged, and WASI will adopt shared-nothing architecture, this project will be able to support all WASI applications. diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..359cf4a --- /dev/null +++ b/build.rs @@ -0,0 +1,30 @@ +use std::env; + +fn main() { + let triple = env::var("TARGET").expect("TARGET was not set"); + if !triple.starts_with("wasm32-") { + println!("wasi-vfs only supports wasm32-unknown-unknown"); + return; + } + let wasi_sdk = env::var("WASI_SDK_PATH").expect("WASI_SDK_PATH is not set"); + let mut build = cc::Build::new(); + build + .compiler(format!("{}/bin/clang", wasi_sdk)) + .archiver(format!("{}/bin/llvm-ar", wasi_sdk)) + .file("src/init.c"); + + let trampoline_file = if env::var("CARGO_FEATURE_LEGACY_WASI_LIBC").is_ok() { + "src/trampoline_generated_legacy_wasi_libc.c" + } else { + "src/trampoline_generated.c" + }; + build.file(trampoline_file); + + println!("cargo:rerun-if-changed=src/init.c"); + println!("cargo:rerun-if-changed={}", trampoline_file); + + build.file("src/embed/linked_storage.c"); + println!("cargo:rerun-if-changed=src/embed/linked_storage.c"); + + build.compile("wasi_vfs_c"); +} diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..1590630 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +too-many-arguments-threshold = 10 diff --git a/crates/wasi-libc-trampoline-bindgen/Cargo.toml b/crates/wasi-libc-trampoline-bindgen/Cargo.toml new file mode 100644 index 0000000..1d84802 --- /dev/null +++ b/crates/wasi-libc-trampoline-bindgen/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wasi-libc-trampoline-bindgen" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +witx = { path = "WASI/tools/witx" } +heck = "0.3" +structopt = "0.3.21" diff --git a/crates/wasi-libc-trampoline-bindgen/WASI b/crates/wasi-libc-trampoline-bindgen/WASI new file mode 160000 index 0000000..326dee6 --- /dev/null +++ b/crates/wasi-libc-trampoline-bindgen/WASI @@ -0,0 +1 @@ +Subproject commit 326dee685d352bc3845b999099f28b0a48734d46 diff --git a/crates/wasi-libc-trampoline-bindgen/src/lib.rs b/crates/wasi-libc-trampoline-bindgen/src/lib.rs new file mode 100644 index 0000000..e6cc460 --- /dev/null +++ b/crates/wasi-libc-trampoline-bindgen/src/lib.rs @@ -0,0 +1,63 @@ +use std::error::Error; + +use object_link::AbiVariant; +use structopt::StructOpt; + +mod object_link; +mod wrapper; + +pub(crate) const WASI_HOOK_FUNCTIONS: &[&str] = &[ + "fd_advise", + "fd_allocate", + "fd_close", + "fd_datasync", + "fd_fdstat_get", + "fd_fdstat_set_flags", + "fd_fdstat_set_rights", + "fd_filestat_get", + "fd_filestat_set_size", + "fd_filestat_set_times", + "fd_pread", + "fd_prestat_dir_name", + "fd_prestat_get", + "fd_pwrite", + "fd_read", + "fd_readdir", + "fd_renumber", + "fd_seek", + "fd_sync", + "fd_tell", + "fd_write", + "path_create_directory", + "path_filestat_get", + "path_filestat_set_times", + "path_link", + "path_open", + "path_readlink", + "path_remove_directory", + "path_rename", + "path_symlink", + "path_unlink_file", + "poll_oneoff", +]; + +#[derive(StructOpt)] +pub enum App { + Wrapper, + ObjectLink { abi_variant: AbiVariant }, +} + +impl App { + pub fn execute(self) -> Result<(), Box> { + let witx_paths = witx::phases::snapshot().unwrap(); + match self { + App::Wrapper => { + print!("{}", wrapper::generate(&witx_paths)); + } + App::ObjectLink { abi_variant } => { + print!("{}", object_link::generate(&witx_paths, abi_variant)); + } + } + Ok(()) + } +} diff --git a/crates/wasi-libc-trampoline-bindgen/src/main.rs b/crates/wasi-libc-trampoline-bindgen/src/main.rs new file mode 100644 index 0000000..f5313e9 --- /dev/null +++ b/crates/wasi-libc-trampoline-bindgen/src/main.rs @@ -0,0 +1,6 @@ +use structopt::StructOpt; +use wasi_libc_trampoline_bindgen::App; + +fn main() { + App::from_args().execute().unwrap(); +} diff --git a/crates/wasi-libc-trampoline-bindgen/src/object_link.rs b/crates/wasi-libc-trampoline-bindgen/src/object_link.rs new file mode 100644 index 0000000..52e20a8 --- /dev/null +++ b/crates/wasi-libc-trampoline-bindgen/src/object_link.rs @@ -0,0 +1,158 @@ +use heck::*; +use std::{path::Path, str::FromStr}; +use witx::*; + +#[derive(Debug, Clone, Copy)] +pub enum AbiVariant { + Legacy, + Latest, +} + +impl FromStr for AbiVariant { + type Err = String; + fn from_str(day: &str) -> Result { + match day { + "legacy" => Ok(Self::Legacy), + "latest" => Ok(Self::Latest), + other => Err(format!("unsupported abi variant {}", other)), + } + } +} + +pub fn generate>(witx_paths: &[P], variant: AbiVariant) -> String { + let doc = witx::load(witx_paths).unwrap(); + + let mut raw = String::new(); + raw.push_str( + "\ +// This file is automatically generated, DO NOT EDIT +// +// To regenerate this file run the `crates/wasi-libc-trampoline-bindgen` command + +#include + +", + ); + for m in doc.modules() { + render_module(&m, variant, &mut raw); + raw.push('\n'); + } + + raw +} + +trait RenderC { + fn render_c(&self, src: &mut String); +} + +impl RenderC for IntRepr { + fn render_c(&self, src: &mut String) { + match self { + IntRepr::U8 => src.push_str("uint8_t"), + IntRepr::U16 => src.push_str("uint16_t"), + IntRepr::U32 => src.push_str("uint32_t"), + IntRepr::U64 => src.push_str("uint64_t"), + } + } +} + +impl RenderC for WasmType { + fn render_c(&self, src: &mut String) { + match self { + WasmType::I32 => src.push_str("int32_t"), + WasmType::I64 => src.push_str("int64_t"), + WasmType::F32 => src.push_str("float"), + WasmType::F64 => src.push_str("double"), + } + } +} + +fn render_module(module: &Module, variant: AbiVariant, src: &mut String) { + for f in module.funcs() { + if !crate::WASI_HOOK_FUNCTIONS.contains(&f.name.as_str()) { + continue; + } + let f_name = f.name.as_str(); + + let abi_name = match variant { + AbiVariant::Latest => { + let mut name = String::new(); + name.push_str("__imported_"); + name.push_str(&module.name.as_str().to_snake_case()); + name.push('_'); + name.push_str(&f_name.to_snake_case()); + name + } + AbiVariant::Legacy => { + let mut name = String::new(); + name.push_str("__wasi_"); + name.push_str(&f_name.to_snake_case()); + name + } + }; + + render_libc_hook_point( + &*f, + &abi_name, + &format!("wasi_vfs_{}", f_name.to_snake_case()), + src, + ); + src.push('\n'); + } +} + +fn render_libc_hook_point( + func: &InterfaceFunc, + name: &str, + trampoline_name: &str, + src: &mut String, +) { + let (params, results) = func.wasm_signature(); + assert!(results.len() <= 1); + src.push_str("__attribute__((weak))\n"); + results[0].render_c(src); + let params_str = params + .iter() + .enumerate() + .map(|(i, param_ty)| { + let mut param = String::new(); + param_ty.render_c(&mut param); + param.push(' '); + param.push_str("arg"); + param.push_str(&i.to_string()); + param + }) + .collect::>() + .join(", "); + src.push(' '); + src.push_str(name); + src.push('('); + src.push_str(¶ms_str); + src.push(')'); + src.push_str(" {\n"); + + src.push_str(" extern "); + results[0].render_c(src); + src.push(' '); + src.push_str(trampoline_name); + src.push('('); + src.push_str(¶ms_str); + src.push(')'); + src.push_str(";\n"); + + src.push_str(" return "); + src.push_str(trampoline_name); + src.push('('); + src.push_str( + ¶ms + .iter() + .enumerate() + .map(|(i, _)| format!("arg{}", i)) + .collect::>() + .join(", "), + ); + src.push(')'); + src.push_str(";\n"); + + src.push_str("}\n"); +} diff --git a/crates/wasi-libc-trampoline-bindgen/src/wrapper.rs b/crates/wasi-libc-trampoline-bindgen/src/wrapper.rs new file mode 100644 index 0000000..1896dda --- /dev/null +++ b/crates/wasi-libc-trampoline-bindgen/src/wrapper.rs @@ -0,0 +1,550 @@ +use heck::*; +use std::io::{Read, Write}; +use std::mem; +use std::path::Path; +use std::process::{Command, Stdio}; +use witx::*; + +pub fn generate>(witx_paths: &[P]) -> String { + let doc = witx::load(witx_paths).unwrap(); + + let mut raw = String::new(); + raw.push_str( + "\ +// This file is automatically generated, DO NOT EDIT +// +// To regenerate this file run the `crates/wasi-libc-trampoline-bindgen` command +#![allow(unused_variables)] +use wasi::*; +use crate::UserFd; + +", + ); + for m in doc.modules() { + m.render(&mut raw); + raw.push('\n'); + } + + let mut rustfmt = Command::new("rustfmt") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + rustfmt + .stdin + .take() + .unwrap() + .write_all(raw.as_bytes()) + .unwrap(); + let mut ret = String::new(); + rustfmt + .stdout + .take() + .unwrap() + .read_to_string(&mut ret) + .unwrap(); + let status = rustfmt.wait().unwrap(); + assert!(status.success()); + ret +} + +trait Render { + fn render(&self, src: &mut String); +} + +impl Render for IntRepr { + fn render(&self, src: &mut String) { + match self { + IntRepr::U8 => src.push_str("u8"), + IntRepr::U16 => src.push_str("u16"), + IntRepr::U32 => src.push_str("u32"), + IntRepr::U64 => src.push_str("u64"), + } + } +} + +impl Render for TypeRef { + fn render(&self, src: &mut String) { + match self { + TypeRef::Name(t) => { + src.push_str(&t.name.as_str().to_camel_case()); + if let Type::List(_) = &**t.type_() { + src.push_str("<'_>"); + } + } + TypeRef::Value(v) => match &**v { + Type::Builtin(t) => t.render(src), + Type::List(t) => match &**t.type_() { + Type::Builtin(BuiltinType::Char) => src.push_str("&str"), + _ => { + src.push_str("&'a ["); + t.render(src); + src.push(']'); + } + }, + Type::Pointer(t) => { + src.push_str("*mut "); + t.render(src); + } + Type::ConstPointer(t) => { + src.push_str("*const "); + t.render(src); + } + Type::Variant(v) if v.is_bool() => src.push_str("bool"), + Type::Variant(v) => match v.as_expected() { + Some((ok, err)) => { + src.push_str("Result<"); + match ok { + Some(ty) => ty.render(src), + None => src.push_str("()"), + } + src.push(','); + match err { + Some(ty) => ty.render(src), + None => src.push_str("()"), + } + src.push('>'); + } + None => { + panic!("unsupported anonymous variant") + } + }, + Type::Record(r) if r.is_tuple() => { + src.push('('); + for member in r.members.iter() { + member.tref.render(src); + src.push(','); + } + src.push(')'); + } + t => panic!("reference to anonymous {} not possible!", t.kind()), + }, + } + } +} + +impl Render for BuiltinType { + fn render(&self, src: &mut String) { + match self { + // A C `char` in Rust we just interpret always as `u8`. It's + // technically possible to use `std::os::raw::c_char` but that's + // overkill for the purposes that we'll be using this type for. + BuiltinType::U8 { lang_c_char: _ } => src.push_str("u8"), + BuiltinType::U16 => src.push_str("u16"), + BuiltinType::U32 { + lang_ptr_size: false, + } => src.push_str("u32"), + BuiltinType::U32 { + lang_ptr_size: true, + } => src.push_str("usize"), + BuiltinType::U64 => src.push_str("u64"), + BuiltinType::S8 => src.push_str("i8"), + BuiltinType::S16 => src.push_str("i16"), + BuiltinType::S32 => src.push_str("i32"), + BuiltinType::S64 => src.push_str("i64"), + BuiltinType::F32 => src.push_str("f32"), + BuiltinType::F64 => src.push_str("f64"), + BuiltinType::Char => src.push_str("char"), + } + } +} + +impl Render for Module { + fn render(&self, src: &mut String) { + for f in self.funcs() { + if !crate::WASI_HOOK_FUNCTIONS.contains(&f.name.as_str()) { + continue; + } + let mut f_name = String::new(); + f.name.render(&mut f_name); + + render_trampoline( + &*f, + &format!("wasi_vfs_{}", f_name.to_snake_case()), + &self.name, + src, + ); + src.push('\n'); + } + } +} + +fn render_trace_syscall_entry_format_args(func: &InterfaceFunc, src: &mut String) { + src.push_str("format_args!(\""); + src.push_str(func.name.as_str()); + src.push('('); + + let mut raw_arg_names = vec![]; + for param in func.params.iter() { + match &**param.tref.type_() { + Type::List(_) => { + raw_arg_names.push(String::from(param.name.as_str())); + raw_arg_names.push(format!("{}_len", param.name.as_str())); + } + _ => { + raw_arg_names.push(String::from(param.name.as_str())); + } + } + } + + src.push_str( + &raw_arg_names + .iter() + .map(|name| format!("{}: {{}}", name)) + .collect::>() + .join(", "), + ); + src.push_str(")\\n\""); + for (i, _) in raw_arg_names.iter().enumerate() { + src.push_str(", "); + src.push_str("arg"); + src.push_str(&i.to_string()); + } + src.push(')'); +} + +fn render_trampoline(func: &InterfaceFunc, name: &str, module: &Id, src: &mut String) { + src.push_str(" #[no_mangle]\n"); + src.push_str("pub unsafe extern \"C\" fn "); + src.push_str(name); + + let (params, results) = func.wasm_signature(); + assert!(results.len() <= 1); + src.push('('); + + for (i, param) in params.iter().enumerate() { + src.push_str(&format!("arg{}: ", i)); + param.render(src); + src.push(','); + } + src.push(')'); + + if func.noreturn { + src.push_str(" -> !"); + } else if let Some(result) = results.get(0) { + src.push_str(" -> "); + result.render(src); + } + src.push_str("{\n"); + + { + src.push_str("#[cfg(feature = \"trace-syscall\")]\n"); + src.push_str("crate::trace::trace_syscall_entry("); + render_trace_syscall_entry_format_args(func, src); + src.push_str(");\n"); + } + { + src.push_str( + "let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { fs } else {\n", + ); + src.push_str(&format!( + "return wasi::{}::{}(\n", + module.as_str(), + func.name.as_str() + )); + for (i, _) in params.iter().enumerate() { + src.push_str(&format!("arg{}, ", i)); + } + src.push_str(");\n"); + src.push_str("};\n"); + } + func.call_interface( + module, + &mut Rust { + src, + func_name: func.name.as_str(), + block_storage: vec![], + blocks: vec![], + }, + ); + + src.push('}'); +} + +struct Rust<'a> { + src: &'a mut String, + func_name: &'a str, + block_storage: Vec, + blocks: Vec, +} + +impl Bindgen for Rust<'_> { + type Operand = String; + + fn emit( + &mut self, + inst: &Instruction<'_>, + operands: &mut Vec, + results: &mut Vec, + ) { + let mut top_as = |cvt: &str| { + let mut s = operands.pop().unwrap(); + s.push_str(" as "); + s.push_str(cvt); + results.push(s); + }; + + match inst { + Instruction::GetArg { nth } => { + results.push(format!("arg{}", nth)); + } + Instruction::AddrOf => todo!(), + Instruction::I32FromChar => todo!(), + Instruction::I64FromU64 => todo!(), + Instruction::I64FromS64 => todo!(), + Instruction::I32FromU32 => todo!(), + Instruction::I32FromS32 => todo!(), + Instruction::I32FromUsize => todo!(), + Instruction::I32FromU16 => todo!(), + Instruction::I32FromS16 => todo!(), + Instruction::I32FromU8 => todo!(), + Instruction::I32FromS8 => todo!(), + Instruction::I32FromChar8 => todo!(), + Instruction::I32FromPointer => todo!(), + Instruction::I32FromConstPointer => todo!(), + Instruction::I32FromHandle { .. } => todo!(), + Instruction::I32FromBitflags { .. } => todo!(), + Instruction::I64FromBitflags { .. } => todo!(), + Instruction::ListPointerLength => todo!(), + Instruction::ListFromPointerLength { ty } => { + let ptr = &operands[0]; + let len = &operands[1]; + match &**ty.type_() { + witx::Type::Builtin(witx::BuiltinType::Char) => { + results.push(format!("{{ + let str_bytes = core::slice::from_raw_parts({} as *const u8, ({} + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }}", ptr, len)); + } + _ => { + results.push(format!( + "core::slice::from_raw_parts({} as *const {}, {} as usize)", + ptr, + ty.to_rust_ident(), + len + )); + } + }; + } + Instruction::F32FromIf32 => todo!(), + Instruction::F64FromIf64 => todo!(), + Instruction::CallInterface { module, func } => { + results.push(format!( + "crate::{}::{}(fs, {})", + module, + func.name.as_str(), + operands.join(", ") + )); + } + Instruction::S8FromI32 => todo!(), + Instruction::U8FromI32 => todo!(), + Instruction::S16FromI32 => todo!(), + Instruction::U16FromI32 => todo!(), + Instruction::S32FromI32 => todo!(), + Instruction::U32FromI32 => top_as("u32"), + Instruction::UsizeFromI32 => top_as("usize"), + Instruction::S64FromI64 => top_as("i64"), + Instruction::U64FromI64 => top_as("u64"), + Instruction::CharFromI32 => todo!(), + Instruction::Char8FromI32 => todo!(), + Instruction::If32FromF32 => todo!(), + Instruction::If64FromF64 => todo!(), + Instruction::HandleFromI32 { ty } => { + if ty.name.as_str() == "fd" { + top_as("UserFd"); + } else { + top_as(&ty.to_rust_ident()) + } + } + Instruction::PointerFromI32 { ty } => top_as(&format!("*mut {}", ty.to_rust_ident())), + Instruction::ConstPointerFromI32 { ty } => { + top_as(&format!("*const {}", ty.to_rust_ident())) + } + Instruction::BitflagsFromI32 { ty } => top_as(&ty.to_rust_ident()), + Instruction::BitflagsFromI64 { ty } => top_as(&ty.to_rust_ident()), + Instruction::ReturnPointerGet { .. } => todo!(), + Instruction::Load { .. } => todo!(), + Instruction::Store { ty } => { + let ptr = operands.pop().unwrap(); + let val = operands.pop().unwrap(); + self.src.push_str(&format!( + "core::ptr::write({} as *mut {}, {})", + ptr, + ty.to_rust_ident(), + val + )); + } + Instruction::ResultLift => todo!(), + Instruction::ResultLower { .. } => { + let err = self.blocks.pop().unwrap(); + let ok = self.blocks.pop().unwrap(); + let val = operands.pop().unwrap(); + results.push(format!( + "{{ + match {} {{ + Ok(e) => {{ {}; wasi::ERRNO_SUCCESS.raw() as i32 }} + Err(e) => {{ + #[cfg(feature = \"trace-syscall\")] + crate::trace::trace_syscall_error(\"{}\", e.clone()); + + {} + }} + }} + }}", + val, ok, self.func_name, err + )); + } + Instruction::EnumLift { .. } => { + // noop because some enum's constructor are invisible + results.push(operands.pop().unwrap()) + } + Instruction::EnumLower { ty } => { + // noop because some enum's constructor are invisible + assert_eq!(ty.name.as_str(), "errno"); + let val = operands.pop().unwrap(); + results.push(format!("{}.raw() as i32", val)); + } + Instruction::TupleLift { .. } => todo!(), + Instruction::TupleLower { .. } => todo!(), + Instruction::ReuseReturn => todo!(), + Instruction::Return { amt: 1 } => { + let ret = operands.pop().unwrap(); + self.src.push_str(&ret); + } + Instruction::Return { .. } => todo!(), + Instruction::VariantPayload => results.push(String::from("e")), + other => panic!("no implementation for {:?}", other), + } + } + + fn allocate_space(&mut self, _slot: usize, _ty: &NamedType) { + unimplemented!(); + } + + fn push_block(&mut self) { + let prev = std::mem::take(self.src); + self.block_storage.push(prev); + } + + fn finish_block(&mut self, operand: Option) { + let to_restore = self.block_storage.pop().unwrap(); + let src = mem::replace(self.src, to_restore); + match operand { + None => { + self.blocks.push(src); + } + Some(s) => { + if src.is_empty() { + self.blocks.push(s); + } else { + self.blocks.push(format!("{{ {}; {} }}", src, s)); + } + } + } + } +} + +fn to_rust_ident(name: &str) -> &str { + match name { + "in" => "in_", + "type" => "type_", + "yield" => "yield_", + s => s, + } +} + +impl Render for Id { + fn render(&self, src: &mut String) { + src.push_str(to_rust_ident(self.as_str())) + } +} + +impl Render for WasmType { + fn render(&self, src: &mut String) { + match self { + WasmType::I32 => src.push_str("i32"), + WasmType::I64 => src.push_str("i64"), + WasmType::F32 => src.push_str("f32"), + WasmType::F64 => src.push_str("f64"), + } + } +} + +trait ToRustIdent { + fn to_rust_ident(&self) -> String; +} + +impl ToRustIdent for NamedType { + fn to_rust_ident(&self) -> String { + let mut buf = String::new(); + let src = &mut buf; + src.push_str(&self.name.as_str().to_camel_case()); + if let Type::List(_) = &**self.type_() { + src.push_str("<'_>"); + } + buf + } +} + +impl ToRustIdent for TypeRef { + fn to_rust_ident(&self) -> String { + let mut buf = String::new(); + let src = &mut buf; + match self { + TypeRef::Name(t) => { + src.push_str(&t.name.as_str().to_camel_case()); + if let Type::List(_) = &**t.type_() { + src.push_str("<'_>"); + } + } + TypeRef::Value(v) => match &**v { + Type::Builtin(t) => t.render(src), + Type::List(t) => match &**t.type_() { + Type::Builtin(BuiltinType::Char) => src.push_str("&str"), + _ => { + src.push_str("&'a ["); + t.render(src); + src.push(']'); + } + }, + Type::Pointer(t) => { + src.push_str("*mut "); + t.render(src); + } + Type::ConstPointer(t) => { + src.push_str("*const "); + t.render(src); + } + Type::Variant(v) if v.is_bool() => src.push_str("bool"), + Type::Variant(v) => match v.as_expected() { + Some((ok, err)) => { + src.push_str("Result<"); + match ok { + Some(ty) => ty.render(src), + None => src.push_str("()"), + } + src.push(','); + match err { + Some(ty) => ty.render(src), + None => src.push_str("()"), + } + src.push('>'); + } + None => { + panic!("unsupported anonymous variant") + } + }, + Type::Record(r) if r.is_tuple() => { + src.push('('); + for member in r.members.iter() { + member.tref.render(src); + src.push(','); + } + src.push(')'); + } + t => panic!("reference to anonymous {} not possible!", t.kind()), + }, + } + buf + } +} diff --git a/crates/wasi-vfs-cli/Cargo.toml b/crates/wasi-vfs-cli/Cargo.toml new file mode 100644 index 0000000..25ed3b9 --- /dev/null +++ b/crates/wasi-vfs-cli/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wasi-vfs-cli" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "wasi-vfs" + +[dependencies] +anyhow = "1.0.40" +structopt = "0.3.21" +wizer = { git = "https://github.com/bytecodealliance/wizer.git", rev = "1802237aeb8c19ff9448d67d280935bc10110d32" } diff --git a/crates/wasi-vfs-cli/src/bin/wasi-vfs.rs b/crates/wasi-vfs-cli/src/bin/wasi-vfs.rs new file mode 100644 index 0000000..c181b6f --- /dev/null +++ b/crates/wasi-vfs-cli/src/bin/wasi-vfs.rs @@ -0,0 +1,6 @@ +use structopt::StructOpt; +use wasi_vfs_cli::App; + +fn main() { + App::from_args().execute().unwrap(); +} diff --git a/crates/wasi-vfs-cli/src/lib.rs b/crates/wasi-vfs-cli/src/lib.rs new file mode 100644 index 0000000..75eeeeb --- /dev/null +++ b/crates/wasi-vfs-cli/src/lib.rs @@ -0,0 +1,71 @@ +use std::path::PathBuf; + +use anyhow::Result; +use structopt::StructOpt; +mod module_link; + +fn parse_map_dirs(s: &str) -> anyhow::Result<(PathBuf, PathBuf)> { + let parts: Vec<&str> = s.split("::").collect(); + if parts.len() != 2 { + anyhow::bail!("must contain exactly one double colon ('::')"); + } + Ok((parts[0].into(), parts[1].into())) +} + +#[derive(Debug, StructOpt)] +pub enum App { + #[structopt(setting(structopt::clap::AppSettings::Hidden))] + LinkModule { + #[structopt(parse(from_os_str))] + input: PathBuf, + + #[structopt(short, parse(from_os_str))] + output: PathBuf, + }, + + /// Package directories into Wasm module + Pack { + /// The input Wasm module's file path. + #[structopt(parse(from_os_str))] + input: PathBuf, + + /// Package a host directory into Wasm module at a guest directory + #[structopt(long = "mapdir", value_name = "GUEST_DIR::HOST_DIR", parse(try_from_str = parse_map_dirs))] + map_dirs: Vec<(PathBuf, PathBuf)>, + + /// The file path to write the output Wasm module to. + #[structopt(long, short, parse(from_os_str))] + output: PathBuf, + }, +} + +impl App { + pub fn execute(self) -> Result<()> { + match self { + App::LinkModule { input, .. } => { + let bytes = std::fs::read(&input)?; + module_link::link(&bytes); + } + App::Pack { + input, + map_dirs, + output, + } => { + std::env::set_var("__WASI_VFS_PACKING", "1"); + let mut wizer = wizer::Wizer::new(); + wizer.allow_wasi(true); + wizer.init_func("wasi_vfs_pack_fs"); + wizer.inherit_stdio(true); + wizer.inherit_env(true); + wizer.keep_init_func(true); + for (guest_dir, host_dir) in map_dirs { + wizer.map_dir(guest_dir, host_dir); + } + let wasm_bytes = std::fs::read(&input)?; + let output_bytes = wizer.run(&wasm_bytes)?; + std::fs::write(output, output_bytes)?; + } + } + Ok(()) + } +} diff --git a/crates/wasi-vfs-cli/src/module_link.rs b/crates/wasi-vfs-cli/src/module_link.rs new file mode 100644 index 0000000..230ec1b --- /dev/null +++ b/crates/wasi-vfs-cli/src/module_link.rs @@ -0,0 +1,60 @@ +/// Given thse two modules: +/// +/// libwasi_vfs.wasm +/// ```webassembly +/// (module +/// (import "wasi_snapshot_preview1" "fd_read" (func (param i32 i32 i32 i32) (result i32))) +/// (export "wasi_vfs_fd_read" (func $wasi_vfs_fd_read.command_export)) +/// ) +/// ``` +/// +/// main.wasm +/// ```webassembly +/// (module +/// (import "wasi_snapshot_preview1" "fd_read" (func (param i32 i32 i32 i32) (result i32))) +/// ) +/// ``` +/// +/// This function generates the following adapter module: +/// +/// ```webassembly +/// (adapter module +/// (import "wasi_snapshot_preview1" (instance $wasi_snapshot_preview1 +/// (export "fd_read" (func (param i32 i32 i32 i32) (result i32))) +/// )) +/// +/// ;; libwasi_vfs.wasm +/// (module $Vfs +/// (import "wasi_snapshot_preview1" "fd_read" (func (param i32 i32 i32 i32) (result i32))) +/// (export "wasi_vfs_fd_read" (func $wasi_vfs_fd_read.command_export)) +/// ) +/// +/// ;; main.wasm +/// (module $Main +/// (import "wasi_snapshot_preview1" "fd_read" (func (param i32 i32 i32 i32) (result i32))) +/// ) +/// +/// (instance $VfsInstance (instantiate $Vfs +/// (import "wasi_snapshot_preview1" (instance $wasi_snapshot_preview1)) +/// )) +/// +/// (instance $MainInstance (instantiate $Main +/// (import "wasi_snapshot_preview1" (instance $VfsInstance)) +/// )) +/// ) +/// ``` +/// +pub fn link(_main_bytes: &[u8]) { + unimplemented!( + r#" + This feature requires module-linking and interface-type proposals, and interface-type support of WASI. + Current WASI interface (wasi_snapshot_preview1) requires the callee to access the caller's linear memory, + so VFS instance has to import it while exporting WASI APIs. Also, calling real WASI API from VFS instance + requires the VFS instance to pass the pointer of the main instance memory. In theory, a virtualized WASI API + in VFS only calls corresponding real WASI API, so it's possible to implement this feature without interface-type + by annotating which parameters and results are pull/push address, and who has the ownership of the pointer. + However, it's hard to maintain such annotations, so I decided to wait the interface-type proposal and WASI adaptation + of shared-nothing architecture. + "# + ) +} diff --git a/docs/overview.png b/docs/overview.png new file mode 100644 index 0000000..bbbea60 Binary files /dev/null and b/docs/overview.png differ diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..19fdbd8 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,5 @@ +/tools +/*.c.o +/*.wasm +fs.c +fs.o diff --git a/examples/getline.c b/examples/getline.c new file mode 100644 index 0000000..d0ff6ae --- /dev/null +++ b/examples/getline.c @@ -0,0 +1,23 @@ +#include +#include + +int main(int argc, char *argv[]) { + char *line = NULL; + size_t len = 0; + ssize_t nread; + FILE *f; + + if (argc != 2) { + printf("No file specified\n"); + exit(EXIT_FAILURE); + } + + if ((f = fopen(argv[1], "r")) == NULL) { + perror("fopen"); + exit(EXIT_FAILURE); + } + while ((nread = getline(&line, &len, f)) != -1) { + fwrite(line, nread, 1, stdout); + } + return 0; +} diff --git a/examples/mnt/hello.txt b/examples/mnt/hello.txt new file mode 100644 index 0000000..e965047 --- /dev/null +++ b/examples/mnt/hello.txt @@ -0,0 +1 @@ +Hello diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..9831f9b --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "stable" +targets = [ "wasm32-unknown-unknown", "wasm32-wasi" ] diff --git a/src/alloc.rs b/src/alloc.rs new file mode 100644 index 0000000..f0d1d4b --- /dev/null +++ b/src/alloc.rs @@ -0,0 +1,77 @@ +//! A global allocator which wraps wasi-libc's malloc while targeting wasm32-unknown-unknown + +#[cfg(not(feature = "module-linking"))] +const MIN_ALIGN: usize = 8; + +#[cfg(not(feature = "module-linking"))] +struct WasiAllocator; + +#[cfg(not(feature = "module-linking"))] +unsafe impl std::alloc::GlobalAlloc for WasiAllocator { + #[inline] + unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 { + extern "C" { + fn malloc(amt: usize) -> *mut std::ffi::c_void; + fn aligned_alloc(a: usize, b: usize) -> *mut std::ffi::c_void; + } + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + malloc(layout.size()) as *mut u8 + } else { + aligned_alloc(layout.align(), layout.size()) as *mut u8 + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: std::alloc::Layout) -> *mut u8 { + extern "C" { + fn calloc(amt: usize, amt2: usize) -> *mut std::ffi::c_void; + } + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + calloc(layout.size(), 1) as *mut u8 + } else { + let ptr = self.alloc(layout); + if !ptr.is_null() { + std::ptr::write_bytes(ptr, 0, layout.size()); + } + ptr + } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, _layout: std::alloc::Layout) { + extern "C" { + fn free(ptr: *mut std::ffi::c_void); + } + free(ptr as *mut std::ffi::c_void) + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: std::alloc::Layout, new_size: usize) -> *mut u8 { + extern "C" { + fn realloc(ptr: *mut std::ffi::c_void, amt: usize) -> *mut std::ffi::c_void; + } + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + realloc(ptr as *mut std::ffi::c_void, new_size) as *mut u8 + } else { + let new_layout = + std::alloc::Layout::from_size_align_unchecked(new_size, layout.align()); + + let new_ptr = std::alloc::GlobalAlloc::alloc(self, new_layout); + if !new_ptr.is_null() { + let size = std::cmp::min(layout.size(), new_size); + std::ptr::copy_nonoverlapping(ptr, new_ptr, size); + std::alloc::GlobalAlloc::dealloc(self, ptr, layout); + } + new_ptr + } + } +} + +#[cfg(not(feature = "module-linking"))] +#[global_allocator] +static GLOBAL: WasiAllocator = WasiAllocator; + +// use self-contained allocator when module-linking +#[cfg(feature = "module-linking")] +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; diff --git a/src/embed/linked_storage.c b/src/embed/linked_storage.c new file mode 100644 index 0000000..2fe76f1 --- /dev/null +++ b/src/embed/linked_storage.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include + +typedef uint16_t wasi_errno_t; +#define WASI_ERRNO_SUCCESS (0) +#define WASI_ERRNO_NOENT (44) +#define WASI_ERRNO_NOTDIR (54) + +struct wasi_vfs_embed_linked_storage {}; + +struct wasi_vfs_node; +struct wasi_vfs_link { + struct wasi_vfs_link *parent; + struct wasi_vfs_node *node; +}; + +struct wasi_vfs_dirent { + struct wasi_vfs_link *link; + const char *name; + struct wasi_vfs_dirent *next; +}; + +// IMPORTANT: This layout must match the layout of struct InnerNode in +// Rust-side. +struct wasi_vfs_node { + bool is_dir; + size_t count; + union { + uint8_t *data; + struct wasi_vfs_dirent *dirents; + }; +}; + +typedef struct { + struct wasi_vfs_node *node; + struct wasi_vfs_link *link; +} node_link_t; + +static void insert_dirent(struct wasi_vfs_node *node, + struct wasi_vfs_dirent *dirent) { + dirent->next = node->dirents; + node->dirents = dirent; + node->count++; +} + +static struct wasi_vfs_node *new_node(bool is_dir) { + struct wasi_vfs_node *node = malloc(sizeof(struct wasi_vfs_node)); + node->is_dir = is_dir; + node->count = 0; + node->data = NULL; + return node; +} + +static struct wasi_vfs_link *new_link(struct wasi_vfs_node *node) { + struct wasi_vfs_link *link = malloc(sizeof(struct wasi_vfs_link)); + link->parent = NULL; + link->node = node; + return link; +} + +static struct wasi_vfs_dirent *new_dirent(struct wasi_vfs_link *link, + const char *name) { + struct wasi_vfs_dirent *dirent = malloc(sizeof(struct wasi_vfs_dirent)); + dirent->link = link; + dirent->name = strdup(name); + dirent->next = NULL; + return dirent; +} + +struct wasi_vfs_embed_linked_storage *wasi_vfs_embed_linked_storage_new(void) { + return malloc(sizeof(struct wasi_vfs_embed_linked_storage)); +} + +void wasi_vfs_embed_linked_storage_free( + struct wasi_vfs_embed_linked_storage *self) { + free(self); +} + +node_link_t wasi_vfs_embed_linked_storage_preopen_new_dir( + struct wasi_vfs_embed_linked_storage *self) { + (void)self; + struct wasi_vfs_node *node = new_node(true); + struct wasi_vfs_link *link = new_link(node); + return (node_link_t){node, link}; +} + +node_link_t wasi_vfs_embed_linked_storage_new_dir( + struct wasi_vfs_embed_linked_storage *self, const node_link_t *parent, + char *name) { + (void)self; + struct wasi_vfs_node *node = new_node(true); + struct wasi_vfs_link *link = new_link(node); + link->parent = parent->link; + + assert(parent->node->is_dir && "parent is not a dir"); + + struct wasi_vfs_dirent *dirent = new_dirent(link, name); + insert_dirent(parent->node, dirent); + + return (node_link_t){node, link}; +} + +node_link_t wasi_vfs_embed_linked_storage_new_file( + struct wasi_vfs_embed_linked_storage *self, const node_link_t *parent, + char *name, uint8_t *content, size_t content_len) { + + (void)self; + struct wasi_vfs_node *node = new_node(false); + node->count = content_len; + node->data = content; + + struct wasi_vfs_link *link = new_link(node); + link->parent = parent->link; + link->node = node; + + struct wasi_vfs_dirent *dirent = new_dirent(link, name); + insert_dirent(parent->node, dirent); + + return (node_link_t){node, link}; +} + +wasi_errno_t wasi_vfs_embed_linked_storage_resolve_node_at( + struct wasi_vfs_embed_linked_storage *self, const node_link_t *base, + const char *path, node_link_t *out) { + (void)self; + + node_link_t current = *base; + +find_parent_node: + while (path[0] != '\0') { + // strip leading '/' + while (path[0] == '/') { + path++; + } + const char *component = path; + while (path[0] != '/' && path[0] != '\0') { + path++; + } + const size_t component_len = path - component; + + // expect that the current node is a dir + if (!current.node->is_dir) { + return WASI_ERRNO_NOTDIR; + } + + // ok we are in a dir + if (component_len == 1 && component[0] == '.') { + // empty component, skip + continue; + } + + if (component_len == 2 && component[0] == '.' && component[1] == '.') { + // '..' component, go up + if (current.link->parent == NULL) { + // we are already at the root + return WASI_ERRNO_NOTDIR; + } + struct wasi_vfs_link *parent = current.link->parent; + current = (node_link_t){.node = parent->node, .link = parent}; + continue; + } + + // ok we have flattened special components, find children + struct wasi_vfs_dirent *dirent = current.node->dirents; + while (dirent != NULL) { + if (strncmp(dirent->name, component, component_len) == 0 && + dirent->name[component_len] == '\0') { + // found the child + current = + (node_link_t){.node = dirent->link->node, .link = dirent->link}; + goto find_parent_node; + } + dirent = dirent->next; + } + return WASI_ERRNO_NOENT; + } + *out = current; + return WASI_ERRNO_SUCCESS; +} diff --git a/src/embed/linked_storage.rs b/src/embed/linked_storage.rs new file mode 100644 index 0000000..de46a71 --- /dev/null +++ b/src/embed/linked_storage.rs @@ -0,0 +1,237 @@ +//! This module consists of C and Rust implementations. +//! C part builds link graph by chaining nodes by pointers. +//! Rust part is a thin wrapper to conform to `Storage` trait. + +use std::{ + ffi::{CStr, CString}, + mem::MaybeUninit, + path::Path, +}; + +use super::{DirEntry, Link, Node, NodeDirBody, NodeFileBody, NodeIdTrait, Storage}; + +#[repr(transparent)] +#[derive(Hash, Clone, Copy, PartialEq, Eq)] +pub struct NodeId(*const std::ffi::c_void); + +impl NodeIdTrait for NodeId { + fn ino(&self) -> u64 { + self.0 as u64 + } +} + +#[repr(transparent)] +#[derive(Hash, Clone, Copy, PartialEq, Eq)] +pub struct LinkId(*const std::ffi::c_void); + +pub struct LinkedStorage { + context: *mut std::ffi::c_void, +} + +#[repr(C)] +struct NodeLink { + node_id: NodeId, + link_id: LinkId, +} + +impl NodeFileBody for InnerNode { + fn content(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(std::mem::transmute(self.dir_or_file), self.count) } + } +} + +impl NodeDirBody for InnerNode { + type Iter = LinkedStorageIterator; + fn entries(&self) -> Self::Iter { + LinkedStorageIterator(self.dir_or_file as *const InnerDirent) + } +} + +pub(crate) struct LinkedStorageIterator(*const InnerDirent); +impl Iterator for LinkedStorageIterator { + type Item = DirEntry; + + fn next(&mut self) -> Option> { + if self.0.is_null() { + return None; + } + let (entry, next) = unsafe { + let node = &(*self.0); + let name = CStr::from_ptr(node.name).to_string_lossy().into_owned(); + let link_id = LinkId(node.link_id); + (DirEntry:: { name, link_id }, node.next) + }; + self.0 = next; + Some(entry) + } +} + +#[repr(C)] +pub(crate) struct InnerNode { + is_dir: bool, + count: usize, + dir_or_file: *const std::ffi::c_void, +} + +#[repr(C)] +struct InnerDirent { + link_id: *const std::ffi::c_void, + name: *const i8, + next: *const InnerDirent, +} + +#[repr(C)] +struct InnerLink { + parent: LinkId, + node: NodeId, +} + +extern "C" { + fn wasi_vfs_embed_linked_storage_new() -> *mut std::ffi::c_void; + fn wasi_vfs_embed_linked_storage_free(context: *mut std::ffi::c_void); + fn wasi_vfs_embed_linked_storage_preopen_new_dir(context: *mut std::ffi::c_void) -> NodeLink; + fn wasi_vfs_embed_linked_storage_new_dir( + context: *mut std::ffi::c_void, + parent: *const NodeLink, + name: *const i8, + ) -> NodeLink; + fn wasi_vfs_embed_linked_storage_new_file( + context: *mut std::ffi::c_void, + parent: *const NodeLink, + name: *const i8, + content: *const u8, + content_len: usize, + ) -> NodeLink; + fn wasi_vfs_embed_linked_storage_resolve_node_at( + context: *mut std::ffi::c_void, + base: *const NodeLink, + path: *const i8, + ret: *mut NodeLink, + ) -> wasi::Errno; +} + +impl Default for LinkedStorage { + fn default() -> Self { + Self::new() + } +} + +impl LinkedStorage { + pub(crate) fn new() -> Self { + Self { + context: unsafe { wasi_vfs_embed_linked_storage_new() }, + } + } +} + +impl Storage for LinkedStorage { + type NodeId = NodeId; + type LinkId = LinkId; + type NodeFileBody = InnerNode; + type NodeDirBody = InnerNode; + + fn new_root_dir(&mut self) -> (NodeId, LinkId) { + unsafe { + let ret = wasi_vfs_embed_linked_storage_preopen_new_dir(self.context); + (ret.node_id, ret.link_id) + } + } + + fn new_dir(&mut self, parent: (NodeId, LinkId), name: String) -> (NodeId, LinkId) { + unsafe { + let name = CString::new(name).unwrap(); + let link = NodeLink { + node_id: parent.0, + link_id: parent.1, + }; + let result = wasi_vfs_embed_linked_storage_new_dir(self.context, &link, name.as_ptr()); + (result.node_id, result.link_id) + } + } + + fn new_file( + &mut self, + parent: (NodeId, LinkId), + name: String, + content: Vec, + ) -> (NodeId, LinkId) { + unsafe { + let name = CString::new(name).unwrap(); + let content_len = content.len(); + let content = Box::new(content); + let link = NodeLink { + node_id: parent.0, + link_id: parent.1, + }; + let result = wasi_vfs_embed_linked_storage_new_file( + self.context, + &link, + name.as_ptr(), + content.leak().as_ptr(), + content_len, + ); + (result.node_id, result.link_id) + } + } + + fn get_inode(&self, node_id: &NodeId) -> Node { + unsafe { + let node = node_id.0 as *const InnerNode; + if (*node).is_dir { + Node::Dir(node.as_ref().unwrap()) + } else { + Node::File(node.as_ref().unwrap()) + } + } + } + + fn get_link(&self, link_id: &LinkId) -> Link { + unsafe { + let inner_link = link_id.0 as *const InnerLink; + let inner_link = inner_link.as_ref().unwrap(); + let parent = if inner_link.parent.0.is_null() { + None + } else { + Some(inner_link.parent) + }; + Link { + parent, + node: inner_link.node, + } + } + } + + fn resolve_node( + &self, + base: NodeId, + base_link: LinkId, + path: &Path, + ) -> Result<(NodeId, LinkId), wasi::Errno> { + unsafe { + let path: &[i8] = std::mem::transmute(path.as_os_str()); + let link = NodeLink { + node_id: base, + link_id: base_link, + }; + let mut ret = MaybeUninit::uninit(); + let errno = wasi_vfs_embed_linked_storage_resolve_node_at( + self.context, + &link, + path.as_ptr(), + ret.as_mut_ptr(), + ); + if errno == wasi::ERRNO_SUCCESS { + let ret = ret.assume_init(); + Ok((ret.node_id, ret.link_id)) + } else { + Err(errno) + } + } + } +} + +impl Drop for LinkedStorage { + fn drop(&mut self) { + unsafe { wasi_vfs_embed_linked_storage_free(self.context) } + } +} diff --git a/src/embed/mod.rs b/src/embed/mod.rs new file mode 100644 index 0000000..a7ae5a9 --- /dev/null +++ b/src/embed/mod.rs @@ -0,0 +1,369 @@ +//! This module provides an in-memory filesystem implementation. + +mod linked_storage; +pub use linked_storage::LinkedStorage; + +use crate::Vfd; +use std::{collections::HashMap, path::Path}; + +pub(crate) trait NodeIdTrait { + fn ino(&self) -> u64; +} + +pub(crate) trait NodeFileBody { + fn content(&self) -> &[u8]; +} + +pub(crate) struct DirEntry { + pub(crate) name: String, + pub(crate) link_id: S::LinkId, +} + +impl Clone for DirEntry { + fn clone(&self) -> Self { + DirEntry { + name: self.name.clone(), + link_id: self.link_id, + } + } +} + +pub(crate) trait NodeDirBody { + type Iter: Iterator>; + fn entries(&self) -> Self::Iter; +} + +/// A storage that can be used to store files and directories. +pub(crate) trait Storage { + type NodeId: NodeIdTrait + Clone + Copy; + type LinkId: Clone + Copy; + type NodeFileBody: NodeFileBody; + type NodeDirBody: NodeDirBody; + + /// Creates a new root node. + fn new_root_dir(&mut self) -> (Self::NodeId, Self::LinkId); + + /// Creates a new directory node under the given parent node. + fn new_dir( + &mut self, + parent: (Self::NodeId, Self::LinkId), + name: String, + ) -> (Self::NodeId, Self::LinkId); + + /// Creates a new file node under the given parent node. + fn new_file( + &mut self, + parent: (Self::NodeId, Self::LinkId), + name: String, + content: Vec, + ) -> (Self::NodeId, Self::LinkId); + + /// Resolve a node from its id. + fn get_inode(&self, node_id: &Self::NodeId) -> Node; + + /// Resolve a link from its id. + fn get_link(&self, link_id: &Self::LinkId) -> Link; + + /// Resolve a node from base node and relative path. + fn resolve_node( + &self, + base: Self::NodeId, + base_link: Self::LinkId, + path: &Path, + ) -> Result<(Self::NodeId, Self::LinkId), wasi::Errno>; +} + +pub(crate) enum Node<'a, S: Storage + ?Sized> { + File(&'a S::NodeFileBody), + Dir(&'a S::NodeDirBody), +} + +/// Represent a hard link to an inode +pub(crate) struct Link { + pub(crate) parent: Option, + pub(crate) node: S::NodeId, +} + +impl Clone for Link { + fn clone(&self) -> Self { + Link { + parent: self.parent, + node: self.node, + } + } +} + +impl Copy for Link {} + +pub(crate) struct FdEntry { + pub(crate) offset: usize, + pub(crate) link_id: S::LinkId, + pub(crate) node_id: S::NodeId, + pub(crate) flags: wasi::Fdflags, +} + +pub(crate) struct PreopenedDir { + pub(crate) path: String, +} + +pub(crate) struct EmbeddedFs { + preopened_dirs: Vec, + storage: S, + + opens: HashMap>, + fd_issuer: IdIssuer, +} + +#[derive(Default)] +struct IdIssuer { + next_id: Id, +} + +impl IdIssuer { + fn new(base: Id) -> Self { + Self { next_id: base } + } +} + +impl + Clone> IdIssuer { + fn issue(&mut self) -> Id { + let id = self.next_id.clone(); + self.next_id += 1; + id + } +} + +impl Default for EmbeddedFs +where + S: Default, +{ + fn default() -> Self { + Self::new(S::default()) + } +} + +impl EmbeddedFs { + pub(crate) fn new(storage: S) -> Self { + Self { + preopened_dirs: vec![], + storage, + opens: HashMap::new(), + fd_issuer: IdIssuer::new(0_u32), + } + } + + pub(crate) fn preopen_dir(&mut self, path: String) -> (Vfd, S::NodeId, S::LinkId) { + assert!(self.preopened_dirs.len() == self.opens.len()); + let fd = self.fd_issuer.issue(); + self.preopened_dirs.push(PreopenedDir { path }); + let (node_id, link_id) = self.storage.new_root_dir(); + self.opens.insert( + fd, + FdEntry { + offset: 0, + node_id, + link_id, + flags: 0, + }, + ); + (fd, node_id, link_id) + } + + pub(crate) fn get_preopened_dir_path(&self, vfd: Vfd) -> Option<&str> { + let vfd = vfd as usize; + if vfd >= self.preopened_dirs.len() { + return None; + } + Some(&self.preopened_dirs[vfd].path) + } + + pub(crate) fn create_file( + &mut self, + dir_node: S::NodeId, + dir_link: S::LinkId, + mut relpath: &str, + content: Vec, + ) -> Result<(), u16> { + let mut cursor = match self.storage.get_inode(&dir_node) { + Node::Dir { .. } => (dir_node, dir_link), + _ => return Err(wasi::ERRNO_BADF.raw()), + }; + if relpath.starts_with('/') { + relpath = &relpath[1..]; + } + let components = relpath.split('/').collect::>(); + let filename = match components.last() { + Some(filename) => *filename, + None => return Err(wasi::ERRNO_NOENT.raw()), + }; + + let components_len = components.len(); + 'find_parent_node: for component in components.into_iter().take(components_len - 1) { + if component == "." { + continue; + } + let entries = match self.storage.get_inode(&cursor.0) { + Node::Dir(body) => body.entries(), + _ => return Err(wasi::ERRNO_BADF.raw()), + }; + for entry in entries { + if component == entry.name { + cursor = (self.storage.get_link(&entry.link_id).node, entry.link_id); + continue 'find_parent_node; + } + } + // create a new intermediate directory + { + let (new_dir_id, new_link_id) = self.storage.new_dir(cursor, component.to_string()); + cursor = (new_dir_id, new_link_id); + } + } + + self.storage.new_file(cursor, filename.to_string(), content); + + Ok(()) + } + + pub(crate) fn get_node_id_by_link(&self, id: S::LinkId) -> S::NodeId { + self.storage.get_link(&id).node + } + + pub(crate) fn get_node(&self, fd: Vfd) -> Result, wasi::Errno> { + match self.opens.get(&fd) { + Some(entry) => Ok(self.storage.get_inode(&entry.node_id)), + None => Err(wasi::ERRNO_BADF), + } + } + + pub(crate) fn get_fd_stat(&self, fd: Vfd) -> Result { + const READ_ONLY_RIGHTS: wasi::Rights = wasi::RIGHTS_FD_READ + | wasi::RIGHTS_FD_ADVISE + | wasi::RIGHTS_PATH_OPEN + | wasi::RIGHTS_FD_READDIR + | wasi::RIGHTS_FD_FILESTAT_GET; + let entry = match self.opens.get(&fd) { + Some(entry) => entry, + None => return Err(wasi::ERRNO_BADF), + }; + Ok(match self.storage.get_inode(&entry.node_id) { + Node::File { .. } => wasi::Fdstat { + fs_filetype: wasi::FILETYPE_REGULAR_FILE, + fs_flags: entry.flags, + fs_rights_base: READ_ONLY_RIGHTS, + fs_rights_inheriting: READ_ONLY_RIGHTS, + }, + Node::Dir { .. } => wasi::Fdstat { + fs_filetype: wasi::FILETYPE_DIRECTORY, + fs_flags: entry.flags, + fs_rights_base: READ_ONLY_RIGHTS, + fs_rights_inheriting: READ_ONLY_RIGHTS, + }, + }) + } + + pub(crate) fn get_filestat_from_node_id(&self, node_id: S::NodeId) -> wasi::Filestat { + let mut stat = wasi::Filestat { + dev: Default::default(), + ino: Default::default(), + filetype: wasi::FILETYPE_UNKNOWN, + nlink: Default::default(), + size: Default::default(), + atim: Default::default(), + mtim: Default::default(), + ctim: Default::default(), + }; + stat.ino = node_id.ino(); + match self.storage.get_inode(&node_id) { + Node::File(body) => { + stat.filetype = wasi::FILETYPE_REGULAR_FILE; + stat.size = body.content().len() as u64; + stat + } + Node::Dir { .. } => { + stat.filetype = wasi::FILETYPE_DIRECTORY; + stat + } + } + } + + pub(crate) fn get_fd_entry_mut(&mut self, fd: Vfd) -> Result<&mut FdEntry, wasi::Errno> { + match self.opens.get_mut(&fd) { + Some(open_file) => Ok(open_file), + None => Err(wasi::ERRNO_BADF), + } + } + + pub(crate) fn get_fd_entry(&self, fd: Vfd) -> Result<&FdEntry, wasi::Errno> { + match self.opens.get(&fd) { + Some(open_file) => Ok(open_file), + None => Err(wasi::ERRNO_BADF), + } + } + + pub(crate) fn close_file(&mut self, fd: Vfd) -> Result<(), wasi::Errno> { + match self.opens.remove(&fd) { + Some(_) => Ok(()), + None => Err(wasi::ERRNO_BADF), + } + } + + pub(crate) fn open_file( + &mut self, + base: Vfd, + path: &Path, + fdflags: wasi::Fdflags, + ) -> Result { + let base = &self.opens[&base]; + let (node_id, link_id) = self + .storage + .resolve_node(base.node_id, base.link_id, path)?; + let new_fd = self.fd_issuer.issue(); + self.opens.insert( + new_fd, + FdEntry { + offset: 0, + node_id, + link_id, + flags: fdflags, + }, + ); + Ok(new_fd) + } + + pub(crate) fn get_filestat_at_path( + &self, + base: Vfd, + path: &Path, + ) -> Result { + let base = &self.opens[&base]; + let (node_id, _) = self + .storage + .resolve_node(base.node_id, base.link_id, path)?; + let res = self.get_filestat_from_node_id(node_id); + Ok(res) + } +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use super::{EmbeddedFs, LinkedStorage}; + + #[test] + fn test_embedded_node_create_file() { + let content = "Hello".as_bytes().to_vec(); + let mut fs = EmbeddedFs::::default(); + let (_, node_id, link_id) = fs.preopen_dir("/".to_string()); + fs.create_file(node_id, link_id, "hello.txt", content) + .unwrap(); + } + + #[test] + fn test_get_filestat_at_path_for_non_existing() { + let mut fs = EmbeddedFs::::default(); + let (vfd, _, _) = fs.preopen_dir("/".to_string()); + let result = fs.get_filestat_at_path(vfd, Path::new("/not-exist")); + assert!(result.is_err()); + } +} diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..fcbc701 --- /dev/null +++ b/src/init.c @@ -0,0 +1,18 @@ +// A dummy symbol to force the linker to include this file +// This symbol is used in lib.rs. +void __wasi_vfs_force_link_init(void) {} + +#pragma clang diagnostic ignored "-Wunknown-attributes" +__attribute__((export_name("wasi_vfs_pack_fs"))) +void export_wasi_vfs_pack_fs(void) { + extern void __internal_wasi_vfs_pack_fs(void); + __internal_wasi_vfs_pack_fs(); +} + +// wasi-libc reserves 50~100 constructor, and __wasilibc_populate_preopens calls +// fs syscall, so this need to be done before that. +__attribute__((constructor(40))) +void __wasi_vfs_rt_init(void) { + extern void __internal_wasi_vfs_rt_init(void); + __internal_wasi_vfs_rt_init(); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8a583f1 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,435 @@ +//! wasi-vfs is a virtual file system compatible with WASI. + +mod alloc; +mod embed; +mod trace; +mod trampoline_generated; +#[allow(unused_variables)] +mod wasi_snapshot_preview1; + +use embed::LinkedStorage as DefaultStorage; +use embed::{EmbeddedFs, NodeIdTrait, Storage}; + +use std::{collections::HashMap, ffi::CStr}; +use wasi::Fd; + +/// User-facing file descriptor managed by wasi-vfs +pub(crate) type UserFd = u32; +/// Internal file descriptor managed by virtual file system backend +pub(crate) type Vfd = u32; + +/// Generic internal file descriptor +#[derive(Clone, Copy, Debug)] +pub(crate) enum BackingFd { + /// File descriptor managed by virtual file system backend + Virtual(Vfd), + /// File descriptor managed by a real WASI implementation + Wasi(Fd), +} + +/// Map of user-facing file descriptors to internal file descriptors. +type FdMap = HashMap; + +struct FileSystem { + embedded_fs: EmbeddedFs, + fd_map: FdMap, + next_fd: u32, +} + +impl FileSystem { + fn create(embedded_fs: EmbeddedFs, preopened_vfds: &[Vfd]) -> Self { + let mut fs = FileSystem { + embedded_fs, + fd_map: FdMap::new(), + next_fd: 3, + }; + // reserve stdin/stdout/stderr + for fd in 0..=2 { + fs.set_user_fd_at(BackingFd::Wasi(fd), fd); + } + + for fd in 3.. { + unsafe { + match wasi::fd_prestat_get(fd) { + Ok(_) => (), + Err(wasi::ERRNO_BADF) => break, + Err(other) => { + panic!("failed to get prestat: {}", other); + } + } + } + + fs.issue_user_fd(BackingFd::Wasi(fd)); + } + for vfd in preopened_vfds { + let fd = fs.next_fd; + fs.next_fd += 1; + fs.fd_map.insert(fd, BackingFd::Virtual(*vfd)); + } + fs + } + + fn set_user_fd_at(&mut self, backing_fd: BackingFd, fd: UserFd) { + self.fd_map.insert(fd, backing_fd); + } + fn issue_user_fd(&mut self, backing_fd: BackingFd) -> UserFd { + let fd = self.next_fd; + self.next_fd += 1; + self.fd_map.insert(fd, backing_fd); + fd as UserFd + } + + fn get_backing_fd(&self, user_fd: UserFd) -> Result { + match self.fd_map.get(&user_fd) { + Some(backing_fd) => Ok(*backing_fd), + None => Err(wasi::ERRNO_BADF.into()), + } + } +} + +struct GlobalState { + embedded_fs: Option<(EmbeddedFs, Vec)>, + overlay_fs: Option>, +} + +static mut GLOBAL_STATE: GlobalState = GlobalState { + embedded_fs: None, + overlay_fs: None, +}; + +// `__internal_wasi_vfs_rt_init` is processed before the wasi-libc's initialization, which +// loads envirnoment variables and preopened directories. `getenv` is not available at this +// time, so use self-made `env_var` instead. +fn env_var(name: &str) -> Option { + let (count, buffer_size) = unsafe { wasi::environ_sizes_get().unwrap() }; + if count == 0 { + return None; + } + let mut offsets: Vec<*mut u8> = vec![std::ptr::null_mut(); count]; + let mut buffer: Vec = vec![0; buffer_size]; + unsafe { + wasi::environ_get(offsets.as_mut_ptr(), buffer.as_mut_ptr()).unwrap(); + } + for offset in offsets { + let c_str = unsafe { CStr::from_ptr(offset as *const i8) }; + let pair = c_str.to_string_lossy(); + let mut pair = pair.splitn(2, '='); + let key = pair.next().unwrap(); + let value = pair.next().unwrap(); + if key == name { + return Some(value.to_string()); + } + } + None +} + +#[no_mangle] +unsafe extern "C" fn __internal_wasi_vfs_rt_init() { + extern "C" { + fn __wasi_vfs_force_link_init(); + } + __wasi_vfs_force_link_init(); + if env_var("__WASI_VFS_PACKING").is_some() { + return; + } + if let Some((embedded_fs, preopened_vfds)) = GLOBAL_STATE.embedded_fs.take() { + let fs = FileSystem::create(embedded_fs, &preopened_vfds); + GLOBAL_STATE.overlay_fs = Some(fs); + } +} + +#[no_mangle] +unsafe extern "C" fn __internal_wasi_vfs_pack_fs() { + std::panic::set_hook(Box::new(|info| { + trace::print(format!("{}\n", info)); + })); + let (mut fs, preopened_vfds) = if let Some((fs, vfds)) = GLOBAL_STATE.embedded_fs.take() { + (fs, vfds) + } else { + (EmbeddedFs::default(), vec![]) + }; + + let (preopened_vfds, prestats) = + FsPacker::scan_preopened_dirs(&mut fs, preopened_vfds).unwrap(); + let packer = FsPacker::new(fs, preopened_vfds).unwrap(); + let fs = packer.pack(prestats).unwrap(); + GLOBAL_STATE.embedded_fs = Some(fs); + + #[cfg(not(feature = "module-linking"))] + { + extern "C" { + fn __wasilibc_deinitialize_environ(); + } + __wasilibc_deinitialize_environ(); + } +} + +struct Prestat { + real_fd: u32, + node_id: S::NodeId, + link_id: S::LinkId, +} + +struct FsPacker { + fs: EmbeddedFs, + preopened_vfds: Vec, + verbose: bool, +} + +trait DirVisitor { + fn visit_file( + &mut self, + path: &str, + fd: u32, + preopened_id: (S::NodeId, S::LinkId), + ) -> Result<(), u16>; + + fn visit_dir( + &mut self, + prefix: &str, + fd: u32, + preopened_id: (S::NodeId, S::LinkId), + ) -> Result<(), u16>; +} + +fn walk_dir>( + visitor: &mut V, + prefix: &str, + fd: u32, + preopened_id: (S::NodeId, S::LinkId), +) -> Result<(), u16> { + const DIRENT_DEFAULT_BUFFER_SIZE: usize = 4096; + let mut offset = 0; + let mut capacity = 0; + let mut cookie = wasi::DIRCOOKIE_START; + let mut buffer = vec![0; DIRENT_DEFAULT_BUFFER_SIZE]; + loop { + if offset == capacity { + capacity = unsafe { wasi::fd_readdir(fd, buffer.as_mut_ptr(), buffer.len(), cookie) } + .expect("failed to readdir"); + offset = 0; + if capacity == 0 { + break; + } + } + let data = &buffer[offset..capacity]; + let dirent_size = core::mem::size_of::(); + + // when dirent is truncated, re-read it + if data.len() < dirent_size { + offset = capacity; + continue; + } + + let (dirent, data) = data.split_at(dirent_size); + let dirent = unsafe { core::ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; + + // when entry name is truncated + if data.len() < dirent.d_namlen as usize { + // when the buffer is not enough to read an big entry, realloc the buffer and re-read the entry + if offset == 0 { + let amt_to_add = buffer.capacity(); + buffer.extend(core::iter::repeat(0).take(amt_to_add)); + } + offset = capacity; + continue; + } + cookie = dirent.d_next; + offset += dirent_size + dirent.d_namlen as usize; + let name = &data[..dirent.d_namlen as usize]; + if name == b"." || name == b".." { + continue; + } + + let name = String::from_utf8(name.to_vec()).unwrap(); + let path = format!("{}/{}", prefix, name); + let rights = wasi::RIGHTS_FD_READ + | wasi::RIGHTS_FD_READDIR + | wasi::RIGHTS_FD_FILESTAT_GET + | wasi::RIGHTS_PATH_OPEN; + + match dirent.d_type { + wasi::FILETYPE_DIRECTORY => { + let oflags = wasi::OFLAGS_DIRECTORY; + let child_fd = unsafe { + wasi::path_open( + fd, + wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, + &name, + oflags, + rights, + rights, + 0, + ) + } + .map_err(|e| e.raw()) + .unwrap(); + + visitor.visit_dir(&path, child_fd, preopened_id)?; + walk_dir(visitor, &path, child_fd, preopened_id)?; + + unsafe { + wasi::fd_close(child_fd).expect("failed to close fd"); + } + } + wasi::FILETYPE_REGULAR_FILE => { + let oflags = 0; + let child_fd = unsafe { + wasi::path_open( + fd, + wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, + &name, + oflags, + rights, + rights, + 0, + ) + } + .map_err(|e| e.raw()) + .unwrap(); + visitor.visit_file(&path, child_fd, preopened_id)?; + unsafe { + wasi::fd_close(child_fd).expect("failed to close fd"); + } + } + _ => {} + } + } + Ok(()) +} + +impl FsPacker { + fn scan_preopened_dirs( + fs: &mut EmbeddedFs, + mut preopened_vfds: Vec, + ) -> Result<(Vec, Vec>), u16> { + let mut preopened_dirs = Vec::new(); + 'scan: for fd in 3.. { + let stat = match unsafe { wasi::fd_prestat_get(fd) } { + Ok(stat) => stat, + Err(wasi::ERRNO_BADF) => break 'scan, + Err(other) => { + return Err(other.raw()); + } + }; + if stat.tag == wasi::PREOPENTYPE_DIR.raw() { + preopened_dirs.push((fd, stat)); + } + } + + let mut prestats = Vec::new(); + for (real_fd, stat) in preopened_dirs { + let dir = unsafe { + let mut prefix = vec![0; stat.u.dir.pr_name_len + 1]; + wasi::fd_prestat_dir_name(real_fd, prefix.as_mut_ptr(), stat.u.dir.pr_name_len) + .map_err(|e| e.raw()) + .expect("failed to get dir name"); + let dir = CStr::from_bytes_with_nul(&prefix).unwrap(); + dir.to_string_lossy().to_string() + }; + let (vfd, node_id, link_id) = fs.preopen_dir(dir); + prestats.push(Prestat { + real_fd, + node_id, + link_id, + }); + preopened_vfds.push(vfd); + } + Ok((preopened_vfds, prestats)) + } + + fn new(fs: EmbeddedFs, preopened_vfds: Vec) -> Result { + Ok(FsPacker { + fs, + preopened_vfds, + verbose: env_var("WASI_VFS_VERBOSE") + .map(|v| v == "1") + .unwrap_or(false), + }) + } + + fn pack(mut self, prestats: Vec>) -> Result<(EmbeddedFs, Vec), u16> { + for stat in prestats { + walk_dir(&mut self, "", stat.real_fd, (stat.node_id, stat.link_id))?; + } + Ok((self.fs, self.preopened_vfds)) + } +} + +impl DirVisitor for FsPacker { + fn visit_dir( + &mut self, + _prefix: &str, + _fd: u32, + _preopened_id: (S::NodeId, S::LinkId), + ) -> Result<(), u16> { + Ok(()) + } + + fn visit_file( + &mut self, + path: &str, + fd: u32, + preopened_id: (S::NodeId, S::LinkId), + ) -> Result<(), u16> { + let stat = unsafe { wasi::fd_filestat_get(fd) } + .map_err(|e| e.raw()) + .unwrap(); + if stat.size >= u32::MAX as u64 { + // ignore too big files + if self.verbose { + trace::print(format!("too large file: {} (size {})\n", path, stat.size)); + } + return Ok(()); + } + let mut buf = vec![0; stat.size as usize]; + let mut offset = 0; + loop { + let read = unsafe { + wasi::fd_read( + fd, + &[wasi::Iovec { + buf: buf[offset..].as_mut_ptr(), + buf_len: buf.len(), + }], + ) + } + .map_err(|e| e.raw()) + .unwrap(); + + offset += read; + if offset == stat.size as usize { + break; + } + } + if self.verbose { + trace::print(format!( + "pack file: {} under node-id={} (size {})\n", + path, + preopened_id.0.ino(), + buf.len() + )); + } + self.fs + .create_file(preopened_id.0, preopened_id.1, path, buf) + .unwrap(); + Ok(()) + } +} + +#[derive(Copy, Clone, Hash, Debug)] +pub(crate) struct Error(u16); + +impl From for Error { + #[inline] + fn from(from: wasi::Errno) -> Self { + Self(from.raw()) + } +} + +impl Error { + #[inline] + pub(crate) fn raw(self) -> u16 { + self.0 + } +} diff --git a/src/trace.rs b/src/trace.rs new file mode 100644 index 0000000..69beb0a --- /dev/null +++ b/src/trace.rs @@ -0,0 +1,51 @@ +#[cfg(feature = "trace-syscall")] +fn is_tracing_enabled() -> bool { + extern "C" { + fn getenv(name: *const i8) -> *const i8; + } + let wasi_vfs_trace = std::ffi::CString::new("WASI_VFS_TRACE").unwrap(); + unsafe { !getenv(wasi_vfs_trace.as_ptr()).is_null() } +} + +#[cfg(feature = "trace-syscall")] +pub(crate) fn trace_syscall_entry(args: std::fmt::Arguments<'_>) { + if !is_tracing_enabled() { + return; + } + let message = std::fmt::format(args); + let data = [wasi::Ciovec { + buf: message.as_ptr(), + buf_len: message.len(), + }]; + let stderr = 2; + unsafe { + wasi::fd_write(stderr, &data).unwrap(); + } +} + +#[cfg(feature = "trace-syscall")] +pub(crate) fn trace_syscall_error(name: &str, errno: crate::Error) { + if !is_tracing_enabled() { + return; + } + let message = format!("{} returns {}\n", name, errno.raw()); + let data = [wasi::Ciovec { + buf: message.as_ptr(), + buf_len: message.len(), + }]; + let stderr = 2; + unsafe { + wasi::fd_write(stderr, &data).unwrap(); + } +} + +pub(crate) fn print(message: String) { + let data = [wasi::Ciovec { + buf: message.as_ptr(), + buf_len: message.len(), + }]; + let stdout = 1; + unsafe { + wasi::fd_write(stdout, &data).unwrap(); + } +} diff --git a/src/trampoline_generated.c b/src/trampoline_generated.c new file mode 100644 index 0000000..0612530 --- /dev/null +++ b/src/trampoline_generated.c @@ -0,0 +1,199 @@ +// This file is automatically generated, DO NOT EDIT +// +// To regenerate this file run the `crates/wasi-libc-trampoline-bindgen` command + +#include + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_advise(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_advise(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3); + return wasi_vfs_fd_advise(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_allocate(int32_t arg0, int64_t arg1, int64_t arg2) { + extern int32_t wasi_vfs_fd_allocate(int32_t arg0, int64_t arg1, int64_t arg2); + return wasi_vfs_fd_allocate(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_close(int32_t arg0) { + extern int32_t wasi_vfs_fd_close(int32_t arg0); + return wasi_vfs_fd_close(arg0); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_datasync(int32_t arg0) { + extern int32_t wasi_vfs_fd_datasync(int32_t arg0); + return wasi_vfs_fd_datasync(arg0); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_fdstat_get(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_fdstat_get(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_fdstat_get(arg0, arg1); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_fdstat_set_flags(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_fdstat_set_flags(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_fdstat_set_flags(arg0, arg1); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_fdstat_set_rights(int32_t arg0, int64_t arg1, int64_t arg2) { + extern int32_t wasi_vfs_fd_fdstat_set_rights(int32_t arg0, int64_t arg1, int64_t arg2); + return wasi_vfs_fd_fdstat_set_rights(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_filestat_get(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_filestat_get(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_filestat_get(arg0, arg1); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_filestat_set_size(int32_t arg0, int64_t arg1) { + extern int32_t wasi_vfs_fd_filestat_set_size(int32_t arg0, int64_t arg1); + return wasi_vfs_fd_filestat_set_size(arg0, arg1); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_filestat_set_times(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_filestat_set_times(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3); + return wasi_vfs_fd_filestat_set_times(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_pread(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_fd_pread(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4); + return wasi_vfs_fd_pread(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_prestat_get(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_prestat_get(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_prestat_get(arg0, arg1); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_prestat_dir_name(int32_t arg0, int32_t arg1, int32_t arg2) { + extern int32_t wasi_vfs_fd_prestat_dir_name(int32_t arg0, int32_t arg1, int32_t arg2); + return wasi_vfs_fd_prestat_dir_name(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_pwrite(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_fd_pwrite(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4); + return wasi_vfs_fd_pwrite(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_read(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_read(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3); + return wasi_vfs_fd_read(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_readdir(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_fd_readdir(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4); + return wasi_vfs_fd_readdir(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_renumber(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_renumber(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_renumber(arg0, arg1); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_seek(int32_t arg0, int64_t arg1, int32_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_seek(int32_t arg0, int64_t arg1, int32_t arg2, int32_t arg3); + return wasi_vfs_fd_seek(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_sync(int32_t arg0) { + extern int32_t wasi_vfs_fd_sync(int32_t arg0); + return wasi_vfs_fd_sync(arg0); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_tell(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_tell(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_tell(arg0, arg1); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_fd_write(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_write(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3); + return wasi_vfs_fd_write(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_create_directory(int32_t arg0, int32_t arg1, int32_t arg2) { + extern int32_t wasi_vfs_path_create_directory(int32_t arg0, int32_t arg1, int32_t arg2); + return wasi_vfs_path_create_directory(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_filestat_get(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_path_filestat_get(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4); + return wasi_vfs_path_filestat_get(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_filestat_set_times(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int64_t arg4, int64_t arg5, int32_t arg6) { + extern int32_t wasi_vfs_path_filestat_set_times(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int64_t arg4, int64_t arg5, int32_t arg6); + return wasi_vfs_path_filestat_set_times(arg0, arg1, arg2, arg3, arg4, arg5, arg6); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_link(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6) { + extern int32_t wasi_vfs_path_link(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6); + return wasi_vfs_path_link(arg0, arg1, arg2, arg3, arg4, arg5, arg6); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_open(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int64_t arg5, int64_t arg6, int32_t arg7, int32_t arg8) { + extern int32_t wasi_vfs_path_open(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int64_t arg5, int64_t arg6, int32_t arg7, int32_t arg8); + return wasi_vfs_path_open(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_readlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) { + extern int32_t wasi_vfs_path_readlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5); + return wasi_vfs_path_readlink(arg0, arg1, arg2, arg3, arg4, arg5); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_remove_directory(int32_t arg0, int32_t arg1, int32_t arg2) { + extern int32_t wasi_vfs_path_remove_directory(int32_t arg0, int32_t arg1, int32_t arg2); + return wasi_vfs_path_remove_directory(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_rename(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) { + extern int32_t wasi_vfs_path_rename(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5); + return wasi_vfs_path_rename(arg0, arg1, arg2, arg3, arg4, arg5); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_symlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_path_symlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4); + return wasi_vfs_path_symlink(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_path_unlink_file(int32_t arg0, int32_t arg1, int32_t arg2) { + extern int32_t wasi_vfs_path_unlink_file(int32_t arg0, int32_t arg1, int32_t arg2); + return wasi_vfs_path_unlink_file(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __imported_wasi_snapshot_preview1_poll_oneoff(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_poll_oneoff(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3); + return wasi_vfs_poll_oneoff(arg0, arg1, arg2, arg3); +} + + diff --git a/src/trampoline_generated.rs b/src/trampoline_generated.rs new file mode 100644 index 0000000..91f2f10 --- /dev/null +++ b/src/trampoline_generated.rs @@ -0,0 +1,1005 @@ +// This file is automatically generated, DO NOT EDIT +// +// To regenerate this file run the `crates/wasi-libc-trampoline-bindgen` command +#![allow(unused_variables)] +use crate::UserFd; +use wasi::*; + +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_advise(arg0: i32, arg1: i64, arg2: i64, arg3: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_advise(fd: {}, offset: {}, len: {}, advice: {})\n", + arg0, arg1, arg2, arg3 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_advise(arg0, arg1, arg2, arg3); + }; + { + match crate::wasi_snapshot_preview1::fd_advise( + fs, + arg0 as UserFd, + arg1 as u64, + arg2 as u64, + arg3, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_advise", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_allocate(arg0: i32, arg1: i64, arg2: i64) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_allocate(fd: {}, offset: {}, len: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_allocate(arg0, arg1, arg2); + }; + { + match crate::wasi_snapshot_preview1::fd_allocate( + fs, + arg0 as UserFd, + arg1 as u64, + arg2 as u64, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_allocate", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_close(arg0: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!("fd_close(fd: {})\n", arg0)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_close(arg0); + }; + { + match crate::wasi_snapshot_preview1::fd_close(fs, arg0 as UserFd) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_close", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_datasync(arg0: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!("fd_datasync(fd: {})\n", arg0)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_datasync(arg0); + }; + { + match crate::wasi_snapshot_preview1::fd_datasync(fs, arg0 as UserFd) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_datasync", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_fdstat_get(arg0: i32, arg1: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!("fd_fdstat_get(fd: {})\n", arg0)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_fdstat_get(arg0, arg1); + }; + { + match crate::wasi_snapshot_preview1::fd_fdstat_get(fs, arg0 as UserFd) { + Ok(e) => { + core::ptr::write(arg1 as *mut Fdstat, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_fdstat_get", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_fdstat_set_flags(arg0: i32, arg1: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_fdstat_set_flags(fd: {}, flags: {})\n", + arg0, arg1 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_fdstat_set_flags(arg0, arg1); + }; + { + match crate::wasi_snapshot_preview1::fd_fdstat_set_flags( + fs, + arg0 as UserFd, + arg1 as Fdflags, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_fdstat_set_flags", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_fdstat_set_rights(arg0: i32, arg1: i64, arg2: i64) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_fdstat_set_rights(fd: {}, fs_rights_base: {}, fs_rights_inheriting: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_fdstat_set_rights(arg0, arg1, arg2); + }; + { + match crate::wasi_snapshot_preview1::fd_fdstat_set_rights( + fs, + arg0 as UserFd, + arg1 as Rights, + arg2 as Rights, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_fdstat_set_rights", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_filestat_get(arg0: i32, arg1: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!("fd_filestat_get(fd: {})\n", arg0)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_filestat_get(arg0, arg1); + }; + { + match crate::wasi_snapshot_preview1::fd_filestat_get(fs, arg0 as UserFd) { + Ok(e) => { + core::ptr::write(arg1 as *mut Filestat, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_filestat_get", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_filestat_set_size(arg0: i32, arg1: i64) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_filestat_set_size(fd: {}, size: {})\n", + arg0, arg1 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_filestat_set_size(arg0, arg1); + }; + { + match crate::wasi_snapshot_preview1::fd_filestat_set_size(fs, arg0 as UserFd, arg1 as u64) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_filestat_set_size", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_filestat_set_times( + arg0: i32, + arg1: i64, + arg2: i64, + arg3: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_filestat_set_times(fd: {}, atim: {}, mtim: {}, fst_flags: {})\n", + arg0, arg1, arg2, arg3 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_filestat_set_times(arg0, arg1, arg2, arg3); + }; + { + match crate::wasi_snapshot_preview1::fd_filestat_set_times( + fs, + arg0 as UserFd, + arg1 as u64, + arg2 as u64, + arg3 as Fstflags, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_filestat_set_times", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_pread( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i64, + arg4: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_pread(fd: {}, iovs: {}, iovs_len: {}, offset: {})\n", + arg0, arg1, arg2, arg3 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_pread(arg0, arg1, arg2, arg3, arg4); + }; + { + match crate::wasi_snapshot_preview1::fd_pread( + fs, + arg0 as UserFd, + core::slice::from_raw_parts(arg1 as *const Iovec, arg2 as usize), + arg3 as u64, + ) { + Ok(e) => { + core::ptr::write(arg4 as *mut Size, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_pread", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_prestat_get(arg0: i32, arg1: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!("fd_prestat_get(fd: {})\n", arg0)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_prestat_get(arg0, arg1); + }; + { + match crate::wasi_snapshot_preview1::fd_prestat_get(fs, arg0 as UserFd) { + Ok(e) => { + core::ptr::write(arg1 as *mut Prestat, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_prestat_get", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_prestat_dir_name(arg0: i32, arg1: i32, arg2: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_prestat_dir_name(fd: {}, path: {}, path_len: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_prestat_dir_name(arg0, arg1, arg2); + }; + { + match crate::wasi_snapshot_preview1::fd_prestat_dir_name( + fs, + arg0 as UserFd, + arg1 as *mut u8, + arg2 as u32, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_prestat_dir_name", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_pwrite( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i64, + arg4: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_pwrite(fd: {}, iovs: {}, iovs_len: {}, offset: {})\n", + arg0, arg1, arg2, arg3 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_pwrite(arg0, arg1, arg2, arg3, arg4); + }; + { + match crate::wasi_snapshot_preview1::fd_pwrite( + fs, + arg0 as UserFd, + core::slice::from_raw_parts(arg1 as *const Ciovec, arg2 as usize), + arg3 as u64, + ) { + Ok(e) => { + core::ptr::write(arg4 as *mut Size, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_pwrite", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_read(arg0: i32, arg1: i32, arg2: i32, arg3: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_read(fd: {}, iovs: {}, iovs_len: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_read(arg0, arg1, arg2, arg3); + }; + { + match crate::wasi_snapshot_preview1::fd_read( + fs, + arg0 as UserFd, + core::slice::from_raw_parts(arg1 as *const Iovec, arg2 as usize), + ) { + Ok(e) => { + core::ptr::write(arg3 as *mut Size, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_read", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_readdir( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i64, + arg4: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_readdir(fd: {}, buf: {}, buf_len: {}, cookie: {})\n", + arg0, arg1, arg2, arg3 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_readdir(arg0, arg1, arg2, arg3, arg4); + }; + { + match crate::wasi_snapshot_preview1::fd_readdir( + fs, + arg0 as UserFd, + arg1 as *mut u8, + arg2 as u32, + arg3 as u64, + ) { + Ok(e) => { + core::ptr::write(arg4 as *mut Size, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_readdir", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_renumber(arg0: i32, arg1: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!("fd_renumber(fd: {}, to: {})\n", arg0, arg1)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_renumber(arg0, arg1); + }; + { + match crate::wasi_snapshot_preview1::fd_renumber(fs, arg0 as UserFd, arg1 as UserFd) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_renumber", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_seek(arg0: i32, arg1: i64, arg2: i32, arg3: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_seek(fd: {}, offset: {}, whence: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_seek(arg0, arg1, arg2, arg3); + }; + { + match crate::wasi_snapshot_preview1::fd_seek(fs, arg0 as UserFd, arg1 as i64, arg2) { + Ok(e) => { + core::ptr::write(arg3 as *mut Filesize, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_seek", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_sync(arg0: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!("fd_sync(fd: {})\n", arg0)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_sync(arg0); + }; + { + match crate::wasi_snapshot_preview1::fd_sync(fs, arg0 as UserFd) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_sync", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_tell(arg0: i32, arg1: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!("fd_tell(fd: {})\n", arg0)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_tell(arg0, arg1); + }; + { + match crate::wasi_snapshot_preview1::fd_tell(fs, arg0 as UserFd) { + Ok(e) => { + core::ptr::write(arg1 as *mut Filesize, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_tell", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_fd_write(arg0: i32, arg1: i32, arg2: i32, arg3: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "fd_write(fd: {}, iovs: {}, iovs_len: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::fd_write(arg0, arg1, arg2, arg3); + }; + { + match crate::wasi_snapshot_preview1::fd_write( + fs, + arg0 as UserFd, + core::slice::from_raw_parts(arg1 as *const Ciovec, arg2 as usize), + ) { + Ok(e) => { + core::ptr::write(arg3 as *mut Size, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("fd_write", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_create_directory(arg0: i32, arg1: i32, arg2: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "path_create_directory(fd: {}, path: {}, path_len: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_create_directory(arg0, arg1, arg2); + }; + { + match crate::wasi_snapshot_preview1::path_create_directory(fs, arg0 as UserFd, { + let str_bytes = core::slice::from_raw_parts(arg1 as *const u8, (arg2 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_create_directory", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_filestat_get( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i32, + arg4: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "path_filestat_get(fd: {}, flags: {}, path: {}, path_len: {})\n", + arg0, arg1, arg2, arg3 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_filestat_get(arg0, arg1, arg2, arg3, arg4); + }; + { + match crate::wasi_snapshot_preview1::path_filestat_get( + fs, + arg0 as UserFd, + arg1 as Lookupflags, + { + let str_bytes = core::slice::from_raw_parts(arg2 as *const u8, (arg3 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + ) { + Ok(e) => { + core::ptr::write(arg4 as *mut Filestat, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_filestat_get", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_filestat_set_times( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i32, + arg4: i64, + arg5: i64, + arg6: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] +crate::trace::trace_syscall_entry(format_args!("path_filestat_set_times(fd: {}, flags: {}, path: {}, path_len: {}, atim: {}, mtim: {}, fst_flags: {})\n", arg0, arg1, arg2, arg3, arg4, arg5, arg6)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_filestat_set_times( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, + ); + }; + { + match crate::wasi_snapshot_preview1::path_filestat_set_times( + fs, + arg0 as UserFd, + arg1 as Lookupflags, + { + let str_bytes = core::slice::from_raw_parts(arg2 as *const u8, (arg3 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + arg4 as u64, + arg5 as u64, + arg6 as Fstflags, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_filestat_set_times", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_link( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i32, + arg4: i32, + arg5: i32, + arg6: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] +crate::trace::trace_syscall_entry(format_args!("path_link(old_fd: {}, old_flags: {}, old_path: {}, old_path_len: {}, new_fd: {}, new_path: {}, new_path_len: {})\n", arg0, arg1, arg2, arg3, arg4, arg5, arg6)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_link(arg0, arg1, arg2, arg3, arg4, arg5, arg6); + }; + { + match crate::wasi_snapshot_preview1::path_link( + fs, + arg0 as UserFd, + arg1 as Lookupflags, + { + let str_bytes = core::slice::from_raw_parts(arg2 as *const u8, (arg3 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + arg4 as UserFd, + { + let str_bytes = core::slice::from_raw_parts(arg5 as *const u8, (arg6 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_link", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_open( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i32, + arg4: i32, + arg5: i64, + arg6: i64, + arg7: i32, + arg8: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] +crate::trace::trace_syscall_entry(format_args!("path_open(fd: {}, dirflags: {}, path: {}, path_len: {}, oflags: {}, fs_rights_base: {}, fs_rights_inheriting: {}, fdflags: {})\n", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_open( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + ); + }; + { + match crate::wasi_snapshot_preview1::path_open( + fs, + arg0 as UserFd, + arg1 as Lookupflags, + { + let str_bytes = core::slice::from_raw_parts(arg2 as *const u8, (arg3 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + arg4 as Oflags, + arg5 as Rights, + arg6 as Rights, + arg7 as Fdflags, + ) { + Ok(e) => { + core::ptr::write(arg8 as *mut Fd, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_open", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_readlink( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i32, + arg4: i32, + arg5: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "path_readlink(fd: {}, path: {}, path_len: {}, buf: {}, buf_len: {})\n", + arg0, arg1, arg2, arg3, arg4 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_readlink(arg0, arg1, arg2, arg3, arg4, arg5); + }; + { + match crate::wasi_snapshot_preview1::path_readlink( + fs, + arg0 as UserFd, + { + let str_bytes = core::slice::from_raw_parts(arg1 as *const u8, (arg2 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + arg3 as *mut u8, + arg4 as u32, + ) { + Ok(e) => { + core::ptr::write(arg5 as *mut Size, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_readlink", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_remove_directory(arg0: i32, arg1: i32, arg2: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "path_remove_directory(fd: {}, path: {}, path_len: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_remove_directory(arg0, arg1, arg2); + }; + { + match crate::wasi_snapshot_preview1::path_remove_directory(fs, arg0 as UserFd, { + let str_bytes = core::slice::from_raw_parts(arg1 as *const u8, (arg2 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_remove_directory", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_rename( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i32, + arg4: i32, + arg5: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] +crate::trace::trace_syscall_entry(format_args!("path_rename(fd: {}, old_path: {}, old_path_len: {}, new_fd: {}, new_path: {}, new_path_len: {})\n", arg0, arg1, arg2, arg3, arg4, arg5)); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_rename(arg0, arg1, arg2, arg3, arg4, arg5); + }; + { + match crate::wasi_snapshot_preview1::path_rename( + fs, + arg0 as UserFd, + { + let str_bytes = core::slice::from_raw_parts(arg1 as *const u8, (arg2 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + arg3 as UserFd, + { + let str_bytes = core::slice::from_raw_parts(arg4 as *const u8, (arg5 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_rename", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_symlink( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i32, + arg4: i32, +) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "path_symlink(old_path: {}, old_path_len: {}, fd: {}, new_path: {}, new_path_len: {})\n", + arg0, arg1, arg2, arg3, arg4 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_symlink(arg0, arg1, arg2, arg3, arg4); + }; + { + match crate::wasi_snapshot_preview1::path_symlink( + fs, + { + let str_bytes = core::slice::from_raw_parts(arg0 as *const u8, (arg1 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + arg2 as UserFd, + { + let str_bytes = core::slice::from_raw_parts(arg3 as *const u8, (arg4 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }, + ) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_symlink", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_path_unlink_file(arg0: i32, arg1: i32, arg2: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "path_unlink_file(fd: {}, path: {}, path_len: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::path_unlink_file(arg0, arg1, arg2); + }; + { + match crate::wasi_snapshot_preview1::path_unlink_file(fs, arg0 as UserFd, { + let str_bytes = core::slice::from_raw_parts(arg1 as *const u8, (arg2 + 1) as usize); + std::ffi::CStr::from_bytes_with_nul_unchecked(str_bytes) + }) { + Ok(e) => wasi::ERRNO_SUCCESS.raw() as i32, + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("path_unlink_file", e.clone()); + + e.raw() as i32 + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn wasi_vfs_poll_oneoff(arg0: i32, arg1: i32, arg2: i32, arg3: i32) -> i32 { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_entry(format_args!( + "poll_oneoff(in: {}, out: {}, nsubscriptions: {})\n", + arg0, arg1, arg2 + )); + let fs = if let Some(fs) = crate::GLOBAL_STATE.overlay_fs.as_mut() { + fs + } else { + return wasi::wasi_snapshot_preview1::poll_oneoff(arg0, arg1, arg2, arg3); + }; + { + match crate::wasi_snapshot_preview1::poll_oneoff( + fs, + arg0 as *const Subscription, + arg1 as *mut Event, + arg2 as u32, + ) { + Ok(e) => { + core::ptr::write(arg3 as *mut Size, e); + wasi::ERRNO_SUCCESS.raw() as i32 + } + Err(e) => { + #[cfg(feature = "trace-syscall")] + crate::trace::trace_syscall_error("poll_oneoff", e.clone()); + + e.raw() as i32 + } + } + } +} diff --git a/src/trampoline_generated_legacy_wasi_libc.c b/src/trampoline_generated_legacy_wasi_libc.c new file mode 100644 index 0000000..099d799 --- /dev/null +++ b/src/trampoline_generated_legacy_wasi_libc.c @@ -0,0 +1,199 @@ +// This file is automatically generated, DO NOT EDIT +// +// To regenerate this file run the `crates/wasi-libc-trampoline-bindgen` command + +#include + +__attribute__((weak)) +int32_t __wasi_fd_advise(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_advise(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3); + return wasi_vfs_fd_advise(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __wasi_fd_allocate(int32_t arg0, int64_t arg1, int64_t arg2) { + extern int32_t wasi_vfs_fd_allocate(int32_t arg0, int64_t arg1, int64_t arg2); + return wasi_vfs_fd_allocate(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __wasi_fd_close(int32_t arg0) { + extern int32_t wasi_vfs_fd_close(int32_t arg0); + return wasi_vfs_fd_close(arg0); +} + +__attribute__((weak)) +int32_t __wasi_fd_datasync(int32_t arg0) { + extern int32_t wasi_vfs_fd_datasync(int32_t arg0); + return wasi_vfs_fd_datasync(arg0); +} + +__attribute__((weak)) +int32_t __wasi_fd_fdstat_get(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_fdstat_get(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_fdstat_get(arg0, arg1); +} + +__attribute__((weak)) +int32_t __wasi_fd_fdstat_set_flags(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_fdstat_set_flags(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_fdstat_set_flags(arg0, arg1); +} + +__attribute__((weak)) +int32_t __wasi_fd_fdstat_set_rights(int32_t arg0, int64_t arg1, int64_t arg2) { + extern int32_t wasi_vfs_fd_fdstat_set_rights(int32_t arg0, int64_t arg1, int64_t arg2); + return wasi_vfs_fd_fdstat_set_rights(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __wasi_fd_filestat_get(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_filestat_get(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_filestat_get(arg0, arg1); +} + +__attribute__((weak)) +int32_t __wasi_fd_filestat_set_size(int32_t arg0, int64_t arg1) { + extern int32_t wasi_vfs_fd_filestat_set_size(int32_t arg0, int64_t arg1); + return wasi_vfs_fd_filestat_set_size(arg0, arg1); +} + +__attribute__((weak)) +int32_t __wasi_fd_filestat_set_times(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_filestat_set_times(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3); + return wasi_vfs_fd_filestat_set_times(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __wasi_fd_pread(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_fd_pread(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4); + return wasi_vfs_fd_pread(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __wasi_fd_prestat_get(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_prestat_get(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_prestat_get(arg0, arg1); +} + +__attribute__((weak)) +int32_t __wasi_fd_prestat_dir_name(int32_t arg0, int32_t arg1, int32_t arg2) { + extern int32_t wasi_vfs_fd_prestat_dir_name(int32_t arg0, int32_t arg1, int32_t arg2); + return wasi_vfs_fd_prestat_dir_name(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __wasi_fd_pwrite(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_fd_pwrite(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4); + return wasi_vfs_fd_pwrite(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __wasi_fd_read(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_read(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3); + return wasi_vfs_fd_read(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __wasi_fd_readdir(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_fd_readdir(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4); + return wasi_vfs_fd_readdir(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __wasi_fd_renumber(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_renumber(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_renumber(arg0, arg1); +} + +__attribute__((weak)) +int32_t __wasi_fd_seek(int32_t arg0, int64_t arg1, int32_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_seek(int32_t arg0, int64_t arg1, int32_t arg2, int32_t arg3); + return wasi_vfs_fd_seek(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __wasi_fd_sync(int32_t arg0) { + extern int32_t wasi_vfs_fd_sync(int32_t arg0); + return wasi_vfs_fd_sync(arg0); +} + +__attribute__((weak)) +int32_t __wasi_fd_tell(int32_t arg0, int32_t arg1) { + extern int32_t wasi_vfs_fd_tell(int32_t arg0, int32_t arg1); + return wasi_vfs_fd_tell(arg0, arg1); +} + +__attribute__((weak)) +int32_t __wasi_fd_write(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_fd_write(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3); + return wasi_vfs_fd_write(arg0, arg1, arg2, arg3); +} + +__attribute__((weak)) +int32_t __wasi_path_create_directory(int32_t arg0, int32_t arg1, int32_t arg2) { + extern int32_t wasi_vfs_path_create_directory(int32_t arg0, int32_t arg1, int32_t arg2); + return wasi_vfs_path_create_directory(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __wasi_path_filestat_get(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_path_filestat_get(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4); + return wasi_vfs_path_filestat_get(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __wasi_path_filestat_set_times(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int64_t arg4, int64_t arg5, int32_t arg6) { + extern int32_t wasi_vfs_path_filestat_set_times(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int64_t arg4, int64_t arg5, int32_t arg6); + return wasi_vfs_path_filestat_set_times(arg0, arg1, arg2, arg3, arg4, arg5, arg6); +} + +__attribute__((weak)) +int32_t __wasi_path_link(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6) { + extern int32_t wasi_vfs_path_link(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6); + return wasi_vfs_path_link(arg0, arg1, arg2, arg3, arg4, arg5, arg6); +} + +__attribute__((weak)) +int32_t __wasi_path_open(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int64_t arg5, int64_t arg6, int32_t arg7, int32_t arg8) { + extern int32_t wasi_vfs_path_open(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int64_t arg5, int64_t arg6, int32_t arg7, int32_t arg8); + return wasi_vfs_path_open(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); +} + +__attribute__((weak)) +int32_t __wasi_path_readlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) { + extern int32_t wasi_vfs_path_readlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5); + return wasi_vfs_path_readlink(arg0, arg1, arg2, arg3, arg4, arg5); +} + +__attribute__((weak)) +int32_t __wasi_path_remove_directory(int32_t arg0, int32_t arg1, int32_t arg2) { + extern int32_t wasi_vfs_path_remove_directory(int32_t arg0, int32_t arg1, int32_t arg2); + return wasi_vfs_path_remove_directory(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __wasi_path_rename(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) { + extern int32_t wasi_vfs_path_rename(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5); + return wasi_vfs_path_rename(arg0, arg1, arg2, arg3, arg4, arg5); +} + +__attribute__((weak)) +int32_t __wasi_path_symlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) { + extern int32_t wasi_vfs_path_symlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4); + return wasi_vfs_path_symlink(arg0, arg1, arg2, arg3, arg4); +} + +__attribute__((weak)) +int32_t __wasi_path_unlink_file(int32_t arg0, int32_t arg1, int32_t arg2) { + extern int32_t wasi_vfs_path_unlink_file(int32_t arg0, int32_t arg1, int32_t arg2); + return wasi_vfs_path_unlink_file(arg0, arg1, arg2); +} + +__attribute__((weak)) +int32_t __wasi_poll_oneoff(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) { + extern int32_t wasi_vfs_poll_oneoff(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3); + return wasi_vfs_poll_oneoff(arg0, arg1, arg2, arg3); +} + + diff --git a/src/wasi_snapshot_preview1.rs b/src/wasi_snapshot_preview1.rs new file mode 100644 index 0000000..102218a --- /dev/null +++ b/src/wasi_snapshot_preview1.rs @@ -0,0 +1,904 @@ +use core::slice; +use std::{ + ffi::{CStr, OsStr}, + mem::MaybeUninit, + path::Path, +}; +use wasi::{ + CiovecArray, Dircookie, Event, Fd, Fdflags, Fdstat, Filedelta, Filesize, Filestat, Fstflags, + IovecArray, Lookupflags, Oflags, Prestat, PrestatDir, PrestatU, Rights, Size, Subscription, + Timestamp, RIGHTS_FD_ADVISE, RIGHTS_FD_FILESTAT_GET, RIGHTS_FD_READ, RIGHTS_FD_READDIR, + RIGHTS_PATH_OPEN, +}; + +use crate::{ + embed::{Node, NodeDirBody, NodeFileBody, NodeIdTrait, Storage}, + BackingFd, Error, FileSystem, UserFd, +}; + +pub(crate) unsafe fn fd_advise( + fs: &mut FileSystem, + fd: UserFd, + offset: Filesize, + len: Filesize, + advice: i32, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::fd_advise( + fd as i32, + offset as i64, + len as i64, + advice as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_allocate( + fs: &mut FileSystem, + fd: UserFd, + offset: Filesize, + len: Filesize, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = + wasi::wasi_snapshot_preview1::fd_allocate(fd as i32, offset as i64, len as i64); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_close(fs: &mut FileSystem, fd: UserFd) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => Ok(fs.embedded_fs.close_file(vfd)?), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::fd_close(fd as i32); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_datasync( + fs: &mut FileSystem, + fd: UserFd, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::fd_datasync(fd as i32); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_fdstat_get( + fs: &mut FileSystem, + fd: UserFd, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + let ro_rights = RIGHTS_FD_READ + | RIGHTS_FD_ADVISE + | RIGHTS_PATH_OPEN + | RIGHTS_FD_READDIR + | RIGHTS_FD_FILESTAT_GET; + match fd { + BackingFd::Virtual(vfd) => { + let stat = fs.embedded_fs.get_fd_stat(vfd)?; + Ok(stat) + } + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = + wasi::wasi_snapshot_preview1::fd_fdstat_get(fd as i32, rp0.as_mut_ptr() as i32); + match ret { + 0 => Ok(rp0.assume_init()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_fdstat_set_flags( + fs: &mut FileSystem, + fd: UserFd, + flags: Fdflags, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => { + let entry = fs.embedded_fs.get_fd_entry_mut(vfd)?; + entry.flags = flags; + Ok(()) + } + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::fd_fdstat_set_flags(fd as i32, flags as i32); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_fdstat_set_rights( + fs: &mut FileSystem, + fd: UserFd, + fs_rights_base: Rights, + fs_rights_inheriting: Rights, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::fd_fdstat_set_rights( + fd as i32, + fs_rights_base as i64, + fs_rights_inheriting as i64, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_filestat_get( + fs: &mut FileSystem, + fd: UserFd, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => { + let fd_entry = fs.embedded_fs.get_fd_entry(vfd)?; + Ok(fs.embedded_fs.get_filestat_from_node_id(fd_entry.node_id)) + } + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = + wasi::wasi_snapshot_preview1::fd_filestat_get(fd as i32, rp0.as_mut_ptr() as i32); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Filestat)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_filestat_set_size( + fs: &mut FileSystem, + fd: UserFd, + size: Filesize, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::fd_filestat_set_size(fd as i32, size as i64); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_filestat_set_times( + fs: &mut FileSystem, + fd: UserFd, + atim: Timestamp, + mtim: Timestamp, + fst_flags: Fstflags, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::fd_filestat_set_times( + fd as i32, + atim as i64, + mtim as i64, + fst_flags as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_pread( + fs: &mut FileSystem, + fd: UserFd, + iovs: IovecArray<'_>, + offset: Filesize, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::fd_pread( + fd as i32, + iovs.as_ptr() as i32, + iovs.len() as i32, + offset as i64, + rp0.as_mut_ptr() as i32, + ); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_prestat_get( + fs: &mut FileSystem, + fd: UserFd, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => { + if let Some(dir) = fs.embedded_fs.get_preopened_dir_path(vfd) { + let stat = Prestat { + tag: wasi::PREOPENTYPE_DIR.raw(), + u: PrestatU { + dir: PrestatDir { + pr_name_len: dir.as_bytes().len(), + }, + }, + }; + Ok(stat) + } else { + Err(wasi::ERRNO_BADF.into()) + } + } + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = + wasi::wasi_snapshot_preview1::fd_prestat_get(fd as i32, rp0.as_mut_ptr() as i32); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Prestat)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_prestat_dir_name( + fs: &mut FileSystem, + fd: UserFd, + path: *mut u8, + path_len: u32, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => { + if let Some(dir) = fs.embedded_fs.get_preopened_dir_path(vfd) { + let path = slice::from_raw_parts_mut(path, path_len as usize); + for (offset, byte) in dir.as_bytes().iter().enumerate() { + path[offset] = *byte; + } + Ok(()) + } else { + Err(wasi::ERRNO_BADF.into()) + } + } + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::fd_prestat_dir_name( + fd as i32, + path as i32, + path_len as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_pwrite( + fs: &mut FileSystem, + fd: UserFd, + iovs: CiovecArray<'_>, + offset: Filesize, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::fd_pwrite( + fd as i32, + iovs.as_ptr() as i32, + iovs.len() as i32, + offset as i64, + rp0.as_mut_ptr() as i32, + ); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_read( + fs: &mut FileSystem, + fd: UserFd, + iovs: IovecArray<'_>, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => { + let node = fs.embedded_fs.get_node(vfd)?; + match node { + Node::Dir { .. } => Err(wasi::ERRNO_ISDIR.into()), + Node::File(body) => { + let open = fs.embedded_fs.get_fd_entry(vfd)?; + let mut cursor = std::io::Cursor::new(body.content()); + cursor.set_position(open.offset as u64); + let read_bytes = read_bytes(cursor, iovs)?; + let open = fs.embedded_fs.get_fd_entry_mut(vfd)?; + open.offset += read_bytes; + Ok(read_bytes) + } + } + } + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::fd_read( + fd as i32, + iovs.as_ptr() as i32, + iovs.len() as i32, + rp0.as_mut_ptr() as i32, + ); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_readdir( + fs: &mut FileSystem, + fd: UserFd, + buf: *mut u8, + buf_len: u32, + cookie: Dircookie, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => { + let node = fs.embedded_fs.get_node(vfd)?; + let entries = match node { + Node::Dir(body) => body.entries(), + Node::File { .. } => { + return Err(wasi::ERRNO_NOTDIR.into()); + } + }; + let mut bufused = 0; + let mut current_cookie = cookie; + let mut buf = buf; + let buf_len = buf_len as usize; + for entry in entries { + current_cookie += 1; + let name_len = entry.name.len(); + let node_id = fs.embedded_fs.get_node_id_by_link(entry.link_id); + let node_stat = fs.embedded_fs.get_filestat_from_node_id(node_id); + let dirent = wasi::Dirent { + d_next: current_cookie, + d_ino: node_id.ino() as u64, + d_namlen: name_len as u32, + d_type: node_stat.filetype, + }; + let dirent_len = std::mem::size_of::(); + let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused); + std::ptr::copy(&dirent as *const _ as *const u8, buf, dirent_copy_len); + if dirent_copy_len < dirent_len { + return Ok(buf_len); + } + buf = buf.add(dirent_copy_len); + bufused += dirent_copy_len; + + let name_copy_len = std::cmp::min(name_len, buf_len - bufused); + std::ptr::copy(entry.name.as_ptr(), buf, name_copy_len); + + if name_copy_len < name_len { + return Ok(buf_len); + } + buf = buf.add(name_len); + bufused += name_copy_len; + } + Ok(bufused) + } + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::fd_readdir( + fd as i32, + buf as i32, + buf_len as i32, + cookie as i64, + rp0.as_mut_ptr() as i32, + ); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_renumber( + fs: &mut FileSystem, + fd: UserFd, + to: UserFd, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + let to = fs.get_backing_fd(to)?; + match (fd, to) { + (BackingFd::Wasi(fd), BackingFd::Wasi(to)) => { + let ret = wasi::wasi_snapshot_preview1::fd_renumber(fd as i32, to as i32); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + (_, _) => todo!(), + } +} + +pub(crate) unsafe fn fd_seek( + fs: &mut FileSystem, + fd: UserFd, + offset: Filedelta, + whence: i32, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + fn compute_new_offset(base: usize, offset: Filedelta) -> Result { + let new_offset = if offset > 0 { + base + (offset as usize) + } else { + let neg_offset = (-offset) as usize; + if neg_offset < base { + base - neg_offset + } else { + return Err(wasi::ERRNO_INVAL.into()); + } + }; + Ok(new_offset) + } + match fd { + BackingFd::Virtual(vfd) => { + let whence: wasi::Whence = std::mem::transmute(whence as u8); + match whence { + wasi::WHENCE_SET => { + let fd_entry = fs.embedded_fs.get_fd_entry_mut(vfd)?; + fd_entry.offset = offset as usize; + Ok(offset as Filesize) + } + wasi::WHENCE_CUR => { + let fd_entry = fs.embedded_fs.get_fd_entry_mut(vfd)?; + let absolute_offset = compute_new_offset(fd_entry.offset, offset)?; + fd_entry.offset = absolute_offset; + Ok(absolute_offset as Filesize) + } + wasi::WHENCE_END => { + let fd_entry = fs.embedded_fs.get_fd_entry(vfd)?; + let node = fs.embedded_fs.get_node(vfd)?; + match node { + Node::File(body) => { + let content_len = body.content().len(); + let fd_entry = fs.embedded_fs.get_fd_entry_mut(vfd)?; + let absolute_offset = compute_new_offset(content_len, offset)?; + fd_entry.offset = absolute_offset; + Ok(absolute_offset as Filesize) + } + Node::Dir { .. } => Err(wasi::ERRNO_INVAL.into()), + } + } + _ => Err(wasi::ERRNO_INVAL.into()), + } + } + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::fd_seek( + fd as i32, + offset, + whence as i32, + rp0.as_mut_ptr() as i32, + ); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Filesize)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_sync(fs: &mut FileSystem, fd: UserFd) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::fd_sync(fd as i32); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_tell( + fs: &mut FileSystem, + fd: UserFd, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => { + let node = fs.embedded_fs.get_node(vfd)?; + let open = fs.embedded_fs.get_fd_entry_mut(vfd)?; + Ok(open.offset as u64) + } + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::fd_tell(fd as i32, rp0.as_mut_ptr() as i32); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Filesize)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn fd_write( + fs: &mut FileSystem, + fd: UserFd, + iovs: CiovecArray<'_>, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::fd_write( + fd as i32, + iovs.as_ptr() as i32, + iovs.len() as i32, + rp0.as_mut_ptr() as i32, + ); + match ret { + 0 => Ok(rp0.assume_init()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn path_create_directory( + fs: &mut FileSystem, + fd: UserFd, + path: &CStr, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::path_create_directory( + fd as i32, + path.as_ptr() as i32, + path.to_bytes().len() as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn path_filestat_get( + fs: &mut FileSystem, + fd: UserFd, + flags: Lookupflags, + path: &CStr, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => { + let path = cstr_to_path(path)?; + Ok(fs.embedded_fs.get_filestat_at_path(vfd, path)?) + } + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::path_filestat_get( + fd as i32, + flags as i32, + path.as_ptr() as i32, + path.to_bytes().len() as i32, + rp0.as_mut_ptr() as i32, + ); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Filestat)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn path_filestat_set_times( + fs: &mut FileSystem, + fd: UserFd, + flags: Lookupflags, + path: &CStr, + atim: Timestamp, + mtim: Timestamp, + fst_flags: Fstflags, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::path_filestat_set_times( + fd as i32, + flags as i32, + path.as_ptr() as i32, + path.to_bytes().len() as i32, + atim as i64, + mtim as i64, + fst_flags as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn path_link( + fs: &mut FileSystem, + old_fd: UserFd, + old_flags: Lookupflags, + old_path: &CStr, + new_fd: UserFd, + new_path: &CStr, +) -> Result<(), Error> { + let old_fd = fs.get_backing_fd(old_fd)?; + let new_fd = fs.get_backing_fd(new_fd)?; + match (old_fd, new_fd) { + (BackingFd::Wasi(old_fd), BackingFd::Wasi(new_fd)) => { + let ret = wasi::wasi_snapshot_preview1::path_link( + old_fd as i32, + old_flags as i32, + old_path.as_ptr() as i32, + old_path.to_bytes().len() as i32, + new_fd as i32, + new_path.as_ptr() as i32, + new_path.to_bytes().len() as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + (_, _) => todo!(), + } +} + +pub(crate) unsafe fn path_open( + fs: &mut FileSystem, + fd: UserFd, + dirflags: Lookupflags, + path: &CStr, + oflags: Oflags, + fs_rights_base: Rights, + fs_rights_inheriting: Rights, + fdflags: Fdflags, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => { + let path = cstr_to_path(path)?; + let new_vfd = fs.embedded_fs.open_file(vfd, path, fdflags)?; + Ok(fs.issue_user_fd(BackingFd::Virtual(new_vfd))) + } + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::path_open( + fd as i32, + dirflags as i32, + path.as_ptr() as i32, + path.to_bytes().len() as i32, + oflags as i32, + fs_rights_base as i64, + fs_rights_inheriting as i64, + fdflags as i32, + rp0.as_mut_ptr() as i32, + ); + match ret { + 0 => { + let new_fd = BackingFd::Wasi(rp0.assume_init()); + Ok(fs.issue_user_fd(new_fd)) + } + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn path_readlink( + fs: &mut FileSystem, + fd: UserFd, + path: &CStr, + buf: *mut u8, + buf_len: u32, +) -> Result { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let mut rp0 = MaybeUninit::::uninit(); + let ret = wasi::wasi_snapshot_preview1::path_readlink( + fd as i32, + path.as_ptr() as i32, + path.to_bytes().len() as i32, + buf as i32, + buf_len as i32, + rp0.as_mut_ptr() as i32, + ); + match ret { + 0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn path_remove_directory( + fs: &mut FileSystem, + fd: UserFd, + path: &CStr, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::path_remove_directory( + fd as i32, + path.as_ptr() as i32, + path.to_bytes().len() as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn path_rename( + fs: &mut FileSystem, + fd: UserFd, + old_path: &CStr, + new_fd: UserFd, + new_path: &CStr, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + let new_fd = fs.get_backing_fd(new_fd)?; + match (fd, new_fd) { + (BackingFd::Wasi(fd), BackingFd::Wasi(new_fd)) => { + let ret = wasi::wasi_snapshot_preview1::path_rename( + fd as i32, + old_path.as_ptr() as i32, + old_path.to_bytes().len() as i32, + new_fd as i32, + new_path.as_ptr() as i32, + new_path.to_bytes().len() as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + (_, _) => todo!(), + } +} + +pub(crate) unsafe fn path_symlink( + fs: &mut FileSystem, + old_path: &CStr, + fd: UserFd, + new_path: &CStr, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::path_symlink( + old_path.as_ptr() as i32, + old_path.to_bytes().len() as i32, + fd as i32, + new_path.as_ptr() as i32, + new_path.to_bytes().len() as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn path_unlink_file( + fs: &mut FileSystem, + fd: UserFd, + path: &CStr, +) -> Result<(), Error> { + let fd = fs.get_backing_fd(fd)?; + match fd { + BackingFd::Virtual(vfd) => todo!(), + BackingFd::Wasi(fd) => { + let ret = wasi::wasi_snapshot_preview1::path_unlink_file( + fd as i32, + path.as_ptr() as i32, + path.to_bytes().len() as i32, + ); + match ret { + 0 => Ok(()), + _ => Err(Error(ret as u16)), + } + } + } +} + +pub(crate) unsafe fn poll_oneoff( + fs: &mut FileSystem, + in_: *const Subscription, + out: *mut Event, + nsubscriptions: u32, +) -> Result { + Err(wasi::ERRNO_NOTSUP.into()) +} + +fn read_bytes(mut src: R, iovs: wasi::IovecArray) -> Result { + let mut bytes_read = 0; + for iov in iovs { + unsafe { + let buf = slice::from_raw_parts_mut(iov.buf, iov.buf_len); + bytes_read += src.read(buf).map_err(|_| wasi::ERRNO_IO)?; + } + } + Ok(bytes_read) +} + +fn cstr_to_path(path: &CStr) -> Result<&Path, wasi::Errno> { + let os_str: &OsStr = unsafe { std::mem::transmute(path.to_bytes()) }; + Ok(Path::new(os_str)) +} diff --git a/tests/run-make/.gitignore b/tests/run-make/.gitignore new file mode 100644 index 0000000..d36977d --- /dev/null +++ b/tests/run-make/.gitignore @@ -0,0 +1 @@ +.tmp diff --git a/tests/run-make/canonical-path/Makefile b/tests/run-make/canonical-path/Makefile new file mode 100644 index 0000000..65bb6f4 --- /dev/null +++ b/tests/run-make/canonical-path/Makefile @@ -0,0 +1,12 @@ +-include ../tools.mk + +objs = $(TMPDIR)/main.c.o + +check: $(objs) + $(CC) $(LDFLAGS) $(objs) $(LIB_WASI_VFS) -o $(TMPDIR)/main.wasm + $(WASI_RUN) --mapdir /mnt::./mnt $(TMPDIR)/main.wasm + $(WASI_VFS_CLI) pack $(TMPDIR)/main.wasm --mapdir /mnt::./mnt -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm + +clean: + rm -rf $(PROG) $(objs) diff --git a/tests/run-make/canonical-path/main.c b/tests/run-make/canonical-path/main.c new file mode 100644 index 0000000..f762496 --- /dev/null +++ b/tests/run-make/canonical-path/main.c @@ -0,0 +1,11 @@ +#include "../check.h" +#include +#include + +int main(int argc, char *argv[]) { + check_file_exists("/mnt/a/b/c.txt"); + check_file_exists("/mnt/a/./b/c.txt"); + check_file_exists("/mnt/a/././b/c.txt"); + check_file_exists("/mnt/a//b/c.txt"); + return 0; +} diff --git a/tests/run-make/canonical-path/mnt/a/b/c.txt b/tests/run-make/canonical-path/mnt/a/b/c.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/chdir-emulation/Makefile b/tests/run-make/chdir-emulation/Makefile new file mode 100644 index 0000000..11e0873 --- /dev/null +++ b/tests/run-make/chdir-emulation/Makefile @@ -0,0 +1,11 @@ +-include ../tools.mk + +objs = $(TMPDIR)/main.c.o + +check: $(objs) + $(CC) $(LDFLAGS) $(objs) $(LIB_WASI_VFS) -o $(TMPDIR)/main.wasm + $(WASI_VFS_CLI) pack $(TMPDIR)/main.wasm --mapdir /mnt::./mnt -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm + +clean: + rm -rf $(PROG) $(objs) diff --git a/tests/run-make/chdir-emulation/main.c b/tests/run-make/chdir-emulation/main.c new file mode 100644 index 0000000..a4d5870 --- /dev/null +++ b/tests/run-make/chdir-emulation/main.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include "../check.h" + +void check_file_content(const char *path, const char *expected) { + FILE *fp; + char *line = NULL; + size_t len = 0; + ssize_t read; + + fp = fopen(path, "r"); + if (fp == NULL) { + perror("fopen"); + exit(EXIT_FAILURE); + } + + read = getline(&line, &len, fp); + if(read == -1) { + perror("getline"); + exit(EXIT_FAILURE); + } + if (strcmp(line, expected) != 0) { + printf("expected '%s' but got '%s'\n", expected, line); + fflush(stdout); + exit(EXIT_FAILURE); + } + + fclose(fp); + if (line) + free(line); +} + +int main(int argc, char *argv[]) { + // it's important to use chdir to enable __wasilibc_find_relpath_alloc path + chdir("/mnt"); + + check_file_content("hello.txt", "Hello\n"); + check_file_content("hello.txt", "Hello\n"); + return 0; +} diff --git a/tests/run-make/chdir-emulation/mnt/hello.txt b/tests/run-make/chdir-emulation/mnt/hello.txt new file mode 100644 index 0000000..e965047 --- /dev/null +++ b/tests/run-make/chdir-emulation/mnt/hello.txt @@ -0,0 +1 @@ +Hello diff --git a/tests/run-make/check.h b/tests/run-make/check.h new file mode 100644 index 0000000..1f993de --- /dev/null +++ b/tests/run-make/check.h @@ -0,0 +1,97 @@ +#ifndef RUN_MAKE_CHECK_H +#define RUN_MAKE_CHECK_H + +#include +#include +#include +#include +#include + +static inline void _check_file_exists(const char *path, const char *file, + const char *func, int line) { + if (!path) { + fprintf(stderr, "check_file_exists: path is null in %s:%d (%s)\n", file, + line, func); + exit(1); + return; + } + FILE *f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "File %s not found in %s:%d (%s)\n", path, file, line, + func); + exit(1); + } + fclose(f); +} + +#define check_file_exists(path) \ + _check_file_exists(path, __FILE__, __func__, __LINE__) + +static inline void _check_file_not_exists(const char *path, const char *file, + const char *func, int line) { + if (!path) { + fprintf(stderr, "check_file_not_exists: path is null in %s:%d (%s)\n", file, + line, func); + exit(1); + return; + } + FILE *f = fopen(path, "r"); + if (f) { + fprintf(stderr, "File %s unexpectedly found in %s:%d (%s)\n", path, file, + line, func); + fclose(f); + } +} + +#define check_file_not_exists(path) \ + _check_file_not_exists(path, __FILE__, __func__, __LINE__) + +static inline void _check_dir_entry_size(const char *dir, int expected, + const char *file, const char *func, + int line) { + DIR *d = opendir(dir); + if (!d) { + fprintf(stderr, "Directory %s not found in %s:%d (%s)\n", dir, file, line, + func); + exit(1); + } + struct dirent *de; + int count = 0; + while ((de = readdir(d))) { + if (strlen(de->d_name) == 1 && de->d_name[0] == '.') + continue; + if (strlen(de->d_name) == 2 && de->d_name[0] == '.' && de->d_name[1] == '.') + continue; + count++; + } + closedir(d); + if (count != expected) { + fprintf(stderr, "Directory %s should contain %d files, but contains %d\n", + dir, expected, count); + exit(1); + } +} + +#define check_dir_entry_size(dir, expected) \ + _check_dir_entry_size(dir, expected, __FILE__, __func__, __LINE__) + +#endif + +static inline void _check_access(const char *path, const char *file, + const char *func, int line) { + if (!path) { + fprintf(stderr, "check_access: path is null in %s:%d (%s)\n", file, line, + func); + exit(1); + return; + } + int e = access(path, F_OK); + if (e != 0) { + fprintf(stderr, "File %s is not accessible %s:%d (%s)\n", path, file, line, + func); + perror("access"); + exit(1); + } +} + +#define check_access(path) _check_access(path, __FILE__, __func__, __LINE__) diff --git a/tests/run-make/libc-api/Makefile b/tests/run-make/libc-api/Makefile new file mode 100644 index 0000000..100b514 --- /dev/null +++ b/tests/run-make/libc-api/Makefile @@ -0,0 +1,11 @@ +-include ../tools.mk + +objs = $(TMPDIR)/main.c.o + +check: $(objs) + $(CC) $(LDFLAGS) $(objs) $(LIB_WASI_VFS) -o $(TMPDIR)/main.wasm + $(WASI_VFS_CLI) pack $(TMPDIR)/main.wasm --mapdir /usr::./mnt -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm --mapdir /::./ + +clean: + rm -rf $(PROG) $(objs) diff --git a/tests/run-make/libc-api/hello.txt b/tests/run-make/libc-api/hello.txt new file mode 100644 index 0000000..e484e3b --- /dev/null +++ b/tests/run-make/libc-api/hello.txt @@ -0,0 +1 @@ +from host diff --git a/tests/run-make/libc-api/main.c b/tests/run-make/libc-api/main.c new file mode 100644 index 0000000..b6311b0 --- /dev/null +++ b/tests/run-make/libc-api/main.c @@ -0,0 +1,34 @@ +#include +#include +#include + +void check_file_content(const char *path, const char *expected) { + FILE *fp; + char *line = NULL; + size_t len = 0; + ssize_t read; + + fp = fopen(path, "r"); + if (fp == NULL) { + perror("fopen"); + exit(EXIT_FAILURE); + } + + read = getline(&line, &len, fp); + if (strcmp(line, expected) != 0) { + printf("expected '%s' but got '%s'\n", expected, line); + fflush(stdout); + exit(EXIT_FAILURE); + } + + fclose(fp); + if (line) + free(line); +} + +int main(void) { + check_file_content("/hello.txt", "from host\n"); + check_file_content("/usr/hello.txt", "from vfs\n"); + check_file_content("/usr/subdir/inner.txt", "inner file\n"); + exit(EXIT_SUCCESS); +} diff --git a/tests/run-make/libc-api/mnt/hello.txt b/tests/run-make/libc-api/mnt/hello.txt new file mode 100644 index 0000000..b474574 --- /dev/null +++ b/tests/run-make/libc-api/mnt/hello.txt @@ -0,0 +1 @@ +from vfs diff --git a/tests/run-make/libc-api/mnt/subdir/inner.txt b/tests/run-make/libc-api/mnt/subdir/inner.txt new file mode 100644 index 0000000..3649765 --- /dev/null +++ b/tests/run-make/libc-api/mnt/subdir/inner.txt @@ -0,0 +1 @@ +inner file diff --git a/tests/run-make/mapdir-root/Makefile b/tests/run-make/mapdir-root/Makefile new file mode 100644 index 0000000..252a8bd --- /dev/null +++ b/tests/run-make/mapdir-root/Makefile @@ -0,0 +1,11 @@ +-include ../tools.mk + +objs = $(TMPDIR)/main.c.o + +check: $(objs) + $(CC) $(LDFLAGS) $(objs) $(LIB_WASI_VFS) -o $(TMPDIR)/main.wasm + $(WASI_VFS_CLI) pack $(TMPDIR)/main.wasm --mapdir /::./mnt -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm + +clean: + rm -rf $(PROG) $(objs) diff --git a/tests/run-make/mapdir-root/main.c b/tests/run-make/mapdir-root/main.c new file mode 100644 index 0000000..58e9b40 --- /dev/null +++ b/tests/run-make/mapdir-root/main.c @@ -0,0 +1,9 @@ +#include "../check.h" +#include +#include + +int main(int argc, char *argv[]) { + check_file_exists("/hello.txt"); + check_dir_entry_size("/", 1); + return 0; +} diff --git a/tests/run-make/mapdir-root/mnt/hello.txt b/tests/run-make/mapdir-root/mnt/hello.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/minimum-link/Makefile b/tests/run-make/minimum-link/Makefile new file mode 100644 index 0000000..99f2d24 --- /dev/null +++ b/tests/run-make/minimum-link/Makefile @@ -0,0 +1,11 @@ +-include ../tools.mk + +objs = $(TMPDIR)/main.c.o + +check: $(objs) + $(CC) $(LDFLAGS) $(objs) $(LIB_WASI_VFS) -o $(TMPDIR)/main.wasm + $(WASI_VFS_CLI) pack $(TMPDIR)/main.wasm -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm + +clean: + rm -rf $(PROG) $(objs) diff --git a/tests/run-make/minimum-link/main.c b/tests/run-make/minimum-link/main.c new file mode 100644 index 0000000..78f2de1 --- /dev/null +++ b/tests/run-make/minimum-link/main.c @@ -0,0 +1 @@ +int main(void) { return 0; } diff --git a/tests/run-make/multi-mapdir/Makefile b/tests/run-make/multi-mapdir/Makefile new file mode 100644 index 0000000..4148168 --- /dev/null +++ b/tests/run-make/multi-mapdir/Makefile @@ -0,0 +1,19 @@ +-include ../tools.mk + +objs = $(TMPDIR)/main.c.o + +define do_check + $(WASI_RUN) $(2) $(TMPDIR)/main.wasm -- $(1) + $(WASI_VFS_CLI) pack $(TMPDIR)/main.wasm $(2) -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm -- $(1) + +endef + +check: $(objs) + $(CC) $(LDFLAGS) $(objs) $(LIB_WASI_VFS) -o $(TMPDIR)/main.wasm + $(call do_check,simple,--mapdir /mnt1::./mnt1 --mapdir /mnt0::./mnt0) + $(call do_check,overlap1,--mapdir /mnt0/mnt1::./mnt1 --mapdir /mnt0::./mnt0) + $(call do_check,overlap2,--mapdir /mnt0::./mnt0 --mapdir /mnt0/mnt1::./mnt1) + +clean: + rm -rf $(PROG) $(objs) diff --git a/tests/run-make/multi-mapdir/main.c b/tests/run-make/multi-mapdir/main.c new file mode 100644 index 0000000..4c064a1 --- /dev/null +++ b/tests/run-make/multi-mapdir/main.c @@ -0,0 +1,26 @@ +#include "../check.h" +#include +#include + +int main(int argc, char *argv[]) { + if (argc != 2) { + return 1; + } + char *mode = argv[1]; + if (strcmp(mode, "simple") == 0) { + check_file_exists("/mnt0/hello.txt"); + check_file_exists("/mnt1/goodbye.txt"); + } else if (strcmp(mode, "overlap1") == 0) { + check_file_exists("/mnt0/hello.txt"); + check_file_exists("/mnt0/mnt1/goodbye.txt"); + check_dir_entry_size("/mnt0", 1); + check_dir_entry_size("/mnt0/mnt1", 1); + } else if (strcmp(mode, "overlap2") == 0) { + check_file_exists("/mnt0/hello.txt"); + check_file_exists("/mnt0/mnt1/goodbye.txt"); + } else { + fprintf(stderr, "Unknown mode: %s\n", mode); + return 1; + } + return 0; +} diff --git a/tests/run-make/multi-mapdir/mnt0/hello.txt b/tests/run-make/multi-mapdir/mnt0/hello.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/multi-mapdir/mnt1/goodbye.txt b/tests/run-make/multi-mapdir/mnt1/goodbye.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/pack-twice/Makefile b/tests/run-make/pack-twice/Makefile new file mode 100644 index 0000000..5f6ec18 --- /dev/null +++ b/tests/run-make/pack-twice/Makefile @@ -0,0 +1,18 @@ +-include ../tools.mk + +objs = $(TMPDIR)/main.c.o + +check: $(objs) + $(CC) $(LDFLAGS) $(objs) $(LIB_WASI_VFS) -o $(TMPDIR)/main.wasm + $(WASI_VFS_CLI) pack $(TMPDIR)/main.wasm --mapdir /mnt0::./mnt0 -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm phase1 + + $(WASI_VFS_CLI) pack $(TMPDIR)/main.packed.wasm --mapdir /mnt1::./mnt1 -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm phase2 + + $(WASI_RUN) --mapdir /mnt0::./mnt0 --mapdir /mnt1::./mnt1 --mapdir /mnt1::./mnt1_1 $(TMPDIR)/main.wasm phase3 + $(WASI_VFS_CLI) pack $(TMPDIR)/main.packed.wasm --mapdir /mnt1::./mnt1_1 -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm phase3 + +clean: + rm -rf $(PROG) $(objs) diff --git a/tests/run-make/pack-twice/main.c b/tests/run-make/pack-twice/main.c new file mode 100644 index 0000000..6ad5289 --- /dev/null +++ b/tests/run-make/pack-twice/main.c @@ -0,0 +1,22 @@ +#include "../check.h" + +int main(int argc, char *argv[]) { + if (argc != 2) { + return 1; + } + char *mode = argv[1]; + if (strcmp(mode, "phase1") == 0) { + check_file_exists("/mnt0/hello.txt"); + } else if (strcmp(mode, "phase2") == 0) { + check_file_exists("/mnt0/hello.txt"); + check_file_exists("/mnt1/goodbye.txt"); + } else if (strcmp(mode, "phase3") == 0) { + check_file_exists("/mnt0/hello.txt"); + check_file_exists("/mnt1/x.txt"); + check_file_not_exists("/mnt1/goodbye.txt"); + } else { + fprintf(stderr, "Unknown mode: %s\n", mode); + return 1; + } + return 0; +} diff --git a/tests/run-make/pack-twice/mnt0/hello.txt b/tests/run-make/pack-twice/mnt0/hello.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/pack-twice/mnt1/goodbye.txt b/tests/run-make/pack-twice/mnt1/goodbye.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/pack-twice/mnt1_1/x.txt b/tests/run-make/pack-twice/mnt1_1/x.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/parent-dir/Makefile b/tests/run-make/parent-dir/Makefile new file mode 100644 index 0000000..e2441be --- /dev/null +++ b/tests/run-make/parent-dir/Makefile @@ -0,0 +1,11 @@ +-include ../tools.mk + +objs = $(TMPDIR)/main.c.o + +check: $(objs) + $(CC) $(LDFLAGS) $(objs) $(LIB_WASI_VFS) -o $(TMPDIR)/main.wasm + $(WASI_VFS_CLI) pack $(TMPDIR)/main.wasm --mapdir /usr::./usr -o $(TMPDIR)/main.packed.wasm + $(WASI_RUN) $(TMPDIR)/main.packed.wasm + +clean: + rm -rf $(PROG) $(objs) diff --git a/tests/run-make/parent-dir/main.c b/tests/run-make/parent-dir/main.c new file mode 100644 index 0000000..2629b8b --- /dev/null +++ b/tests/run-make/parent-dir/main.c @@ -0,0 +1,22 @@ +#include "../check.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + int usr_local = open("/usr/local/bin", O_RDONLY, O_DIRECTORY); + perror("usr_local"); + assert(usr_local != -1); + // int yay = openat(usr_local, "./yay", O_RDONLY, 0); + // perror("yay"); + // assert(yay != -1); + check_access("/usr/local/bin/../hey"); + int hey = openat(usr_local, "../hey", O_RDONLY, 0); + perror("hey"); + assert(hey != -1); + return 0; +} diff --git a/tests/run-make/parent-dir/usr/local/bin/yay b/tests/run-make/parent-dir/usr/local/bin/yay new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/parent-dir/usr/local/hey b/tests/run-make/parent-dir/usr/local/hey new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/reactor-model/Makefile b/tests/run-make/reactor-model/Makefile new file mode 100644 index 0000000..0404e55 --- /dev/null +++ b/tests/run-make/reactor-model/Makefile @@ -0,0 +1,9 @@ +-include ../tools.mk + +check: $(objs) + $(CC) $(LDFLAGS) main.c $(LIB_WASI_VFS) -mexec-model=reactor -o $(TMPDIR)/main.wasm + $(WASI_VFS_CLI) pack $(TMPDIR)/main.wasm --mapdir /::./mnt -o $(TMPDIR)/main.packed.wasm + $(NODE) --experimental-wasi-unstable-preview1 ./check.js $(TMPDIR)/main.packed.wasm + +clean: + rm -rf $(TMPDIR)/* diff --git a/tests/run-make/reactor-model/check.js b/tests/run-make/reactor-model/check.js new file mode 100644 index 0000000..3a87afb --- /dev/null +++ b/tests/run-make/reactor-model/check.js @@ -0,0 +1,17 @@ +const { WASI } = require("wasi"); +const fs = require("fs"); +const process = require("process"); + +const buffer = fs.readFileSync(process.argv[2]); + +const wasi = new WASI({ + env: { ...process.env }, +}); +const m = new WebAssembly.Module(buffer); +const i = new WebAssembly.Instance(m, { + wasi_snapshot_preview1: wasi.wasiImport, +}); + +console.log(i.exports) +wasi.initialize(i); +i.exports.check(); diff --git a/tests/run-make/reactor-model/main.c b/tests/run-make/reactor-model/main.c new file mode 100644 index 0000000..732837e --- /dev/null +++ b/tests/run-make/reactor-model/main.c @@ -0,0 +1,14 @@ +#include "../check.h" +#include + +#pragma clang diagnostic ignored "-Wunknown-attributes" +__attribute__((export_name("check"))) +void check(void) { + printf("checking\n"); + check_file_exists("/hello.txt"); + check_dir_entry_size("/", 1); +} + +int main(int argc, char *argv[]) { + return 1; +} diff --git a/tests/run-make/reactor-model/mnt/hello.txt b/tests/run-make/reactor-model/mnt/hello.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-make/tools.mk b/tests/run-make/tools.mk new file mode 100644 index 0000000..e1312af --- /dev/null +++ b/tests/run-make/tools.mk @@ -0,0 +1,19 @@ +CC = $(WASI_SDK_PATH)/bin/clang +WASI_VFS_CLI = cargo run -p wasi-vfs-cli -- +WASI_RUN = wasmtime +NODE = node + +TARGET = wasm32-unknown-wasi + +OPTFLAGS ?= +CCFLAGS = -target $(TARGET) $(OPTFLAGS) +LDFLAGS = -target $(TARGET) + +RUNMAKE_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +LIB_WASI_VFS ?= $(RUNMAKE_DIR)/../../target/wasm32-unknown-unknown/debug/libwasi_vfs.a + +TMPDIR = $(shell mkdir -p .tmp && echo .tmp) + +$(TMPDIR)/%.c.o: %.c $(RUNMAKE_DIR)/check.h + $(CC) -c $(CCFLAGS) $< -o $@ diff --git a/tools/run-make-test.sh b/tools/run-make-test.sh new file mode 100755 index 0000000..5ddd466 --- /dev/null +++ b/tools/run-make-test.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -eu + +TOOLS_DIR="$(cd "$(dirname "$0")" && pwd)" +TESTS_DIR="$(dirname "$TOOLS_DIR")/tests/run-make" +TMPDIR_BASE="$(mktemp -d)" + +for testdir in "$TESTS_DIR"/*; do + if [ -d "$testdir" ]; then + pushd "$testdir" + tmpdir="$TMPDIR_BASE/$(basename "$testdir")" + mkdir -p "$tmpdir" + make check TMPDIR="$tmpdir" + popd + fi +done