feat(shim): forward ro bind-mount option to virtio-fs share#204
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds virtio-fs read-only propagation end-to-end by wiring OCI ro mount intent into libkrun’s newer krun_add_virtiofs3 API (with a safe fallback when the symbol is missing), and extends tests to validate both symbol binding and mount option flow.
Changes:
- Add optional libkrun symbol binding support via
C:"...,optional"tags andregisterOptionalLibFunc. - Update virtio-fs plumbing to pass a
readonlyflag (shim → sandbox opts → vm instance → libkrun), preferringkrun_add_virtiofs3when available. - Add unit tests for tag parsing and virtio-fs routing/fallback/error propagation, plus mount flow assertions.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/vm/libkrun/krun_test.go | Adds unit tests for parseCTag and virtio-fs v3/legacy routing behavior. |
| internal/vm/libkrun/krun.go | Introduces optional symbol parsing/binding and updates AddVirtiofs to support read-only + logging. |
| internal/vm/libkrun/instance.go | Propagates mount options into the new AddVirtiofs(ctx, ..., readonly) signature. |
| internal/vm/libkrun/dlfcn_windows.go | Adds non-panicking optional symbol registration for Windows. |
| internal/vm/libkrun/dlfcn_unix.go | Adds non-panicking optional symbol registration for Unix. |
| internal/shim/task/mount_test.go | Extends tests to assert sandbox.Filesystem.Readonly is set correctly from OCI mounts. |
| internal/shim/task/mount.go | Tracks ro option on bind mounts and passes it into sandbox filesystem opts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for _, c := range cases { | ||
| t.Run(c.in, func(t *testing.T) { | ||
| name, opt := parseCTag(c.in) | ||
| if name != c.wantName || opt != c.wantOpt { | ||
| t.Fatalf("parseCTag(%q) = (%q, %v), want (%q, %v)", c.in, name, opt, c.wantName, c.wantOpt) | ||
| } | ||
| }) | ||
| } |
There was a problem hiding this comment.
Fixed in cb20f8b — switched the table to named subtests (empty, plain, etc.) so t.Run no longer receives an empty string for the "" case.
| readOnly := false | ||
| for _, opt := range m.Options { | ||
| if opt == "ro" { | ||
| readOnly = true | ||
| break | ||
| } | ||
| } |
There was a problem hiding this comment.
Fixed in cb20f8b — the loop now scans every option and lets later ro/rw override earlier ones (no break), matching standard mount-option semantics. Added two regression tests (bind mount ro then rw and bind mount rw then ro) in mount_test.go to lock the behavior down.
00b167a to
cb20f8b
Compare
The ext4 mount path in mount.go already parses `ro` from the OCI mount options; the bind-mount path silently dropped it. Plumb the flag through bindMounter and vmInstance.AddFS so the virtio-fs share itself is marked read-only at the host edge via krun_add_virtiofs3 (libkrun >= 1.18), giving us defense-in-depth on top of the guest-side `ro` bind mount and opening the door to VMM-side optimizations on immutable shares. When the loaded libkrun is older and does not export krun_add_virtiofs3, the binding falls back to krun_add_virtiofs with an info log and enforcement stays on the guest mount options exactly as today. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
cb20f8b to
2f60004
Compare
|
Downgraded the fallback log from warn to info: missing krun_add_virtiofs3 isn't a blocker — guest-side enforcement still applies. New head: 2f60004. |
| // AddVirtiofs attaches a virtio-fs share to the VM. When readonly is true | ||
| // it prefers krun_add_virtiofs3 (libkrun >= 1.18) which natively marks the | ||
| // share read-only at the host edge. If that symbol is not exported by the | ||
| // loaded libkrun, it falls back to krun_add_virtiofs and logs an info | ||
| // message: in that case the read-only enforcement relies on the guest | ||
| // applying the `ro` mount option, which the OCI spec already carries. | ||
| func (vmc *vmcontext) AddVirtiofs(ctx context.Context, tag, path string, readonly bool) error { | ||
| if vmc.lib.AddVirtiofs3 != nil { | ||
| ret := vmc.lib.AddVirtiofs3(vmc.ctxID, tag, path, 0, readonly) |
| if readonly { | ||
| log.G(ctx).WithField("tag", tag).Info( | ||
| "libkrun does not export krun_add_virtiofs3; " + | ||
| "read-only virtiofs enforcement deferred to the guest mount options", | ||
| ) | ||
| } |
| tag := ik.Type().Field(i).Tag.Get("C") | ||
| cName, optional := parseCTag(tag) | ||
| fn := ik.Field(i).Addr().Interface() | ||
| if optional { | ||
| // registerOptionalLibFunc reports whether the symbol was | ||
| // resolved; if not, the field is left nil and call sites | ||
| // must guard against it. | ||
| registerOptionalLibFunc(fn, f, cName) | ||
| continue | ||
| } |
| if err != nil || addr == 0 { | ||
| return false | ||
| } | ||
| purego.RegisterFunc(fn, uintptr(addr)) |
Summary
roOCI mount option inbindMounter.FromBundleand carry it onbindMount, mirroring what the ext4 branch in the same file already does.sandbox.WithFStovmInstance.AddFS, which now appliesvm.MountOptsbefore calling into libkrun.krun_add_virtiofs3symbol (libkrun >= 1.18) via a smallregisterOptionalLibFunchelper and a,optionalstruct-tag flag; routeAddVirtiofsthrough it when present so the share is marked read-only at the host edge.krun_add_virtiofswith a single warn log when the v3 symbol is not exported; guest-sideroenforcement via OCI mount options is unchanged.parseCTagand theAddVirtiofsv3-vs-legacy matrix (prefers v3, falls back when v3 is nil, surfaces non-zero return codes on both paths, errors when neither symbol is bound).Behavior
< 1.18(nokrun_add_virtiofs3exported, e.g. the v1.17.4 currently pinned)rothrough the OCI mount options exactly as today.>= 1.18(krun_add_virtiofs3exported)Test plan
go build ./...go test ./internal/shim/task/... ./internal/vm/libkrun/...