From 0400633010336910be93a144a1e7a4457ee83c55 Mon Sep 17 00:00:00 2001 From: Ahmed Al Hafoudh Date: Wed, 5 Feb 2025 22:29:37 +0100 Subject: [PATCH 1/5] Implement preopened_dir using set_mapped_directory setting Add support for directory and file permissions --- ext/src/ruby_api/wasi_config.rs | 105 +++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/ext/src/ruby_api/wasi_config.rs b/ext/src/ruby_api/wasi_config.rs index 7b028dd0..442ace98 100644 --- a/ext/src/ruby_api/wasi_config.rs +++ b/ext/src/ruby_api/wasi_config.rs @@ -3,14 +3,17 @@ use crate::error; use crate::helpers::OutputLimitedBuffer; use magnus::{ class, function, gc::Marker, method, typed_data::Obj, value::Opaque, DataTypeFunctions, Error, - Module, Object, RArray, RHash, RString, Ruby, TryConvert, TypedData, + Module, Object, RArray, RHash, RString, Ruby, Symbol, TryConvert, TypedData, }; +use rb_sys::ruby_rarray_flags::RARRAY_EMBED_FLAG; use std::cell::RefCell; use std::fs; +use std::path::Path; use std::{fs::File, path::PathBuf}; use wasmtime_wasi::p2::pipe::MemoryInputPipe; use wasmtime_wasi::p2::{OutputFile, WasiCtx, WasiCtxBuilder}; use wasmtime_wasi::preview1::WasiP1Ctx; +use wasmtime_wasi::{DirPerms, FilePerms}; enum ReadStream { Inherit, @@ -51,6 +54,7 @@ struct WasiConfigInner { env: Option>, args: Option>, deterministic: bool, + mapped_directories: Option>, } impl WasiConfigInner { @@ -70,6 +74,9 @@ impl WasiConfigInner { if let Some(v) = self.args.as_ref() { marker.mark(*v); } + if let Some(v) = self.mapped_directories.as_ref() { + marker.mark(*v); + } } } @@ -233,6 +240,40 @@ impl WasiConfig { rb_self } + /// @yard + /// Set mapped directory for host path and guest path. + /// @param host_path [String] + /// @param guest_path [String] + /// @param dir_perms [Symbol] Directory permissions, one of :read, :mutate, or :all + /// @param file_perms [Symbol] File permissions, one of :read, :write, or :all + /// @def set_mapped_directory(host_path, guest_path, dir_perms, file_perms) + /// @return [WasiConfig] +self+ + pub fn set_mapped_directory( + rb_self: RbSelf, + host_path: RString, + guest_path: RString, + dir_perms: Symbol, + file_perms: Symbol, + ) -> RbSelf { + let mapped_directory = RArray::new(); + mapped_directory.push(host_path).unwrap(); + mapped_directory.push(guest_path).unwrap(); + mapped_directory.push(dir_perms).unwrap(); + mapped_directory.push(file_perms).unwrap(); + + let init_directory = RArray::new(); + + let mut inner = rb_self.inner.borrow_mut(); + if inner.mapped_directories.is_none() { + inner.mapped_directories = Some(init_directory.into()); + } + + let ruby = Ruby::get().unwrap(); + let mapped_directories = ruby.get_inner(inner.mapped_directories.unwrap()); + mapped_directories.push(mapped_directory).unwrap(); + rb_self + } + pub fn build_p1(&self, ruby: &Ruby) -> Result { let mut builder = self.build_impl(ruby)?; let ctx = builder.build_p1(); @@ -317,6 +358,63 @@ impl WasiConfig { deterministic_wasi_ctx::add_determinism_to_wasi_ctx_builder(&mut builder); } + if let Some(mapped_directories) = inner.mapped_directories.as_ref() { + for item in unsafe { ruby.get_inner(*mapped_directories).as_slice() } { + let mapped_directory = RArray::try_convert(*item)?; + if mapped_directory.len() == 4 { + let host_path = + RString::try_convert(mapped_directory.entry(0)?)?.to_string()?; + let guest_path = + RString::try_convert(mapped_directory.entry(1)?)?.to_string()?; + let dir_perms = Symbol::from_value(mapped_directory.entry(2)?) + .unwrap() + .name()?; + let file_perms = Symbol::from_value(mapped_directory.entry(3)?) + .unwrap() + .name()?; + + let host_path_dir = Path::new(&host_path); + let guest_path_path = guest_path.as_str(); + + // Convert to FilePerms and DirPerms enums + let dir_perms_flags; + match dir_perms { + std::borrow::Cow::Borrowed("read") => dir_perms_flags = DirPerms::READ, + std::borrow::Cow::Borrowed("mutate") => dir_perms_flags = DirPerms::MUTATE, + std::borrow::Cow::Borrowed("all") => dir_perms_flags = DirPerms::all(), + _ => { + return Err(error!( + "Invalid dir_perms: {}. Use one of :read, :mutate, or :all", + dir_perms + )) + } + } + + let file_perms_flags; + match file_perms { + std::borrow::Cow::Borrowed("read") => file_perms_flags = FilePerms::READ, + std::borrow::Cow::Borrowed("write") => file_perms_flags = FilePerms::WRITE, + std::borrow::Cow::Borrowed("all") => file_perms_flags = FilePerms::all(), + _ => { + return Err(error!( + "Invalid file_perms: {}. Use one of :read, :write, or :all", + file_perms + )) + } + } + + builder + .preopened_dir( + host_path_dir, + guest_path_path, + dir_perms_flags, + file_perms_flags, + ) + .map_err(|e| error!("{}", e))?; + } + } + } + Ok(builder) } } @@ -355,5 +453,10 @@ pub fn init() -> Result<(), Error> { class.define_method("set_argv", method!(WasiConfig::set_argv, 1))?; + class.define_method( + "set_mapped_directory", + method!(WasiConfig::set_mapped_directory, 4), + )?; + Ok(()) } From cea0c26619f1f2c0598b13faadff6af281a1abf3 Mon Sep 17 00:00:00 2001 From: william-stacken Date: Thu, 4 Sep 2025 13:40:39 +0200 Subject: [PATCH 2/5] Add tests for mapped directories --- spec/fixtures/wasi-fs-p2.wasm | Bin 0 -> 132463 bytes spec/fixtures/wasi-fs.wasm | Bin 0 -> 65988 bytes spec/fixtures/wasi-fs/.cargo/config.toml | 2 + spec/fixtures/wasi-fs/Cargo.toml | 10 ++ spec/fixtures/wasi-fs/README.md | 14 +++ spec/fixtures/wasi-fs/src/main.rs | 18 ++++ spec/unit/wasi_spec.rb | 111 +++++++++++++++++++++++ 7 files changed, 155 insertions(+) create mode 100644 spec/fixtures/wasi-fs-p2.wasm create mode 100644 spec/fixtures/wasi-fs.wasm create mode 100644 spec/fixtures/wasi-fs/.cargo/config.toml create mode 100644 spec/fixtures/wasi-fs/Cargo.toml create mode 100644 spec/fixtures/wasi-fs/README.md create mode 100644 spec/fixtures/wasi-fs/src/main.rs diff --git a/spec/fixtures/wasi-fs-p2.wasm b/spec/fixtures/wasi-fs-p2.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6d01a17c73e0c96f8322214b325768ade8c9fc11 GIT binary patch literal 132463 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 jt8B^WNUR3=;i`g!it~f?qe2a30h3jZaY{?+$A = std::env::args().collect(); + + let counter_file = File::open(&args[1]).expect("failed to open counter file"); + + let mut counter_str = String::new(); + counter_file.take(100).read_to_string(&mut counter_str) + .expect("failed to read counter file"); + let mut counter: u32 = counter_str.trim().parse().expect("failed to parse counter"); + + counter += 1; + + let mut counter_file = File::create(&args[1]).expect("failed to create counter file"); + write!(counter_file, "{}", counter).expect("failed to write counter file"); +} diff --git a/spec/unit/wasi_spec.rb b/spec/unit/wasi_spec.rb index 2d1a9b3f..f7c483d4 100644 --- a/spec/unit/wasi_spec.rb +++ b/spec/unit/wasi_spec.rb @@ -12,9 +12,11 @@ module Wasmtime # Compile module only once for speed @compiled_wasi_module = @engine.precompile_module(IO.binread("spec/fixtures/wasi-debug.wasm")) @compiled_wasi_deterministic_module = @engine.precompile_module(IO.binread("spec/fixtures/wasi-deterministic.wasm")) + @compiled_wasi_fs_module = @engine.precompile_module(IO.binread("spec/fixtures/wasi-fs.wasm")) @compiled_wasi_component = @engine.precompile_component(IO.binread("spec/fixtures/wasi-debug-p2.wasm")) @compiled_wasi_deterministic_component = @engine.precompile_component(IO.binread("spec/fixtures/wasi-deterministic-p2.wasm")) + @compiled_wasi_fs_component = @engine.precompile_component(IO.binread("spec/fixtures/wasi-fs-p2.wasm")) end describe "Linker.new" do @@ -233,6 +235,95 @@ module Wasmtime end end end + + it "writes to mapped directory" do + Dir.mkdir(tempfile_path("tmp")) + File.write(tempfile_path(File.join("tmp", "counter")), "0") + + wasi_config = WasiConfig.new + .set_argv(["wasi-fs", "/tmp/counter"]) + .set_mapped_directory(tempfile_path("tmp"), "/tmp", :all, :all) + + expect { run_fs.call(wasi_config) }.not_to raise_error + + expect(File.read(tempfile_path(File.join("tmp", "counter")))).to eq("1") + end + + it "fails to write to mapped directory if not permitted" do + Dir.mkdir(tempfile_path("tmp")) + File.write(tempfile_path(File.join("tmp", "counter")), "0") + + stderr_str = "" + wasi_config = WasiConfig.new + .set_argv(["wasi-fs", "/tmp/counter"]) + .set_stderr_buffer(stderr_str, 40000) + .set_mapped_directory(tempfile_path("tmp"), "/tmp", :read, :read) + + expect { run_fs.call(wasi_config) }.to raise_error do |error| + expect(error).to be_a(Wasmtime::Error) + end + + expect(stderr_str).to match(/failed to create counter file/) + + expect(File.read(tempfile_path(File.join("tmp", "counter")))).to eq("0") + end + + it "fails to read from mapped directory if not permitted" do + Dir.mkdir(tempfile_path("tmp")) + File.write(tempfile_path(File.join("tmp", "counter")), "0") + + stderr_str = "" + wasi_config = WasiConfig.new + .set_argv(["wasi-fs", "/tmp/counter"]) + .set_stderr_buffer(stderr_str, 40000) + .set_mapped_directory(tempfile_path("tmp"), "/tmp", :mutate, :write) + + expect { run_fs.call(wasi_config) }.to raise_error do |error| + expect(error).to be_a(Wasmtime::Error) + end + + expect(stderr_str).to match(/failed to open counter file/) + + expect(File.read(tempfile_path(File.join("tmp", "counter")))).to eq("0") + end + + it "fails to access non-mapped directories" do + Dir.mkdir(tempfile_path("tmp")) + File.write(tempfile_path(File.join("tmp", "counter")), "0") + + stderr_str = "" + wasi_config = WasiConfig.new + .set_argv(["wasi-fs", File.join(tempfile_path("tmp"), "counter")]) + .set_stderr_buffer(stderr_str, 40000) + + expect { run_fs.call(wasi_config) }.to raise_error do |error| + expect(error).to be_a(Wasmtime::Error) + end + + expect(stderr_str).to match(/failed to find a pre-opened file descriptor/) + + expect(File.read(tempfile_path(File.join("tmp", "counter")))).to eq("0") + end + + it "does not accept an invalid host path" do + wasi_config = WasiConfig.new + .set_mapped_directory(tempfile_path("tmp"), "/tmp", :all, :all) + + expect { run_fs.call(wasi_config) }.to raise_error do |error| + expect(error).to be_a(Wasmtime::Error) + # error message is os-specific + end + end + + it "does not accept invalid permissions" do + wasi_config = WasiConfig.new + .set_mapped_directory(tempfile_path("tmp"), "/tmp", :mutate, :invalid_permission) + + expect { run_fs.call(wasi_config) }.to raise_error do |error| + expect(error).to be_a(Wasmtime::Error) + expect(error.message).to match(/Invalid file_perms: invalid_permission. Use one of :read, :write, or :all/) + end + end end describe "WasiConfig preview 1" do @@ -240,6 +331,7 @@ module Wasmtime let(:run) { method(:run_wasi_module) } let(:wasi_env) { method(:wasi_module_env) } let(:run_deterministic) { method(:run_wasi_module_deterministic) } + let(:run_fs) { method(:run_wasi_module_fs) } end end @@ -248,6 +340,7 @@ module Wasmtime let(:run) { method(:run_wasi_component) } let(:wasi_env) { method(:wasi_component_env) } let(:run_deterministic) { method(:run_wasi_component_deterministic) } + let(:run_fs) { method(:run_wasi_component_fs) } end end @@ -272,6 +365,13 @@ def run_wasi_module_deterministic(wasi_config) .invoke("_start") end + def run_wasi_module_fs(wasi_config) + linker = Linker.new(@engine) + WASI::P1.add_to_linker_sync(linker) + store = Store.new(@engine, wasi_p1_config: wasi_config) + linker.instantiate(store, Module.deserialize(@engine, @compiled_wasi_fs_module)).invoke("_start") + end + def wasi_module_env stdout_file = tempfile_path("stdout") @@ -318,6 +418,17 @@ def run_wasi_component_deterministic(wasi_config) ).call_run(store) end + def run_wasi_component_fs(wasi_config) + linker = Component::Linker.new(@engine) + WASI::P2.add_to_linker_sync(linker) + store = Store.new(@engine, wasi_config: wasi_config) + Component::WasiCommand.new( + store, + Component::Component.deserialize(@engine, @compiled_wasi_fs_component), + linker + ).call_run(store) + end + def tempfile_path(name) File.join(tmpdir, name) end From be6813a189cbfa940a0f5d3205e62982e9021239 Mon Sep 17 00:00:00 2001 From: william-stacken Date: Mon, 8 Sep 2025 10:36:04 +0200 Subject: [PATCH 3/5] Use rust struct instead of nested RArrays --- ext/src/ruby_api/wasi_config.rs | 128 ++++++++++++++------------------ spec/unit/wasi_spec.rb | 8 +- 2 files changed, 59 insertions(+), 77 deletions(-) diff --git a/ext/src/ruby_api/wasi_config.rs b/ext/src/ruby_api/wasi_config.rs index 442ace98..ea237aff 100644 --- a/ext/src/ruby_api/wasi_config.rs +++ b/ext/src/ruby_api/wasi_config.rs @@ -15,6 +15,14 @@ use wasmtime_wasi::p2::{OutputFile, WasiCtx, WasiCtxBuilder}; use wasmtime_wasi::preview1::WasiP1Ctx; use wasmtime_wasi::{DirPerms, FilePerms}; +#[derive(Clone)] +struct MappedDirectory { + host_path: String, + guest_path: String, + dir_perms: DirPerms, + file_perms: FilePerms, +} + enum ReadStream { Inherit, Path(Opaque), @@ -54,7 +62,7 @@ struct WasiConfigInner { env: Option>, args: Option>, deterministic: bool, - mapped_directories: Option>, + mapped_directories: Vec, } impl WasiConfigInner { @@ -74,9 +82,6 @@ impl WasiConfigInner { if let Some(v) = self.args.as_ref() { marker.mark(*v); } - if let Some(v) = self.mapped_directories.as_ref() { - marker.mark(*v); - } } } @@ -254,24 +259,47 @@ impl WasiConfig { guest_path: RString, dir_perms: Symbol, file_perms: Symbol, - ) -> RbSelf { - let mapped_directory = RArray::new(); - mapped_directory.push(host_path).unwrap(); - mapped_directory.push(guest_path).unwrap(); - mapped_directory.push(dir_perms).unwrap(); - mapped_directory.push(file_perms).unwrap(); + ) -> Result { + let host_path = host_path.to_string().unwrap(); + let guest_path = guest_path.to_string().unwrap(); + let dir_perms_str = dir_perms.name().unwrap().to_string(); + let file_perms_str = file_perms.name().unwrap().to_string(); + + let dir_perms = match dir_perms_str.as_str() { + "read" => DirPerms::READ, + "mutate" => DirPerms::MUTATE, + "all" => DirPerms::all(), + _ => { + return Err(error!( + "Invalid dir_perms: {}. Use one of :read, :mutate, or :all", + dir_perms_str + )) + } + }; + + let file_perms = match file_perms_str.as_str() { + "read" => FilePerms::READ, + "write" => FilePerms::WRITE, + "all" => FilePerms::all(), + _ => { + return Err(error!( + "Invalid file_perms: {}. Use one of :read, :write, or :all", + file_perms_str + )) + } + }; - let init_directory = RArray::new(); + let mapped_dir = MappedDirectory { + host_path, + guest_path, + dir_perms, + file_perms, + }; let mut inner = rb_self.inner.borrow_mut(); - if inner.mapped_directories.is_none() { - inner.mapped_directories = Some(init_directory.into()); - } + inner.mapped_directories.push(mapped_dir); - let ruby = Ruby::get().unwrap(); - let mapped_directories = ruby.get_inner(inner.mapped_directories.unwrap()); - mapped_directories.push(mapped_directory).unwrap(); - rb_self + Ok(rb_self) } pub fn build_p1(&self, ruby: &Ruby) -> Result { @@ -358,61 +386,15 @@ impl WasiConfig { deterministic_wasi_ctx::add_determinism_to_wasi_ctx_builder(&mut builder); } - if let Some(mapped_directories) = inner.mapped_directories.as_ref() { - for item in unsafe { ruby.get_inner(*mapped_directories).as_slice() } { - let mapped_directory = RArray::try_convert(*item)?; - if mapped_directory.len() == 4 { - let host_path = - RString::try_convert(mapped_directory.entry(0)?)?.to_string()?; - let guest_path = - RString::try_convert(mapped_directory.entry(1)?)?.to_string()?; - let dir_perms = Symbol::from_value(mapped_directory.entry(2)?) - .unwrap() - .name()?; - let file_perms = Symbol::from_value(mapped_directory.entry(3)?) - .unwrap() - .name()?; - - let host_path_dir = Path::new(&host_path); - let guest_path_path = guest_path.as_str(); - - // Convert to FilePerms and DirPerms enums - let dir_perms_flags; - match dir_perms { - std::borrow::Cow::Borrowed("read") => dir_perms_flags = DirPerms::READ, - std::borrow::Cow::Borrowed("mutate") => dir_perms_flags = DirPerms::MUTATE, - std::borrow::Cow::Borrowed("all") => dir_perms_flags = DirPerms::all(), - _ => { - return Err(error!( - "Invalid dir_perms: {}. Use one of :read, :mutate, or :all", - dir_perms - )) - } - } - - let file_perms_flags; - match file_perms { - std::borrow::Cow::Borrowed("read") => file_perms_flags = FilePerms::READ, - std::borrow::Cow::Borrowed("write") => file_perms_flags = FilePerms::WRITE, - std::borrow::Cow::Borrowed("all") => file_perms_flags = FilePerms::all(), - _ => { - return Err(error!( - "Invalid file_perms: {}. Use one of :read, :write, or :all", - file_perms - )) - } - } - - builder - .preopened_dir( - host_path_dir, - guest_path_path, - dir_perms_flags, - file_perms_flags, - ) - .map_err(|e| error!("{}", e))?; - } - } + for mapped_dir in &inner.mapped_directories { + builder + .preopened_dir( + Path::new(&mapped_dir.host_path), + &mapped_dir.guest_path, + mapped_dir.dir_perms, + mapped_dir.file_perms, + ) + .map_err(|e| error!("{}", e))?; } Ok(builder) diff --git a/spec/unit/wasi_spec.rb b/spec/unit/wasi_spec.rb index f7c483d4..976fbf6f 100644 --- a/spec/unit/wasi_spec.rb +++ b/spec/unit/wasi_spec.rb @@ -316,10 +316,10 @@ module Wasmtime end it "does not accept invalid permissions" do - wasi_config = WasiConfig.new - .set_mapped_directory(tempfile_path("tmp"), "/tmp", :mutate, :invalid_permission) - - expect { run_fs.call(wasi_config) }.to raise_error do |error| + expect { + WasiConfig.new + .set_mapped_directory(tempfile_path("tmp"), "/tmp", :mutate, :invalid_permission) + }.to raise_error do |error| expect(error).to be_a(Wasmtime::Error) expect(error.message).to match(/Invalid file_perms: invalid_permission. Use one of :read, :write, or :all/) end From 4bd4e3885409b1049e24b505ef338608fc2248fe Mon Sep 17 00:00:00 2001 From: william-stacken Date: Mon, 8 Sep 2025 11:47:32 +0200 Subject: [PATCH 4/5] Model enums as SymbolEnums --- ext/src/ruby_api/wasi_config.rs | 78 +++++++++++++++++++++------------ spec/unit/wasi_spec.rb | 4 +- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/ext/src/ruby_api/wasi_config.rs b/ext/src/ruby_api/wasi_config.rs index ea237aff..ab03ecf1 100644 --- a/ext/src/ruby_api/wasi_config.rs +++ b/ext/src/ruby_api/wasi_config.rs @@ -1,12 +1,16 @@ use super::root; use crate::error; use crate::helpers::OutputLimitedBuffer; +use crate::ruby_api::convert::ToValType; +use crate::{define_rb_intern, helpers::SymbolEnum}; +use lazy_static::lazy_static; use magnus::{ class, function, gc::Marker, method, typed_data::Obj, value::Opaque, DataTypeFunctions, Error, - Module, Object, RArray, RHash, RString, Ruby, Symbol, TryConvert, TypedData, + IntoValue, Module, Object, RArray, RHash, RString, Ruby, Symbol, TryConvert, TypedData, Value, }; use rb_sys::ruby_rarray_flags::RARRAY_EMBED_FLAG; use std::cell::RefCell; +use std::convert::TryFrom; use std::fs; use std::path::Path; use std::{fs::File, path::PathBuf}; @@ -15,6 +19,36 @@ use wasmtime_wasi::p2::{OutputFile, WasiCtx, WasiCtxBuilder}; use wasmtime_wasi::preview1::WasiP1Ctx; use wasmtime_wasi::{DirPerms, FilePerms}; +define_rb_intern!( + READ => "read", + WRITE => "write", + MUTATE => "mutate", + ALL => "all", +); + +lazy_static! { + static ref FILE_PERMS_MAPPING: SymbolEnum<'static, FilePerms> = { + let mapping = vec![ + (*READ, FilePerms::READ), + (*WRITE, FilePerms::WRITE), + (*ALL, FilePerms::all()), + ]; + + SymbolEnum::new(":file_perms", mapping) + }; + static ref DIR_PERMS_MAPPING: SymbolEnum<'static, DirPerms> = { + let mapping = vec![ + (*READ, DirPerms::READ), + (*MUTATE, DirPerms::MUTATE), + (*ALL, DirPerms::all()), + ]; + + SymbolEnum::new(":dir_perms", mapping) + }; +} + +struct PermsSymbolEnum(Symbol); + #[derive(Clone)] struct MappedDirectory { host_path: String, @@ -85,6 +119,20 @@ impl WasiConfigInner { } } +impl TryFrom for DirPerms { + type Error = magnus::Error; + fn try_from(value: PermsSymbolEnum) -> Result { + DIR_PERMS_MAPPING.get(value.0.into_value()) + } +} + +impl TryFrom for FilePerms { + type Error = magnus::Error; + fn try_from(value: PermsSymbolEnum) -> Result { + FILE_PERMS_MAPPING.get(value.0.into_value()) + } +} + /// @yard /// WASI config to be sent as {Store#new}’s +wasi_config+ keyword argument. /// @@ -262,32 +310,8 @@ impl WasiConfig { ) -> Result { let host_path = host_path.to_string().unwrap(); let guest_path = guest_path.to_string().unwrap(); - let dir_perms_str = dir_perms.name().unwrap().to_string(); - let file_perms_str = file_perms.name().unwrap().to_string(); - - let dir_perms = match dir_perms_str.as_str() { - "read" => DirPerms::READ, - "mutate" => DirPerms::MUTATE, - "all" => DirPerms::all(), - _ => { - return Err(error!( - "Invalid dir_perms: {}. Use one of :read, :mutate, or :all", - dir_perms_str - )) - } - }; - - let file_perms = match file_perms_str.as_str() { - "read" => FilePerms::READ, - "write" => FilePerms::WRITE, - "all" => FilePerms::all(), - _ => { - return Err(error!( - "Invalid file_perms: {}. Use one of :read, :write, or :all", - file_perms_str - )) - } - }; + let dir_perms: DirPerms = PermsSymbolEnum(dir_perms).try_into()?; + let file_perms: FilePerms = PermsSymbolEnum(file_perms).try_into()?; let mapped_dir = MappedDirectory { host_path, diff --git a/spec/unit/wasi_spec.rb b/spec/unit/wasi_spec.rb index 976fbf6f..d6f44efc 100644 --- a/spec/unit/wasi_spec.rb +++ b/spec/unit/wasi_spec.rb @@ -320,8 +320,8 @@ module Wasmtime WasiConfig.new .set_mapped_directory(tempfile_path("tmp"), "/tmp", :mutate, :invalid_permission) }.to raise_error do |error| - expect(error).to be_a(Wasmtime::Error) - expect(error.message).to match(/Invalid file_perms: invalid_permission. Use one of :read, :write, or :all/) + expect(error).to be_a(ArgumentError) + expect(error.message).to match(/invalid :file_perms, expected one of \[:read, :write, :all\], got :invalid_permission/) end end end From f4f74478e76e1b7c7d118a23eedec6be99768388 Mon Sep 17 00:00:00 2001 From: william-stacken Date: Tue, 9 Sep 2025 11:36:55 +0200 Subject: [PATCH 5/5] Lazily convert ruby MappedDirectory values to rust --- ext/src/ruby_api/wasi_config.rs | 61 +++++++++++++++++++-------------- spec/unit/wasi_spec.rb | 8 ++--- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/ext/src/ruby_api/wasi_config.rs b/ext/src/ruby_api/wasi_config.rs index ab03ecf1..4990045e 100644 --- a/ext/src/ruby_api/wasi_config.rs +++ b/ext/src/ruby_api/wasi_config.rs @@ -47,16 +47,6 @@ lazy_static! { }; } -struct PermsSymbolEnum(Symbol); - -#[derive(Clone)] -struct MappedDirectory { - host_path: String, - guest_path: String, - dir_perms: DirPerms, - file_perms: FilePerms, -} - enum ReadStream { Inherit, Path(Opaque), @@ -88,6 +78,24 @@ impl WriteStream { } } +struct PermsSymbolEnum(Symbol); + +#[derive(Clone)] +struct MappedDirectory { + host_path: Opaque, + guest_path: Opaque, + dir_perms: Opaque, + file_perms: Opaque, +} +impl MappedDirectory { + pub fn mark(&self, marker: &Marker) { + marker.mark(self.host_path); + marker.mark(self.guest_path); + marker.mark(self.dir_perms); + marker.mark(self.file_perms); + } +} + #[derive(Default)] struct WasiConfigInner { stdin: Option, @@ -116,6 +124,9 @@ impl WasiConfigInner { if let Some(v) = self.args.as_ref() { marker.mark(*v); } + for v in &self.mapped_directories { + v.mark(marker); + } } } @@ -307,23 +318,18 @@ impl WasiConfig { guest_path: RString, dir_perms: Symbol, file_perms: Symbol, - ) -> Result { - let host_path = host_path.to_string().unwrap(); - let guest_path = guest_path.to_string().unwrap(); - let dir_perms: DirPerms = PermsSymbolEnum(dir_perms).try_into()?; - let file_perms: FilePerms = PermsSymbolEnum(file_perms).try_into()?; - + ) -> RbSelf { let mapped_dir = MappedDirectory { - host_path, - guest_path, - dir_perms, - file_perms, + host_path: host_path.into(), + guest_path: guest_path.into(), + dir_perms: dir_perms.into(), + file_perms: file_perms.into(), }; let mut inner = rb_self.inner.borrow_mut(); inner.mapped_directories.push(mapped_dir); - Ok(rb_self) + rb_self } pub fn build_p1(&self, ruby: &Ruby) -> Result { @@ -411,12 +417,17 @@ impl WasiConfig { } for mapped_dir in &inner.mapped_directories { + let host_path = ruby.get_inner(mapped_dir.host_path).to_string()?; + let guest_path = ruby.get_inner(mapped_dir.guest_path).to_string()?; + let dir_perms = ruby.get_inner(mapped_dir.dir_perms); + let file_perms = ruby.get_inner(mapped_dir.file_perms); + builder .preopened_dir( - Path::new(&mapped_dir.host_path), - &mapped_dir.guest_path, - mapped_dir.dir_perms, - mapped_dir.file_perms, + Path::new(&host_path), + &guest_path, + PermsSymbolEnum(dir_perms).try_into()?, + PermsSymbolEnum(file_perms).try_into()?, ) .map_err(|e| error!("{}", e))?; } diff --git a/spec/unit/wasi_spec.rb b/spec/unit/wasi_spec.rb index d6f44efc..de0340fc 100644 --- a/spec/unit/wasi_spec.rb +++ b/spec/unit/wasi_spec.rb @@ -316,10 +316,10 @@ module Wasmtime end it "does not accept invalid permissions" do - expect { - WasiConfig.new - .set_mapped_directory(tempfile_path("tmp"), "/tmp", :mutate, :invalid_permission) - }.to raise_error do |error| + wasi_config = WasiConfig.new + .set_mapped_directory(tempfile_path("tmp"), "/tmp", :mutate, :invalid_permission) + + expect { run_fs.call(wasi_config) }.to raise_error do |error| expect(error).to be_a(ArgumentError) expect(error.message).to match(/invalid :file_perms, expected one of \[:read, :write, :all\], got :invalid_permission/) end