Problem
An embedder often shares a host directory, owned by the host user, with a guest that runs as some other user: a project folder mounted into a sandbox that runs as node or nobody. The guest user must see the files as its own, or it cannot write to them.
On a macOS host the only way to get that today is LinuxSimplified, which reports every file as owned by the process that asks. It works, but it is very slow. On a 63,224-file node_modules, unpacking takes 168 s and find takes 17 s, against 36 s and 1.4 s on Apple's Virtualization.framework.
Cause
Under LinuxSimplified the owner depends on the caller, so attributes cannot be cached, and the device sets the attribute timeout to 0. Every stat in the guest becomes a round trip to the host.
Fix
Let the embedder fix the owner instead. A fixed owner is the same for every caller, so attributes keep the default 5 s timeout.
Two commits:
- virtio/fs: add a fixed-owner variant of the simplified semantics.
PermissionSemantics::LinuxSimplifiedOwnedBy(uid, gid)behaves asLinuxSimplified, butstat,lstatandmknodreportuid:gidas the owner.Fs::set_fixed_ownerselects it before the device activates.- Linux declares the variant next to
LinuxSimplified, which it does not act on either.
- lib: add FsDevice::set_fixed_owner.
- Exposes it as
FsDevice::set_fixed_owner(uid, gid)andkrun_fs_device_set_fixed_owner. - The bindings are regenerated with
make gen-libkrun-bindings; the C header diff is only the new function. - Adds the
virtiofs-fixed-ownertest.
- Exposes it as
On the host, files are still created and changed as the user running libkrun. A chown from the guest is accepted and not kept, as under LinuxSimplified.
With a fixed owner of 0, the guest sees every set-user-ID file on the share as owned by root, so such a file runs as root in the guest. LinuxSimplified does not have this, because the owner is always the caller. The doc comment of set_fixed_owner (and so the C header) says to mount such a share with nosuid.
Evidence
Setup: M4 Pro, macOS 26.7, aarch64 Linux guest with 2 vCPUs and 2 GiB, a 63,224-file node_modules tarball on a virtio-fs share, used by a guest user other than root; medians of 3.
| Step | LinuxSimplified | Fixed owner | Virtualization.framework |
|---|---|---|---|
| Unpack | 168 s | 38 s | 36 s |
find -type f | 17 s | 3.1 s | 1.4 s |
du -s | 16 s | 1.5 s | 1.3 s |
rm -rf | 39 s | 14 s | 8.6 s |
The remaining gap on find and rm -rf is the per-request round trip, which #943 and #944 address.
The new virtiofs-fixed-owner test runs on macOS. It checks that these report the fixed owner:
- a host file and the root directory;
- a file, a directory, a FIFO (
mknod) and a symlink the guest creates; - a file after a
chownfrom the guest, which is accepted without changing it.
It also checks that the guest's write reaches the host directory. With the fixed owner ignored (the caller reported instead), the test fails.
Tests
make test, M4 Pro, macOS 26.7, dynamic linking:
| Ref | Result |
|---|---|
upstream main | 15/16 pass; virtiofs-misc fails in uid_gid_mapping_preexisting |
| this branch | 16/17 pass; the same failure, nothing new; virtiofs-fixed-owner passes |
The 17 skipped tests need FreeBSD assets, pjdfstest, iperf, TAP or passt, none of which this Mac has.
Related work
- #759 added
LinuxSimplifiedand left it out of the 2.0 API on purpose, while that API settles. This PR adds the first 2.0 setter for a permission semantics. If a general setter is preferred,set_fixed_ownercan become one variant of it. - #933 (experimental) restructures
fs/device.rs.Fs::set_fixed_owneris small and rebases onto it either way.
@jansav thanks for the PR! This new mode looks like a good idea to me. Please rebase this on #954, extend
PermissionSemanticswith your new mode andset_fixed_ownerwith an API just for setting the uid/gid (this one should fail if the configured mode isn't the new mode).