fix(kvm): probe virtiofsd fallback paths in KvmBackend (#447) (#454)

Follow-up to #453: the daemon still spawns virtiofsd via PATH lookup
(`spawnProcess('virtiofsd', ...)`), so on stock Debian/Ubuntu
(`/usr/libexec/virtiofsd`) and Arch/CachyOS/Manjaro
(`/usr/lib/virtiofsd`) the spawn ENOENTs and KvmBackend silently
falls through to virtio-9p — users who opted into
`COWORK_VM_BACKEND=kvm` and installed virtiofsd get 9p performance
without knowing.

Mirror doctor.sh's `_find_virtiofsd` in JS: probe `COWORK_VM_VIRTIOFSD_BIN`
override, then `which`, then the same fallback list. Pass the resolved
absolute path as argv[0] so the spawn bypasses PATH entirely.

Also:
- Add a `spawnFailed` flag the socket-wait loop checks for early exit
  when the async 'error' event fires (e.g. binary removed between
  probe and exec) — prevents a 5s stall before 9p fallback.
- Guard `this.virtiofsdProcess.kill()` against the race where the
  error handler has already zeroed it.
- Rename doctor.sh's test hook `_COWORK_DOCTOR_VFSD_PATHS` →
  `_COWORK_VFSD_PATHS` so doctor and daemon share the same env var
  for lock-step test parity (shipped 24h ago in #453, zero external
  users).

Verified on CachyOS via a node harness covering 8 scenarios:
PATH hit, fallback hit, fallback ordering, total miss, non-executable
rejection, explicit override wins over PATH, override non-executable
→ null, override missing → null (no fall-through).

All 45 BATS tests still pass after the env-var rename.

Not verifiable locally: Ubuntu `/usr/libexec/virtiofsd` hit (needs an
Ubuntu VM with `qemu-system-common`). Logic is symmetric to the Arch
case that is verified.

Co-authored-by: Claude <claude@anthropic.com>
This commit is contained in:
Travis
2026-04-20 15:52:53 -05:00
committed by GitHub
parent 89582bb8f0
commit 7e33c095da
3 changed files with 131 additions and 38 deletions

View File

@@ -679,7 +679,7 @@ assert(warnings[1].includes('/outside/home'), 'warns about rw outside home');
# shellcheck source=scripts/launcher-common.sh
source "scripts/launcher-common.sh"
PATH="${TEST_TMP}/bin" \
_COWORK_DOCTOR_VFSD_PATHS='/nonexistent/virtiofsd' \
_COWORK_VFSD_PATHS='/nonexistent/virtiofsd' \
run _find_virtiofsd
[[ "$status" -eq 0 ]]
[[ "$output" == "$stub" ]]
@@ -695,7 +695,7 @@ assert(warnings[1].includes('/outside/home'), 'warns about rw outside home');
source "scripts/launcher-common.sh"
# Empty PATH so `command -v` cannot resolve virtiofsd
PATH='' \
_COWORK_DOCTOR_VFSD_PATHS="$stub" \
_COWORK_VFSD_PATHS="$stub" \
run _find_virtiofsd
[[ "$status" -eq 0 ]]
[[ "$output" == "$stub" ]]
@@ -711,7 +711,7 @@ assert(warnings[1].includes('/outside/home'), 'warns about rw outside home');
source "scripts/launcher-common.sh"
# First fallback is missing; second is present. Expect second.
PATH='' \
_COWORK_DOCTOR_VFSD_PATHS="/nonexistent/virtiofsd:$stub" \
_COWORK_VFSD_PATHS="/nonexistent/virtiofsd:$stub" \
run _find_virtiofsd
[[ "$status" -eq 0 ]]
[[ "$output" == "$stub" ]]
@@ -721,7 +721,7 @@ assert(warnings[1].includes('/outside/home'), 'warns about rw outside home');
# shellcheck source=scripts/launcher-common.sh
source "scripts/launcher-common.sh"
PATH='' \
_COWORK_DOCTOR_VFSD_PATHS='/nonexistent/a:/nonexistent/b' \
_COWORK_VFSD_PATHS='/nonexistent/a:/nonexistent/b' \
run _find_virtiofsd
[[ "$status" -eq 1 ]]
[[ -z "$output" ]]
@@ -737,7 +737,7 @@ assert(warnings[1].includes('/outside/home'), 'warns about rw outside home');
# shellcheck source=scripts/launcher-common.sh
source "scripts/launcher-common.sh"
PATH='' \
_COWORK_DOCTOR_VFSD_PATHS="$stub" \
_COWORK_VFSD_PATHS="$stub" \
run _find_virtiofsd
[[ "$status" -eq 1 ]]
[[ -z "$output" ]]
@@ -745,7 +745,7 @@ assert(warnings[1].includes('/outside/home'), 'warns about rw outside home');
@test "_find_virtiofsd: default path list covers deb/rpm/arch" {
# Guard against regression: the built-in fallback list (used when
# _COWORK_DOCTOR_VFSD_PATHS is unset) must include the off-PATH
# _COWORK_VFSD_PATHS is unset) must include the off-PATH
# install locations for Debian/Ubuntu, legacy Debian, and Arch.
# shellcheck source=scripts/launcher-common.sh
source "scripts/launcher-common.sh"