Skip to content

VirtioFS serves symlinks with st_size=0 on cold path lookups (Sandboxes microVM mounts) #7883

Description

@JStonevalley

Summary

Through a VirtioFS mount, symlinks are served with st_size = 0 once the virtiofs server first enumerates their directory on behalf of the guest — which, for a sandbox/boot, is how every pre-existing symlink gets its attributes cached. readlink(2) still returns the correct target, and the macOS host reports the correct size. Creating or touching the file on the host immediately restores the correct size in the guest (after the guest's ~1s attribute cache TTL); recreating the file from the guest also works. A symlink created on the host inside an already-enumerated directory reports correctly, and the poisoned state returns whenever the guest enumerates the containing directories again (observed with guest-side find walks) — both point at the directory-enumeration path in the server: its batch attribute fetch appears to drop symlink size.

Environment

  • Docker Desktop 4.89.0, engine 29.7.2, macOS 26.6.2 (Apple Silicon)
  • Docker Sandboxes (sbx v0.39.0) microVM, guest kernel 7.0.12 aarch64, workspace mounted via virtiofs (host /Users/... virtiofs rw,relatime from /proc/mounts in the guest)
  • Guest git 2.53.0; statx is ENOSYS in the guest, so all stat traffic uses newfstatat
  • Verified with python os.lstat and coreutils stat inside the guest

Steps to reproduce

With any sbx sandbox whose workspace is a macOS host directory (paths are identical on both sides of the mount):

WS=~/path/to/workspace            # the mounted workspace
mkdir -p "$WS/.sbx-probe/repro"   # a directory NEW to the VM
ln -s a-target "$WS/.sbx-probe/repro/link"

stat -f %z "$WS/.sbx-probe/repro/link"   # host -> 8 (target-string length, correct)

sbx exec <sandbox> -- sh -c "cd '$WS/.sbx-probe/repro' && readlink link && stat -c %s link"
# guest -> a-target    (readlink works)
# guest -> 0           <- the bug

touch -h "$WS/.sbx-probe/repro/link"   # host-side event
sleep 2                                # guest attr-cache TTL
sbx exec <sandbox> -- sh -c "stat -c %s '$WS/.sbx-probe/repro/link'"   # guest -> 8, fixed

Expected behavior

stat inside the guest returns the symlink's target-string length, matching the host.

Actual behavior

It returns 0 until a host-side event on the file (touch -h) or a guest-side create repopulates the attribute. Guest-side enumeration of the containing directory re-poisons it.

Impact

git inside the sandbox flags every pre-existing tracked symlink as modified and renders it as a deletion plus an empty file, because git (a) compares st_size against its index under every core.checkStat mode and (b) treats zero-size worktree entries as empty content in diff_populate_filespec without ever calling readlink. The phantom modifications cannot be cleared with git update-index --refresh, and they reappear after every VM boot. Any other tool that trusts st_size (rsync, tar, ...) is similarly affected. We currently work around it by touch -h-ing tracked symlinks from the host at every sandbox start.

Diagnostics: not uploaded — happy to provide on request.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions