From ff9f3863997b3484a13b9f09ff44cf7b6fcd02be Mon Sep 17 00:00:00 2001 From: Dino Korah <691011+codemedic@users.noreply.github.com> Date: Sun, 22 Mar 2026 16:37:20 +0000 Subject: [PATCH 1/9] feat: add virtiofs support for Linux guests Automatically use virtiofsd (vhost-user-fs-pci) instead of 9p when virtiofsd is present on the host and the guest OS is Linux. Falls back silently to 9p when virtiofsd is unavailable. Updates README to document the new capability. --- README.md | 1 + quickemu | 73 +++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7697a22b9b..e107a4ec5f 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ required to run the virtual machines. - **Nearly 1000 operating system editions are supported!** - Full SPICE support including host/guest clipboard sharing - VirtIO-webdavd file sharing for Linux and Windows guests +- VirtIO-fs file sharing for Linux guests (*automatically preferred over 9p when `virtiofsd` is installed on the host*) - VirtIO-9p file sharing for Linux and macOS guests - [QEMU Guest Agent support](https://wiki.qemu.org/Features/GuestAgent); provides access diff --git a/quickemu b/quickemu index 9b65359d48..145c266cd5 100755 --- a/quickemu +++ b/quickemu @@ -1544,15 +1544,16 @@ function configure_file_sharing() { *) echo " - WebDAV: On guest: dav://localhost:9843/";; esac - # 9P + # virtiofs or 9p depending on host capability if [ "${guest_os}" != "windows" ] || [ "${guest_os}" == "windows-server" ]; then - echo -n " - 9P: On guest: " - if [ "${guest_os}" == "linux" ]; then - echo "sudo mount -t 9p -o trans=virtio,version=9p2000.L,msize=104857600 ${PUBLIC_TAG} ~/$(basename "${PUBLIC}")" + if [ "${guest_os}" == "linux" ] && [ -n "${VIRTIOFSD}" ]; then + echo " - virtiofs: On guest: sudo mount -t virtiofs ${PUBLIC_TAG} ~/$(basename "${PUBLIC}")" + elif [ "${guest_os}" == "linux" ]; then + echo " - 9P: On guest: sudo mount -t 9p -o trans=virtio,version=9p2000.L,msize=104857600 ${PUBLIC_TAG} ~/$(basename "${PUBLIC}")" elif [ "${guest_os}" == "macos" ]; then # PUBLICSHARE needs to be world writeable for seamless integration with # macOS. Test if it is world writeable, and prompt what to do if not. - echo "sudo mount_9p ${PUBLIC_TAG}" + echo " - 9P: On guest: sudo mount_9p ${PUBLIC_TAG}" if [ "${PUBLIC_PERMS}" != "drwxrwxrwx" ]; then echo " - 9P: On host: chmod 777 ${PUBLIC}" echo " Required for macOS integration 👆" @@ -1616,6 +1617,37 @@ function configure_cpu_pinning() { echo " - CPU Pinning: Bind guest cores to host cores (${GUEST_CPUS} -> ${CPU_PINNING})" } +function start_virtiofsd() { + # Start virtiofsd as a background daemon and record its PID so it can be + # cleaned up when the VM exits. The socket path is placed alongside other + # VM runtime files in VMDIR. + if [ -z "${VIRTIOFSD}" ]; then + return + fi + + VIRTIOFSD_SOCKET="${VMDIR}/${VMNAME}.virtiofsd-sock" + local virtiofsd_args=( + --socket-path="${VIRTIOFSD_SOCKET}" + --shared-dir="${PUBLIC}" + --announce-submounts + ) + + echo "${VIRTIOFSD} ${virtiofsd_args[*]} &" >> "${VMDIR}/${VMNAME}.sh" + ${VIRTIOFSD} "${virtiofsd_args[@]}" >> "${VMDIR}/${VMNAME}.log" 2>&1 & + VIRTIOFSD_PID=$! + sleep 0.25 + + if ! kill -0 "${VIRTIOFSD_PID}" 2>/dev/null; then + echo " - WARNING! virtiofsd failed to start; falling back to 9p." + VIRTIOFSD="" + VIRTIOFSD_SOCKET="" + VIRTIOFSD_PID="" + return + fi + + echo " - virtiofsd: ${VIRTIOFSD_SOCKET} (${VIRTIOFSD_PID})" +} + function vm_boot() { AUDIO_DEV="" BALLOON="-device virtio-balloon" @@ -2053,12 +2085,23 @@ function vm_boot() { fi fi + # File sharing: prefer virtiofs (shared memory, lower latency) over 9p when + # virtiofsd is available; virtiofsd must already be running at this point. # https://wiki.qemu.org/Documentation/9psetup # https://askubuntu.com/questions/772784/9p-libvirt-qemu-share-modes if [ "${guest_os}" != "windows" ] || [ "${guest_os}" == "windows-server" ] && [ -n "${PUBLIC}" ]; then - # shellcheck disable=SC2054 - args+=(-fsdev local,id=fsdev0,path="${PUBLIC}",security_model=mapped-xattr - -device virtio-9p-pci,fsdev=fsdev0,mount_tag="${PUBLIC_TAG}") + if [ -n "${VIRTIOFSD_SOCKET}" ]; then + # virtiofs requires a shared-memory backend; the size mirrors the VM RAM. + # shellcheck disable=SC2054 + args+=(-object "memory-backend-file,id=mem,size=${RAM_VM},mem-path=/dev/shm,share=on" + -numa node,memdev=mem + -chardev "socket,id=char0,path=${VIRTIOFSD_SOCKET}" + -device "vhost-user-fs-pci,queue-size=1024,chardev=char0,tag=${PUBLIC_TAG}") + else + # shellcheck disable=SC2054 + args+=(-fsdev local,id=fsdev0,path="${PUBLIC}",security_model=mapped-xattr + -device virtio-9p-pci,fsdev=fsdev0,mount_tag="${PUBLIC_TAG}") + fi fi if [ -n "${USB_PASSTHROUGH}" ]; then @@ -2469,6 +2512,16 @@ function fileshare_param_check() { PUBLIC_PERMS=$(${STAT} -c "%A" "${PUBLIC}") fi fi + + # Prefer virtiofs over 9p when virtiofsd is available and the guest is Linux. + # virtiofs uses shared memory rather than a transport protocol, giving much + # lower latency and higher throughput than 9p. + if [ -n "${PUBLIC}" ] && [ "${guest_os}" == "linux" ]; then + VIRTIOFSD=$(command -v virtiofsd 2>/dev/null || echo "/usr/lib/qemu/virtiofsd") + if [ ! -x "${VIRTIOFSD}" ]; then + VIRTIOFSD="" + fi + fi } function parse_ports_from_file { @@ -2579,6 +2632,9 @@ MONITOR_CMD="" PUBLIC="" PUBLIC_PERMS="" PUBLIC_TAG="" +VIRTIOFSD="" +VIRTIOFSD_PID="" +VIRTIOFSD_SOCKET="" SHORTCUT_OPTIONS="" SNAPSHOT_ACTION="" SNAPSHOT_TAG="" @@ -2903,6 +2959,7 @@ viewer_param_check fileshare_param_check if [ -z "${VM_PID}" ]; then + start_virtiofsd vm_boot start_viewer # If the VM being started is an uninstalled Windows VM then auto-skip the press-any key prompt. From f695b046d654bb7a1d8ddd2e10da7196d65f7d44 Mon Sep 17 00:00:00 2001 From: Dino Korah <691011+codemedic@users.noreply.github.com> Date: Sun, 22 Mar 2026 17:19:37 +0000 Subject: [PATCH 2/9] fix: use standalone virtiofsd only; improve failure diagnostics Drop the fallback to the QEMU-bundled C virtiofsd (/usr/lib/qemu/virtiofsd) as it uses incompatible CLI syntax and requires root privileges. Only the standalone Rust virtiofsd in PATH is supported. Capture virtiofsd stderr to a temp file rather than the shared VM log to avoid false-positive permission-error matches from stale log entries. Move start_virtiofsd() call inside vm_boot() so the Quickemu banner is printed before any virtiofsd warnings. --- quickemu | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/quickemu b/quickemu index 145c266cd5..15901b57f6 100755 --- a/quickemu +++ b/quickemu @@ -1632,18 +1632,31 @@ function start_virtiofsd() { --announce-submounts ) + # Capture virtiofsd stderr separately so we can inspect it without + # false-matching stale entries from previous runs in the shared VM log. + local virtiofsd_stderr + virtiofsd_stderr=$(mktemp) echo "${VIRTIOFSD} ${virtiofsd_args[*]} &" >> "${VMDIR}/${VMNAME}.sh" - ${VIRTIOFSD} "${virtiofsd_args[@]}" >> "${VMDIR}/${VMNAME}.log" 2>&1 & + ${VIRTIOFSD} "${virtiofsd_args[@]}" >> "${VMDIR}/${VMNAME}.log" 2>"${virtiofsd_stderr}" & VIRTIOFSD_PID=$! sleep 0.25 if ! kill -0 "${VIRTIOFSD_PID}" 2>/dev/null; then - echo " - WARNING! virtiofsd failed to start; falling back to 9p." + if grep -q "Operation not permitted" "${virtiofsd_stderr}" 2>/dev/null; then + echo " - WARNING! virtiofsd failed to start (insufficient permissions); falling back to 9p." + echo " Install the standalone virtiofsd package to enable virtiofs support." + else + echo " - WARNING! virtiofsd failed to start; falling back to 9p." + fi + cat "${virtiofsd_stderr}" >> "${VMDIR}/${VMNAME}.log" + rm -f "${virtiofsd_stderr}" VIRTIOFSD="" VIRTIOFSD_SOCKET="" VIRTIOFSD_PID="" return fi + cat "${virtiofsd_stderr}" >> "${VMDIR}/${VMNAME}.log" + rm -f "${virtiofsd_stderr}" echo " - virtiofsd: ${VIRTIOFSD_SOCKET} (${VIRTIOFSD_PID})" } @@ -1698,6 +1711,7 @@ function vm_boot() { echo "Quickemu ${VERSION} using ${QEMU} v${QEMU_VER_LONG}" echo " - Host: ${OS_RELEASE} running ${KERNEL_NAME} ${KERNEL_VER} ${KERNEL_NODE}" + start_virtiofsd # Force to lowercase. boot=${boot,,} @@ -2513,11 +2527,14 @@ function fileshare_param_check() { fi fi - # Prefer virtiofs over 9p when virtiofsd is available and the guest is Linux. - # virtiofs uses shared memory rather than a transport protocol, giving much - # lower latency and higher throughput than 9p. + # Prefer virtiofs over 9p when the standalone virtiofsd is available and the + # guest is Linux. virtiofs uses shared memory rather than a transport protocol, + # giving much lower latency and higher throughput than 9p. + # NOTE: only the standalone virtiofsd (Rust) is supported — the legacy + # QEMU-bundled C daemon (/usr/lib/qemu/virtiofsd) uses incompatible CLI + # syntax and requires root, so it is intentionally ignored here. if [ -n "${PUBLIC}" ] && [ "${guest_os}" == "linux" ]; then - VIRTIOFSD=$(command -v virtiofsd 2>/dev/null || echo "/usr/lib/qemu/virtiofsd") + VIRTIOFSD=$(command -v virtiofsd 2>/dev/null) if [ ! -x "${VIRTIOFSD}" ]; then VIRTIOFSD="" fi @@ -2959,7 +2976,6 @@ viewer_param_check fileshare_param_check if [ -z "${VM_PID}" ]; then - start_virtiofsd vm_boot start_viewer # If the VM being started is an uninstalled Windows VM then auto-skip the press-any key prompt. From 26e4dcf8ee17cbdef36de03476d2066cb0b2028a Mon Sep 17 00:00:00 2001 From: Dino Korah <691011+codemedic@users.noreply.github.com> Date: Sun, 22 Mar 2026 17:39:39 +0000 Subject: [PATCH 3/9] fix: remove stale virtiofsd socket before starting daemon --- quickemu | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/quickemu b/quickemu index 15901b57f6..7d3141a2d6 100755 --- a/quickemu +++ b/quickemu @@ -1626,6 +1626,10 @@ function start_virtiofsd() { fi VIRTIOFSD_SOCKET="${VMDIR}/${VMNAME}.virtiofsd-sock" + # Remove any stale socket left by an unclean shutdown. quickemu already + # checks the PID file and refuses to start if the VM is running, so a + # live virtiofsd cannot be behind this socket at this point. + rm -f "${VIRTIOFSD_SOCKET}" local virtiofsd_args=( --socket-path="${VIRTIOFSD_SOCKET}" --shared-dir="${PUBLIC}" From d66d9c3c1bba97f418f12280709a743d0ea08cb8 Mon Sep 17 00:00:00 2001 From: Dino Korah <691011+codemedic@users.noreply.github.com> Date: Sun, 22 Mar 2026 17:50:14 +0000 Subject: [PATCH 4/9] feat: stop virtiofsd gracefully when VM is killed or fails to start Write virtiofsd PID to a file alongside other VM runtime files. Add stop_virtiofsd() which sends SIGTERM and waits up to 1s for a clean exit before falling back to SIGKILL. Call it from kill_vm() and on QEMU startup failure. --- quickemu | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/quickemu b/quickemu index 7d3141a2d6..27701d41af 100755 --- a/quickemu +++ b/quickemu @@ -167,12 +167,14 @@ function kill_vm() { rm -f "${VMDIR}/${VMNAME}.pid" rm -f "${VMDIR}/${VMNAME}.spice" rm -f "${VMDIR}/${VMNAME}.sock" + stop_virtiofsd elif [ -n "${VM_PID}" ]; then if kill -9 "${VM_PID}" > /dev/null 2>&1; then echo " - ${VMNAME} (${VM_PID}) killed." rm -f "${VMDIR}/${VMNAME}.pid" rm -f "${VMDIR}/${VMNAME}.spice" rm -f "${VMDIR}/${VMNAME}.sock" + stop_virtiofsd else echo " - ${VMNAME} (${VM_PID}) was not killed." fi @@ -1662,9 +1664,44 @@ function start_virtiofsd() { cat "${virtiofsd_stderr}" >> "${VMDIR}/${VMNAME}.log" rm -f "${virtiofsd_stderr}" + echo "${VIRTIOFSD_PID}" > "${VMDIR}/${VMNAME}.virtiofsd-pid" echo " - virtiofsd: ${VIRTIOFSD_SOCKET} (${VIRTIOFSD_PID})" } +function stop_virtiofsd() { + local pid_file="${VMDIR}/${VMNAME}.virtiofsd-pid" + local pid="" + + if [ -n "${VIRTIOFSD_PID}" ]; then + pid="${VIRTIOFSD_PID}" + elif [ -r "${pid_file}" ]; then + pid=$(cat "${pid_file}") + fi + + if [ -z "${pid}" ]; then + return + fi + + if kill -0 "${pid}" 2>/dev/null; then + # Ask virtiofsd to shut down gracefully first; it will close the + # vhost-user socket and flush any pending I/O before exiting. + kill -TERM "${pid}" 2>/dev/null + local i + for i in 1 2 3 4 5; do + kill -0 "${pid}" 2>/dev/null || break + sleep 0.2 + done + # Force-kill only if it is still alive after the grace period. + if kill -0 "${pid}" 2>/dev/null; then + kill -KILL "${pid}" 2>/dev/null + fi + fi + + rm -f "${pid_file}" "${VMDIR}/${VMNAME}.virtiofsd-sock" + VIRTIOFSD_PID="" + VIRTIOFSD_SOCKET="" +} + function vm_boot() { AUDIO_DEV="" BALLOON="-device virtio-balloon" @@ -2226,6 +2263,7 @@ function vm_boot() { rm -f "${VMDIR}/${VMNAME}.pid" rm -f "${VMDIR}/${VMNAME}.spice" rm -f "${VMDIR}/${VMNAME}.sock" + stop_virtiofsd echo && cat "${VMDIR}/${VMNAME}.log" exit 1 fi From 058e229d5325cb7cd5f715287a1de0d6766e08aa Mon Sep 17 00:00:00 2001 From: Dino Korah <691011+codemedic@users.noreply.github.com> Date: Sun, 22 Mar 2026 18:47:18 +0000 Subject: [PATCH 5/9] fix: skip virtiofs during OS installation to prevent installer hangs The shared-memory NUMA backend required for virtiofs can confuse installers. Fall back to 9p when an ISO is present (install mode). --- quickemu | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/quickemu b/quickemu index 27701d41af..4f72511623 100755 --- a/quickemu +++ b/quickemu @@ -2575,7 +2575,9 @@ function fileshare_param_check() { # NOTE: only the standalone virtiofsd (Rust) is supported — the legacy # QEMU-bundled C daemon (/usr/lib/qemu/virtiofsd) uses incompatible CLI # syntax and requires root, so it is intentionally ignored here. - if [ -n "${PUBLIC}" ] && [ "${guest_os}" == "linux" ]; then + # Skip virtiofs during OS installation: the required shared-memory NUMA + # backend can confuse installers and cause hangs. 9p is used instead. + if [ -n "${PUBLIC}" ] && [ "${guest_os}" == "linux" ] && [ -z "${iso}" ]; then VIRTIOFSD=$(command -v virtiofsd 2>/dev/null) if [ ! -x "${VIRTIOFSD}" ]; then VIRTIOFSD="" From 4544d1ff9656af1275814fdbac43811f56eb3c35 Mon Sep 17 00:00:00 2001 From: Dino Korah <691011+codemedic@users.noreply.github.com> Date: Sun, 22 Mar 2026 21:11:17 +0000 Subject: [PATCH 6/9] =?UTF-8?q?fix:=20correct=20virtiofsd=20lifecycle=20?= =?UTF-8?q?=E2=80=94=20forking,=20iso=20check=20ordering,=20and=20fallback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virtiofsd forks after startup; check socket existence rather than parent PID to detect success, and use pgrep to track the daemon child for cleanup. Move start_virtiofsd() call to after configure_storage() so $iso is correctly cleared before the installer check runs. Clear VIRTIOFSD on early return so configure_file_sharing() falls back to 9p instructions when virtiofsd is not started. --- quickemu | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/quickemu b/quickemu index 4f72511623..c2b63ef044 100755 --- a/quickemu +++ b/quickemu @@ -1627,6 +1627,15 @@ function start_virtiofsd() { return fi + # Skip virtiofs when booting from an ISO (installation). By this point in + # vm_boot(), quickemu has already cleared $iso when the disk is in use, so + # a non-empty $iso reliably means we are booting the installer. The + # shared-memory NUMA backend required for virtiofs can cause installer hangs. + if [ -n "${iso}" ]; then + VIRTIOFSD="" + return + fi + VIRTIOFSD_SOCKET="${VMDIR}/${VMNAME}.virtiofsd-sock" # Remove any stale socket left by an unclean shutdown. quickemu already # checks the PID file and refuses to start if the VM is running, so a @@ -1644,10 +1653,11 @@ function start_virtiofsd() { virtiofsd_stderr=$(mktemp) echo "${VIRTIOFSD} ${virtiofsd_args[*]} &" >> "${VMDIR}/${VMNAME}.sh" ${VIRTIOFSD} "${virtiofsd_args[@]}" >> "${VMDIR}/${VMNAME}.log" 2>"${virtiofsd_stderr}" & - VIRTIOFSD_PID=$! - sleep 0.25 + sleep 0.5 - if ! kill -0 "${VIRTIOFSD_PID}" 2>/dev/null; then + # virtiofsd forks: the shell child we spawned exits once the daemon child + # is running. Check the socket rather than the parent PID to detect success. + if [ ! -S "${VIRTIOFSD_SOCKET}" ]; then if grep -q "Operation not permitted" "${virtiofsd_stderr}" 2>/dev/null; then echo " - WARNING! virtiofsd failed to start (insufficient permissions); falling back to 9p." echo " Install the standalone virtiofsd package to enable virtiofs support." @@ -1664,6 +1674,8 @@ function start_virtiofsd() { cat "${virtiofsd_stderr}" >> "${VMDIR}/${VMNAME}.log" rm -f "${virtiofsd_stderr}" + # The parent exits after forking the daemon child; find the child by socket. + VIRTIOFSD_PID=$(pgrep -f "virtiofsd.*${VIRTIOFSD_SOCKET}" 2>/dev/null | head -1) echo "${VIRTIOFSD_PID}" > "${VMDIR}/${VMNAME}.virtiofsd-pid" echo " - virtiofsd: ${VIRTIOFSD_SOCKET} (${VIRTIOFSD_PID})" } @@ -1752,7 +1764,6 @@ function vm_boot() { echo "Quickemu ${VERSION} using ${QEMU} v${QEMU_VER_LONG}" echo " - Host: ${OS_RELEASE} running ${KERNEL_NAME} ${KERNEL_VER} ${KERNEL_NODE}" - start_virtiofsd # Force to lowercase. boot=${boot,,} @@ -1769,6 +1780,7 @@ function vm_boot() { configure_bios configure_os_quirks configure_storage + start_virtiofsd configure_display configure_audio configure_ports @@ -2575,9 +2587,7 @@ function fileshare_param_check() { # NOTE: only the standalone virtiofsd (Rust) is supported — the legacy # QEMU-bundled C daemon (/usr/lib/qemu/virtiofsd) uses incompatible CLI # syntax and requires root, so it is intentionally ignored here. - # Skip virtiofs during OS installation: the required shared-memory NUMA - # backend can confuse installers and cause hangs. 9p is used instead. - if [ -n "${PUBLIC}" ] && [ "${guest_os}" == "linux" ] && [ -z "${iso}" ]; then + if [ -n "${PUBLIC}" ] && [ "${guest_os}" == "linux" ]; then VIRTIOFSD=$(command -v virtiofsd 2>/dev/null) if [ ! -x "${VIRTIOFSD}" ]; then VIRTIOFSD="" From a9a6871b9f9c6bd347fcc6ce0aa8ba12ca0f07d8 Mon Sep 17 00:00:00 2001 From: Dino Korah <691011+codemedic@users.noreply.github.com> Date: Sun, 22 Mar 2026 21:25:59 +0000 Subject: [PATCH 7/9] fix: address code review issues from cubic-dev-ai - README: clarify virtiofs requires a public directory to be configured - stop_virtiofsd: guard against PID reuse by verifying /proc//comm before signalling - start_virtiofsd: replace fixed sleep with poll loop for socket readiness; use fuser (or comm-verified pgrep fallback) for safe PID resolution - vm_boot: check QEMU supports vhost-user-fs-pci before committing to virtiofs; stop virtiofsd and fall back to 9p if unsupported --- README.md | 2 +- quickemu | 69 +++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index e107a4ec5f..826ccaec30 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ required to run the virtual machines. - **Nearly 1000 operating system editions are supported!** - Full SPICE support including host/guest clipboard sharing - VirtIO-webdavd file sharing for Linux and Windows guests -- VirtIO-fs file sharing for Linux guests (*automatically preferred over 9p when `virtiofsd` is installed on the host*) +- VirtIO-fs file sharing for Linux guests (*automatically preferred over 9p when `virtiofsd` is installed on the host and a public directory is configured*) - VirtIO-9p file sharing for Linux and macOS guests - [QEMU Guest Agent support](https://wiki.qemu.org/Features/GuestAgent); provides access diff --git a/quickemu b/quickemu index c2b63ef044..136b7e35ec 100755 --- a/quickemu +++ b/quickemu @@ -1653,10 +1653,15 @@ function start_virtiofsd() { virtiofsd_stderr=$(mktemp) echo "${VIRTIOFSD} ${virtiofsd_args[*]} &" >> "${VMDIR}/${VMNAME}.sh" ${VIRTIOFSD} "${virtiofsd_args[@]}" >> "${VMDIR}/${VMNAME}.log" 2>"${virtiofsd_stderr}" & - sleep 0.5 - # virtiofsd forks: the shell child we spawned exits once the daemon child - # is running. Check the socket rather than the parent PID to detect success. + # virtiofsd forks: the shell child exits once the daemon child is running. + # Poll for the socket rather than using a fixed sleep to avoid a race. + local i + for i in $(seq 1 20); do + [ -S "${VIRTIOFSD_SOCKET}" ] && break + sleep 0.1 + done + if [ ! -S "${VIRTIOFSD_SOCKET}" ]; then if grep -q "Operation not permitted" "${virtiofsd_stderr}" 2>/dev/null; then echo " - WARNING! virtiofsd failed to start (insufficient permissions); falling back to 9p." @@ -1674,14 +1679,25 @@ function start_virtiofsd() { cat "${virtiofsd_stderr}" >> "${VMDIR}/${VMNAME}.log" rm -f "${virtiofsd_stderr}" - # The parent exits after forking the daemon child; find the child by socket. - VIRTIOFSD_PID=$(pgrep -f "virtiofsd.*${VIRTIOFSD_SOCKET}" 2>/dev/null | head -1) + # Resolve the daemon child's PID via the socket to avoid PID reuse issues. + # Prefer fuser (unambiguous socket owner); fall back to pgrep with a comm + # check to confirm the process is actually virtiofsd. + if command -v fuser >/dev/null 2>&1; then + VIRTIOFSD_PID=$(fuser "${VIRTIOFSD_SOCKET}" 2>/dev/null | tr -s ' ' '\n' | grep -m1 '[0-9]') + else + local candidate + candidate=$(pgrep -f "virtiofsd" 2>/dev/null | head -1) + if grep -q 'virtiofsd' "/proc/${candidate}/comm" 2>/dev/null; then + VIRTIOFSD_PID="${candidate}" + fi + fi echo "${VIRTIOFSD_PID}" > "${VMDIR}/${VMNAME}.virtiofsd-pid" echo " - virtiofsd: ${VIRTIOFSD_SOCKET} (${VIRTIOFSD_PID})" } function stop_virtiofsd() { local pid_file="${VMDIR}/${VMNAME}.virtiofsd-pid" + local socket="${VMDIR}/${VMNAME}.virtiofsd-sock" local pid="" if [ -n "${VIRTIOFSD_PID}" ]; then @@ -1690,26 +1706,26 @@ function stop_virtiofsd() { pid=$(cat "${pid_file}") fi - if [ -z "${pid}" ]; then - return - fi - - if kill -0 "${pid}" 2>/dev/null; then - # Ask virtiofsd to shut down gracefully first; it will close the - # vhost-user socket and flush any pending I/O before exiting. - kill -TERM "${pid}" 2>/dev/null - local i - for i in 1 2 3 4 5; do - kill -0 "${pid}" 2>/dev/null || break - sleep 0.2 - done - # Force-kill only if it is still alive after the grace period. - if kill -0 "${pid}" 2>/dev/null; then - kill -KILL "${pid}" 2>/dev/null + if [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null; then + # Guard against PID reuse: only signal the process if it is still + # a virtiofsd instance. /proc//comm holds the executable name. + if grep -q 'virtiofsd' "/proc/${pid}/comm" 2>/dev/null; then + # Ask virtiofsd to shut down gracefully first; it will close the + # vhost-user socket and flush any pending I/O before exiting. + kill -TERM "${pid}" 2>/dev/null + local i + for i in 1 2 3 4 5; do + kill -0 "${pid}" 2>/dev/null || break + sleep 0.2 + done + # Force-kill only if it is still alive after the grace period. + if kill -0 "${pid}" 2>/dev/null; then + kill -KILL "${pid}" 2>/dev/null + fi fi fi - rm -f "${pid_file}" "${VMDIR}/${VMNAME}.virtiofsd-sock" + rm -f "${pid_file}" "${socket}" VIRTIOFSD_PID="" VIRTIOFSD_SOCKET="" } @@ -2157,6 +2173,15 @@ function vm_boot() { # https://wiki.qemu.org/Documentation/9psetup # https://askubuntu.com/questions/772784/9p-libvirt-qemu-share-modes if [ "${guest_os}" != "windows" ] || [ "${guest_os}" == "windows-server" ] && [ -n "${PUBLIC}" ]; then + if [ -n "${VIRTIOFSD_SOCKET}" ]; then + # Verify QEMU supports vhost-user-fs-pci before using it; older QEMU + # builds silently lack the device and would abort VM startup. + if ! "${QEMU}" -device vhost-user-fs-pci,help 2>&1 | grep -q "vhost-user-fs-pci"; then + echo " - WARNING! QEMU does not support vhost-user-fs-pci; falling back to 9p." + stop_virtiofsd + VIRTIOFSD_SOCKET="" + fi + fi if [ -n "${VIRTIOFSD_SOCKET}" ]; then # virtiofs requires a shared-memory backend; the size mirrors the VM RAM. # shellcheck disable=SC2054 From 53bf54cdb3df55ce67ae469d13e3e77b03c99b3a Mon Sep 17 00:00:00 2001 From: Dino Korah <691011+codemedic@users.noreply.github.com> Date: Sun, 22 Mar 2026 21:38:32 +0000 Subject: [PATCH 8/9] =?UTF-8?q?fix:=20address=20remaining=20cubic=20review?= =?UTF-8?q?=20issues=20=E2=80=94=20pgrep=20scope,=20/proc=20portability,?= =?UTF-8?q?=20QEMU=20device=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Scope pgrep fallback to the VM socket path to avoid matching unrelated virtiofsd instances - Replace /proc//comm checks with portable `ps -p ... -o comm=` in both start and stop - Fix QEMU virtiofs capability check: use `-device help` and match quoted device name to avoid false-positives on error output --- quickemu | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/quickemu b/quickemu index 136b7e35ec..cde805fac3 100755 --- a/quickemu +++ b/quickemu @@ -1686,8 +1686,8 @@ function start_virtiofsd() { VIRTIOFSD_PID=$(fuser "${VIRTIOFSD_SOCKET}" 2>/dev/null | tr -s ' ' '\n' | grep -m1 '[0-9]') else local candidate - candidate=$(pgrep -f "virtiofsd" 2>/dev/null | head -1) - if grep -q 'virtiofsd' "/proc/${candidate}/comm" 2>/dev/null; then + candidate=$(pgrep -f "virtiofsd.*${VIRTIOFSD_SOCKET}" 2>/dev/null | head -1) + if ps -p "${candidate}" -o comm= 2>/dev/null | grep -q 'virtiofsd'; then VIRTIOFSD_PID="${candidate}" fi fi @@ -1708,8 +1708,8 @@ function stop_virtiofsd() { if [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null; then # Guard against PID reuse: only signal the process if it is still - # a virtiofsd instance. /proc//comm holds the executable name. - if grep -q 'virtiofsd' "/proc/${pid}/comm" 2>/dev/null; then + # a virtiofsd instance. + if ps -p "${pid}" -o comm= 2>/dev/null | grep -q 'virtiofsd'; then # Ask virtiofsd to shut down gracefully first; it will close the # vhost-user socket and flush any pending I/O before exiting. kill -TERM "${pid}" 2>/dev/null @@ -2176,7 +2176,7 @@ function vm_boot() { if [ -n "${VIRTIOFSD_SOCKET}" ]; then # Verify QEMU supports vhost-user-fs-pci before using it; older QEMU # builds silently lack the device and would abort VM startup. - if ! "${QEMU}" -device vhost-user-fs-pci,help 2>&1 | grep -q "vhost-user-fs-pci"; then + if ! "${QEMU}" -device help 2>&1 | grep -q '"vhost-user-fs-pci"'; then echo " - WARNING! QEMU does not support vhost-user-fs-pci; falling back to 9p." stop_virtiofsd VIRTIOFSD_SOCKET="" From ccac3a32670132a4019b003c687b2d370068bd11 Mon Sep 17 00:00:00 2001 From: Dino Korah <691011+codemedic@users.noreply.github.com> Date: Sun, 22 Mar 2026 21:47:08 +0000 Subject: [PATCH 9/9] fix: escape socket path regex metacharacters in pgrep fallback --- quickemu | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/quickemu b/quickemu index cde805fac3..52d4da14cc 100755 --- a/quickemu +++ b/quickemu @@ -1685,8 +1685,10 @@ function start_virtiofsd() { if command -v fuser >/dev/null 2>&1; then VIRTIOFSD_PID=$(fuser "${VIRTIOFSD_SOCKET}" 2>/dev/null | tr -s ' ' '\n' | grep -m1 '[0-9]') else - local candidate - candidate=$(pgrep -f "virtiofsd.*${VIRTIOFSD_SOCKET}" 2>/dev/null | head -1) + local candidate socket_pat + # Escape regex metacharacters in the socket path before passing to pgrep -f. + socket_pat=$(printf '%s' "${VIRTIOFSD_SOCKET}" | sed 's/[[\.*^$()+?{}|]/\\&/g') + candidate=$(pgrep -f "virtiofsd.*${socket_pat}" 2>/dev/null | head -1) if ps -p "${candidate}" -o comm= 2>/dev/null | grep -q 'virtiofsd'; then VIRTIOFSD_PID="${candidate}" fi