Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

virtio-fs (macOS): a file the host replaces via `rename()` reads as ENOENT in the guest for up to `entry_timeout` (5s)

Open
#888 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 2 days

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
linux, macos, rust
Domain
backend

Research direction

Start with macos/passthrough.rs and trace inode_to_handle, do_getattr, and open_inode; compare the host-rename case with the existing unlinked_fd handling from issue #700. Then inspect Config::default(), Fs::new, and the C API and krunkit options mentioned in the issue. The open question is whether to return ESTALE or expose configurable entry_timeout; done means an agreed approach and a regression test for host-side atomic replacement.

Written by the indexing model from the issue text.

Description

I've been having trouble with git being out of sync between host and containers in a Podman driven VM in my sandbox library foldyard and had to write pretty elaborate git shims, monitors and heal scripts. What led me to this bug is that with Lima / vz the issue was nowhere near as bad because the time windows are much shorter.

Questions:

  1. When a path-based operation on a tracked, non-root inode fails with ENOENT because its
    /.vol/{dev}/{ino} no longer resolves (and it has no unlinked_fd), would returning ESTALE
    be acceptable? The guest would then re-look-up the name straight away. It would find the new file
    if one exists, and get a real ENOENT if the file was deleted. NFS clients rely on the same
    semantics.
  2. Failing that, would you take a patch that makes entry_timeout configurable (libkrun API plus a
    krunkit virtio-fs option), so that users sharing a tree the host also writes could lower it?

Claude's detailed writeup:

Summary

On a macOS host, when the host atomically replaces a file in a shared directory (write a temp
file, then rename() it over the existing name), a Linux guest that has already looked up that name
gets ENOENT for it until the guest's dentry expires, which takes up to 5 seconds. New names show
up at once, and writes from the guest reach the host at once.

git updates HEAD, loose refs, packed-refs and .git/index in exactly this way. So git in the
guest sees a branch that the host just moved as missing. It falls back to packed-refs (an older
commit) or treats the branch as unborn, and a guest commit made in that window can rewind the branch.

Environment

  • macOS 26.7, Apple Silicon
  • podman 6.0.2 → krunkit 1.3.2 (/opt/podman/bin/krunkit) → the libkrun it bundles (≥ 1.19.3; it
    exports krun_add_virtiofs4)
  • Guest: podman machine's Fedora CoreOS 44.20260621.3.1, kernel 7.0.12-201.fc44.aarch64
  • The share is podman's default virtio-fs,sharedDir=…,mountTag=… with no
    permissionSemantics, so krunkit's default simplified applies (attr_timeout = 0,
    entry_timeout = 5 s)

Reproducer

Host (macOS), inside a directory the machine mounts, for example under $HOME:

# host.py: replace ./f with a new version every 2 s
import os, time
for i in range(30):
    with open("f.tmp", "w") as fh:
        fh.write(str(i))
    os.replace("f.tmp", "f")
    time.sleep(2)

Guest (podman machine ssh, same path):

# guest.py: stat ./f every 10 ms, print each ENOENT window
import os, time
t0, gone = time.monotonic(), None
while time.monotonic() - t0 < 60:
    now = time.monotonic() - t0
    try:
        os.stat("f")
        if gone is not None:
            print(f"ENOENT {gone:7.3f}s .. {now:7.3f}s ({(now - gone) * 1000:.0f} ms)")
            gone = None
    except FileNotFoundError:
        gone = now if gone is None else gone
    time.sleep(0.01)

Start guest.py, then host.py. Replaces then produce ENOENT windows in the guest.

Observed

  • ENOENT windows of 0.2–4.7 s after each host replace. The new version appears at t ≈ 5, 15, 20,
    30, 35 s, which is a 5 s grid rather than a fixed delay after each replace.
  • For comparison, on the same Mac, Apple's Virtualization.framework virtio-fs (Lima vz, and
    podman's applehv provider) shows the same shape but for only 22 ms to ~1 s.
  • A link()-family call on the name (for example ln -dT / f, which fails with EEXIST and creates
    nothing) makes the next read see the new file at once, for every process in the VM.

What the source suggests

Please correct me if I've misread any of this.

  • macos/passthrough.rs addresses a node by its volfs path /.vol/{dev}/{ino}
    (inode_to_handle). Once the host's rename() drops the old inode's last link, that path no
    longer resolves. do_getattr / open_inode then pass the host's ENOENT to the guest.
    #700 fixed this for renames the guest makes, through unlinked_fd. A rename by the host
    isn't visible to libkrun, so the fix can't cover it.
  • The guest kernel treats ENOENT from GETATTR/OPEN as final for as long as the dentry is valid. The
    dentry lifetime is entry_timeout, which is Config::default()'s 5 s. Fs::new overrides only
    attr_timeout. As far as I can see, neither value is exposed through the C API or krunkit.
  • The ln workaround works because LOOKUP_EXCL forces fuse_dentry_revalidate to re-issue
    LOOKUP. LOOKUP_REVAL is in the same mask. The VFS retries a path walk with LOOKUP_REVAL when an
    operation returns ESTALE (vfs_statx's retry_estale, and the same in do_filp_open).
Dominant language
Rust
Stars
2.8k
Forks
279
Avg merge
2d 9h
Merged PRs (30d)
28

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from libkrun/libkrun

All issues in libkrun/libkrun

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.