Libvirt virtio-fs passthrough without root
July 11, 2023•526 words
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"/>
</filesystem>
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
):
[Unit]
Description=Virtio FS Daemon instance %i
[Service]
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
Restart=always
RestartSec=2s
[Install]
WantedBy=default.target
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
.