microsandbox-rb 0.5.10 → 0.5.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4d9e8db2dddac7a22c2910c82a56603e95ad702d328c6a020f514997d29859c
4
- data.tar.gz: 5f19089f8d1d0f18d5d930cf49576d25862f02323495ffa02db683f57a3439cd
3
+ metadata.gz: a2d0013cf55f89aa21492847859554937ff838d5d38d4f5f34d0e8da473c9554
4
+ data.tar.gz: 95bd9d994905567facd356597a8fee6e7f25715a485edbbc48bf98bb09f90eb3
5
5
  SHA512:
6
- metadata.gz: 88e44654909dd3f39ac23c6cf682bd62519bbaf7f57ca24f611a66e05bb66c825919fc1f2be6f9044513c99508bfedd412e5c72c1f15fc8b66c3ac11ee2b78ac
7
- data.tar.gz: 00b0f44a4568107891a2ce9a623950245457ea51d0700f45333b96fec7eb73e6572b8b9b67b4147490423687fa1ff7dcb96b6edd29c2aa28c3ed22807eab7bdb
6
+ metadata.gz: 337de8ed90dfea5b44d959579a16dafdd736e33fbfff7e10ed8ad4678184f84bdf417f33d85a7a8f11ab69a4785a91c381ea719600769b804d9a71c1c048adb1
7
+ data.tar.gz: 672d7c7945e3a5561ad9934bc682b5f9764d1873d4235105e33ca1494a0079d11e574d520ed0e1f90bb66049264cdc783180448d2f456c78185024d9f18b186d
data/CHANGELOG.md CHANGED
@@ -6,6 +6,20 @@ upstream microsandbox runtime.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.11] - 2026-06-23
10
+
11
+ ### Added
12
+
13
+ - **Read-only / mount-option passthrough for volumes.** A volume spec Hash may
14
+ now carry `ro:`/`readonly:`, `noexec:`, `nosuid:`, `nodev:`, or an explicit
15
+ `options:` array, e.g. `volumes: { "/repos" => { bind: "/host/repos", ro: true } }`.
16
+ The Ruby layer appends a 4th comma-joined options element to the normalized
17
+ mount triple and the native ext applies the matching `MountBuilder` flags. RO is
18
+ enforced both host-side (virtiofs rejects writes) and guest-side (kernel returns
19
+ `EROFS`). Previously the gem could only request read-write mounts, so callers had
20
+ to fake read-only with host `chmod -R a-w`. Backward compatible: String specs and
21
+ option-less Hash specs serialize to the exact same 3-element triple as before.
22
+
9
23
  ## [0.5.10] - 2026-06-22
10
24
 
11
25
  ### Added
data/Cargo.lock CHANGED
@@ -3249,7 +3249,7 @@ dependencies = [
3249
3249
 
3250
3250
  [[package]]
3251
3251
  name = "microsandbox_rb"
3252
- version = "0.5.10"
3252
+ version = "0.5.11"
3253
3253
  dependencies = [
3254
3254
  "chrono",
3255
3255
  "futures",
@@ -7,7 +7,7 @@ description = "Ruby SDK native extension for microsandbox — secure, fast micro
7
7
  # Must equal Microsandbox::VERSION (lib/microsandbox/version.rb) — Native.version
8
8
  # returns this via env!("CARGO_PKG_VERSION") and version_spec.rb asserts equality.
9
9
  # The core-crate dependency below stays pinned at its own tag (v0.5.8).
10
- version = "0.5.10"
10
+ version = "0.5.11"
11
11
  authors = ["Super Rad Company <development@superrad.company>"]
12
12
  repository = "https://github.com/superradcompany/microsandbox"
13
13
  license = "Apache-2.0"
@@ -89,9 +89,11 @@ impl Sandbox {
89
89
  for (host, guest) in conv::opt_port_map(opts, "ports")? {
90
90
  b = b.port(host, guest);
91
91
  }
92
- // volumes: normalized by the Ruby layer to [guest, kind, source] triples.
92
+ // volumes: normalized by the Ruby layer to [guest, kind, source] triples,
93
+ // or [guest, kind, source, options] quads where options is a comma-joined
94
+ // list (ro/readonly, rw, noexec, nosuid, nodev).
93
95
  for spec in conv::opt::<Vec<Vec<String>>>(opts, "volumes")?.unwrap_or_default() {
94
- if spec.len() != 3 {
96
+ if spec.len() < 3 || spec.len() > 4 {
95
97
  return Err(error::base_error("invalid volume mount spec"));
96
98
  }
97
99
  let (guest, kind, source) = (spec[0].clone(), spec[1].clone(), spec[2].clone());
@@ -103,12 +105,43 @@ impl Sandbox {
103
105
  )))
104
106
  }
105
107
  }
108
+ // Validate options up front (the volume closure cannot return an error).
109
+ let mount_opts: Vec<String> = spec
110
+ .get(3)
111
+ .map(|s| {
112
+ s.split(',')
113
+ .map(|o| o.trim().to_string())
114
+ .filter(|o| !o.is_empty())
115
+ .collect()
116
+ })
117
+ .unwrap_or_default();
118
+ for opt in &mount_opts {
119
+ match opt.as_str() {
120
+ "ro" | "readonly" | "rw" | "noexec" | "nosuid" | "nodev" => {}
121
+ other => {
122
+ return Err(error::base_error(format!(
123
+ "unknown volume mount option {other:?} \
124
+ (expected ro/rw/noexec/nosuid/nodev)"
125
+ )))
126
+ }
127
+ }
128
+ }
106
129
  b = b.volume(guest, move |m| {
107
- if kind == "named" {
130
+ let mut m = if kind == "named" {
108
131
  m.named(source)
109
132
  } else {
110
133
  m.bind(source)
134
+ };
135
+ for opt in &mount_opts {
136
+ m = match opt.as_str() {
137
+ "ro" | "readonly" => m.readonly(),
138
+ "noexec" => m.noexec(),
139
+ "nosuid" => m.nosuid(),
140
+ "nodev" => m.nodev(),
141
+ _ => m, // "rw" — default; already validated above
142
+ };
111
143
  }
144
+ m
112
145
  });
113
146
  }
114
147
  // patches: rootfs modifications applied before boot. The Ruby layer
@@ -387,8 +387,10 @@ module Microsandbox
387
387
  end
388
388
 
389
389
  # Normalize volumes (Hash of guest_path => spec) into [guest, kind, source]
390
- # triples for the native layer. A spec is a host path String (bind mount),
391
- # or a Hash { bind: "/host" } / { named: "volume-name" }.
390
+ # triples (or [guest, kind, source, options] quads) for the native layer. A
391
+ # spec is a host path String (read-write bind mount), or a Hash
392
+ # { bind: "/host" } / { named: "volume-name" } optionally carrying mount
393
+ # options: { bind: "/host", ro: true } / { named: "v", options: %w[ro noexec] }.
392
394
  def normalize_volumes(volumes)
393
395
  volumes.map do |guest, spec|
394
396
  guest = guest.to_s
@@ -396,19 +398,39 @@ module Microsandbox
396
398
  when String
397
399
  [guest, "bind", spec]
398
400
  when Hash
399
- if (named = spec[:named] || spec["named"])
400
- [guest, "named", named.to_s]
401
- elsif (bind = spec[:bind] || spec["bind"])
402
- [guest, "bind", bind.to_s]
403
- else
404
- raise ArgumentError, "volume spec for #{guest.inspect} needs :bind or :named"
405
- end
401
+ triple =
402
+ if (named = spec[:named] || spec["named"])
403
+ [guest, "named", named.to_s]
404
+ elsif (bind = spec[:bind] || spec["bind"])
405
+ [guest, "bind", bind.to_s]
406
+ else
407
+ raise ArgumentError, "volume spec for #{guest.inspect} needs :bind or :named"
408
+ end
409
+ # Optional 4th element: comma-joined mount options. Omitted when empty
410
+ # so existing [guest,kind,source] consumers and the common read-write
411
+ # case are byte-for-byte unchanged.
412
+ opts = mount_options(spec)
413
+ opts.empty? ? triple : triple + [opts.join(",")]
406
414
  else
407
415
  raise ArgumentError, "invalid volume spec for #{guest.inspect}: #{spec.inspect}"
408
416
  end
409
417
  end
410
418
  end
411
419
 
420
+ # Collect mount options from a volume spec Hash. `ro:`/`readonly:` makes the
421
+ # mount read-only (host virtiofs rejects writes + guest kernel returns EROFS);
422
+ # `noexec:`/`nosuid:`/`nodev:` set the matching flags; an explicit `options:`
423
+ # array passes through verbatim. Unknown options are rejected natively.
424
+ def mount_options(spec)
425
+ opts = []
426
+ opts << "ro" if spec[:ro] || spec["ro"] || spec[:readonly] || spec["readonly"]
427
+ opts << "noexec" if spec[:noexec] || spec["noexec"]
428
+ opts << "nosuid" if spec[:nosuid] || spec["nosuid"]
429
+ opts << "nodev" if spec[:nodev] || spec["nodev"]
430
+ Array(spec[:options] || spec["options"]).each { |o| opts << o.to_s }
431
+ opts.uniq
432
+ end
433
+
412
434
  # Normalize a list of patches (each a Hash from the {Patch} factory, or a
413
435
  # plain Hash) into string-keyed Hashes for the native layer. Values are
414
436
  # passed through unchanged (mode stays Integer, content stays String).
@@ -5,5 +5,5 @@ module Microsandbox
5
5
  # the pinned core-crate tag); the patch segment advances for gem-only revisions
6
6
  # that add bindings atop the same core. Must equal the native ext's Cargo crate
7
7
  # version (`Native.version`), enforced by spec/unit/version_spec.rb.
8
- VERSION = "0.5.10"
8
+ VERSION = "0.5.11"
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: microsandbox-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.10
4
+ version: 0.5.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - ya-luotao