Libvirt virtio-fs passthrough without root

Libvirt (and by extension, virt-manager) has supported filesystem passthrough to guest for a long time. This is implemented through one of two mechanisms: virtio-9p, or virtio-fs. virtio-9p is basically the virtio version of the famous Plan 9 filesystem protocol. With virtio-fs, the host-side filesystem is passed through directly via a FUSE-like protocol running over the virtio transport, which promises high performance and native POSIX filesystem semantics.

Compared to file sharing with guests through NFS/SMB, both of these options tend to provide a much better experience. At the very least, one would not have to set up all the authentication and security measures associated with NFS or SMB. One limitation of virtio-fs, however, is that it must be run as root on the host, because it also passes through all the POSIX attributes (uid, gid, etc.) as-is to the guest, and thus whatever is executing on the host must actually possess the permission to write as any user or group. 9p as a network protocol does not share this restriction, and works perfectly fine with Linux guests. However, the only supported filesystem sharing protocol for Windows guests is virtio-fs, and having to use a system-wide (root) libvirtd session makes it really inconvenient, not to mention all the headaches with host-side file ownership when you have a VM running as root.

Fortunately, there seems to be a workaround. The host-side handling logic of virtio-fs is executed as a separate process (which libvirtd will launch automatically) called virtiofsd. Instead of running this process as the actual root user on the host, we can feasibly just isolate this process into a user namespace, with the root user mapped to a normal, non-privileged user on the host (e.g. your regular user account). This is similar to how containerization tools like Podman work in rootless mode -- and the same containerization developers have kindly extracted all the namespace initialization code into a separate tool called RootlessKit. Any program wrapped with the rootlesskit command would gain a "fake" rootful view into the filesystem, with the root user actually mapped to the current user on host, perfect for running virtiofsd.

With this tool, it is actually simple to bring up a rootless virtiofsd process:

rootlesskit virtiofsd --socket-path /path/to/virtiofsd.sock --shared-dir /path/to/shared/dir

and tell libvirtd (and QEMU) to use that instead of launching its own (by editing the XML definition of your libvirtd domain):

<filesystem type="mount" accessmode="passthrough">
  <driver type="virtiofs" queue="1024"/>
  <source socket="/path/to/virtiofsd.sock"/>
  <target dir="your_dir_name"/>

Note that if you then try to edit this filesystem node in virt-manager GUI, it would result in an error, but it is purely a UI bug and can be safely ignored. After saving the XML, you should then be able to launch the VM and mount the your_dir_name share using the guest-side virtio-fs tools.

To make launching virtiofsd more convenient, I also came up with a systemd user service file (as $HOME/.config/systemd/user/virtiofsd@.service):

Description=Virtio FS Daemon instance %i

ExecStartPre=/usr/bin/mkdir -p %h/shared/%i
ExecStart=/usr/bin/rootlesskit /usr/lib/virtiofsd --socket-path %t/virtiofsd-%i.sock --shared-dir %h/shared/%i


Using this unit, when you enable/launch, for example, virtiofsd@windows.service, a virtiofsd process will be launched that shares $HOME/shared/windows, and listens on $XDG_RUNTIME_DIR/virtiofsd-windows.sock.

You'll only receive email when they publish something new.

More from Snowy Day with Peter
All posts