├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "wasi-test" 5 | version = "0.1.0" 6 | 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasi-test" 3 | version = "0.1.0" 4 | authors = ["Jakub Konka "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | 9 | [[bin]] 10 | path = "src/main.rs" 11 | name = "main" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-wasi-tutorial 2 | Just some simple program with very basic I/O serving as a Rust version of 3 | the excellent [WASI tutorial](https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-tutorial.md). 4 | 5 | ## Building 6 | Firstly, make sure you are running the latest version of Rust `stable`, `v1.36.0`. 7 | If not, go ahead and install it. BTW, I strongly recommend `rustup` for all 8 | your Rust versioning worries. 9 | 10 | Next, install the required target 11 | ``` 12 | $ rustup target add wasm32-wasi 13 | ``` 14 | 15 | Afterwards, you should be able to cross-compile to WASI by simply running 16 | ``` 17 | $ cargo build --target=wasm32-wasi 18 | ``` 19 | 20 | ## Running 21 | Just for fun, let's test it out using [wasmtime](https://github.com/CraneStation/wasmtime) 22 | WASI runtime. 23 | 24 | Create sample input 25 | ``` 26 | echo 'WASI is a lot of fun!' > in.txt 27 | ``` 28 | 29 | Run it using `wasmtime` 30 | ``` 31 | $ wasmtime --dir=. target/wasm32-wasi/debug/main.wasm in.txt out.txt 32 | ``` 33 | 34 | As a result, you should have `out.txt` file created with the same contents as `in.txt` :-) 35 | 36 | ### Bonus: exploring the syscalls 37 | For the curious ones, here's a simple way to trace back the syscalls used by our WASI program 38 | in `wasmtime` 39 | ``` 40 | RUST_LOG=wasi_common=trace wasmtime -d --dir=. target/wasm32-wasi/debug/main.wasm -- in.txt out.txt 41 | ``` 42 | 43 | As a result, you should get output similar to the following 44 | ``` 45 | TRACE wasi_common::hostcalls::fs > fd_prestat_get(fd=3, prestat_ptr=0xffff0) 46 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 47 | TRACE wasi_common::hostcalls::fs > fd_prestat_dir_name(fd=3, path_ptr=0x110088, path_len=1) 48 | TRACE wasi_common::hostcalls::fs > | (path_ptr,path_len)="." 49 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 50 | TRACE wasi_common::hostcalls::fs > fd_fdstat_get(fd=3, fdstat_ptr=0xfffd8) 51 | TRACE wasi_common::hostcalls::fs > | *buf=__wasi_fdstat_t { fs_filetype: 3, fs_flags: 0, fs_rights_base: 264240792, fs_rights_inheriting: 268435455 } 52 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 53 | TRACE wasi_common::hostcalls::fs > fd_prestat_get(fd=4, prestat_ptr=0xffff0) 54 | TRACE wasi_common::hostcalls > -> errno=__WASI_EBADF 55 | TRACE wasi_common::hostcalls::misc > environ_sizes_get(environ_count_ptr=0xffff0, environ_size_ptr=0xffffc) 56 | TRACE wasi_common::hostcalls::misc > | *environ_count_ptr=0 57 | TRACE wasi_common::hostcalls::misc > | *environ_size_ptr=0 58 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 59 | TRACE wasi_common::hostcalls::misc > args_sizes_get(argc_ptr=0xffffc, argv_buf_size_ptr=0xffff0) 60 | TRACE wasi_common::hostcalls::misc > | *argc_ptr=3 61 | TRACE wasi_common::hostcalls::misc > | *argv_buf_size_ptr=17 62 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 63 | TRACE wasi_common::hostcalls::misc > args_get(argv_ptr=0x110088, argv_buf=0x1100a8) 64 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 65 | TRACE wasi_common::hostcalls::misc > args_sizes_get(argc_ptr=0xffed8, argv_buf_size_ptr=0xffedc) 66 | TRACE wasi_common::hostcalls::misc > | *argc_ptr=3 67 | TRACE wasi_common::hostcalls::misc > | *argv_buf_size_ptr=17 68 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 69 | TRACE wasi_common::hostcalls::misc > args_get(argv_ptr=0x110118, argv_buf=0x110128) 70 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 71 | TRACE wasi_common::hostcalls::fs > path_open(dirfd=3, dirflags=1, path_ptr=0x110198, path_len=2, oflags=0x0, fs_rights_base=0xf87febe, fs_rights_inheriting=0xf87febe, fs_flags=0x0, fd_out_ptr=0xffcbc) 72 | TRACE wasi_common::hostcalls::fs > | (path_ptr,path_len)="in" 73 | TRACE wasi_common::hostcalls::fs > | *fd=4 74 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 75 | TRACE wasi_common::hostcalls::fs > fd_read(fd=4, iovs_ptr=0xffc88, iovs_len=1, nread=0xffc7c) 76 | TRACE wasi_common::hostcalls::fs > | *nread=12 77 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 78 | TRACE wasi_common::hostcalls::fs > fd_read(fd=4, iovs_ptr=0xffc88, iovs_len=1, nread=0xffc7c) 79 | TRACE wasi_common::hostcalls::fs > | *nread=0 80 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 81 | TRACE wasi_common::hostcalls::fs > path_open(dirfd=3, dirflags=1, path_ptr=0x1101c0, path_len=3, oflags=0x9, fs_rights_base=0xfc7bffd, fs_rights_inheriting=0xfc7bffd, fs_flags=0x0, fd_out_ptr=0xffcbc) 82 | TRACE wasi_common::hostcalls::fs > | (path_ptr,path_len)="out" 83 | TRACE wasi_common::hostcalls::fs > | *fd=4 84 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 85 | TRACE wasi_common::hostcalls::fs > fd_write(fd=4, iovs_ptr=0xffce8, iovs_len=1, nwritten=0xffcdc) 86 | TRACE wasi_common::hostcalls::fs > | *nwritten=12 87 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 88 | TRACE wasi_common::hostcalls::fs > fd_close(fd=4) 89 | TRACE wasi_common::hostcalls > -> errno=__WASI_ESUCCESS 90 | TRACE wasi_common::hostcalls::fs > fd_close(fd=4) 91 | TRACE wasi_common::hostcalls > -> errno=__WASI_EBADF 92 | ``` 93 | 94 | Pretty neat, isn't it? ;-) 95 | 96 | ## License 97 | [The Unlicense](LICENSE) 98 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::io::{Read, Write}; 4 | 5 | fn process(input_fname: &str, output_fname: &str) -> Result<(), String> { 6 | let mut input_file = 7 | fs::File::open(input_fname).map_err(|err| format!("error opening input: {}", err))?; 8 | let mut contents = Vec::new(); 9 | input_file 10 | .read_to_end(&mut contents) 11 | .map_err(|err| format!("read error: {}", err))?; 12 | 13 | let mut output_file = fs::File::create(output_fname) 14 | .map_err(|err| format!("error opening output '{}': {}", output_fname, err))?; 15 | output_file 16 | .write_all(&contents) 17 | .map_err(|err| format!("write error: {}", err)) 18 | } 19 | 20 | fn main() { 21 | let args: Vec = env::args().collect(); 22 | let program = args[0].clone(); 23 | 24 | if args.len() < 3 { 25 | eprintln!("{} ", program); 26 | return; 27 | } 28 | 29 | if let Err(err) = process(&args[1], &args[2]) { 30 | eprintln!("{}", err) 31 | } 32 | } 33 | --------------------------------------------------------------------------------