Skip to content

passthrough: handle NFS directory cookies exceeding i64::MAX#228

Open
duanhongyi wants to merge 1 commit into
cloud-hypervisor:masterfrom
duanhongyi:fix-nfs-large-cookie-readdir
Open

passthrough: handle NFS directory cookies exceeding i64::MAX#228
duanhongyi wants to merge 1 commit into
cloud-hypervisor:masterfrom
duanhongyi:fix-nfs-large-cookie-readdir

Conversation

@duanhongyi

Copy link
Copy Markdown

passthrough: handle NFS directory cookies exceeding i64::MAX

Problem

NFSv4 directory cookies (nfs_cookie4, RFC 7530) are unsigned 64-bit values. Roughly half of them exceed INT64_MAX. When a FUSE/virtiofs guest resumes a readdir from such a cookie, the current code casts the u64 cookie to libc::off64_t (i64) and calls lseek64():

libc::lseek64(dir.as_raw_fd(), offset as libc::off64_t, libc::SEEK_SET)

On an NFS-backed directory the host kernel's nfs_llseek_dir() treats the cast value as negative and rejects it with EINVAL. This causes readdir to fail and directory listings to abort prematurely.

This was originally reported in the Kata Containers project while running with dragonball's inline-virtio-fs (which uses fuse-backend-rs). The same issue was independently fixed in virtiofsd upstream via MR !319.

Solution

When lseek64() fails with EINVAL for a cookie that cannot be represented as a positive signed offset, fall back to a linear scan:

  1. Rewind the directory to offset 0.
  2. Read batches with getdents64().
  3. Scan each batch for the entry whose d_off equals the target cookie.
  4. Return that batch starting just after the matched entry, so the caller only sees entries it has not already received.

This avoids the trap of returning the target entry itself, which would cause the guest to request the same cookie again and create an infinite loop.

The scan logic is factored into a small helper skip_to_cookie() so it can be unit-tested without requiring a real NFS mount.

Changes

  • src/passthrough/sync_io.rs
    • Add PassthroughFs::skip_to_cookie() helper.
    • Modify do_readdir() to detect the too-large-cookie case and use the fallback path.
    • Add unit tests covering middle, first, last, and not-found cookie cases, including a cookie with the high bit set.

Testing

cargo test --features virtiofs passthrough::sync_io::readdir_cookie_tests

All new tests pass. The existing test suite is unchanged.

Limitations

This is a userspace partial fix, matching the scope of virtiofsd MR !319. If guest code performs an explicit lseek() on a directory fd using a large cookie, the request still reaches the host NFS kernel and may fail; fully fixing that requires changes to both the guest virtiofs driver and the host NFS kernel.

Related

NFSv4 directory cookies (nfs_cookie4) are unsigned 64-bit values. Roughly
half of them exceed INT64_MAX, so the current code that casts the cookie to
off64_t and calls lseek64() receives EINVAL from nfs_llseek_dir() on the
host kernel.

Fall back to a linear scan: rewind to the start of the directory and walk
getdents64() batches until we find the entry whose d_off equals the target
cookie. To avoid returning the target entry itself and causing an infinite
loop, skip past it before returning the buffer to the caller.

This mirrors the approach taken by virtiofsd in MR !319.

Fixes #TODO.

Signed-off-by: duanhongyi <duanhongyi@example.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant