Conversation
There was a problem hiding this comment.
2 issues found across 25 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/syscall/fs-stat.c">
<violation number="1" location="src/syscall/fs-stat.c:201">
P1: FUSE stat routing drops `AT_SYMLINK_NOFOLLOW` semantics because it calls `fuse_stat_path(...)` without any flag context.</violation>
</file>
<file name="tests/test-fuse-basic.c">
<violation number="1" location="tests/test-fuse-basic.c:480">
P2: This expectation is inconsistent with the fixture: the file is marked 0644 for the current user and the daemon never denies `FUSE_OPEN`, so `O_RDWR` should not be treated as an error here.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
Comment on lines
+480
to
+483
| if (wfd >= 0 || errno != EACCES) { | ||
| fprintf(stderr, "expected O_RDWR FUSE open to fail with EACCES\n"); | ||
| return 1; | ||
| } |
There was a problem hiding this comment.
P2: This expectation is inconsistent with the fixture: the file is marked 0644 for the current user and the daemon never denies FUSE_OPEN, so O_RDWR should not be treated as an error here.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test-fuse-basic.c, line 480:
<comment>This expectation is inconsistent with the fixture: the file is marked 0644 for the current user and the daemon never denies `FUSE_OPEN`, so `O_RDWR` should not be treated as an error here.</comment>
<file context>
@@ -0,0 +1,643 @@
+ die("open(file)");
+ errno = 0;
+ int wfd = open(hello_path, O_RDWR);
+ if (wfd >= 0 || errno != EACCES) {
+ fprintf(stderr, "expected O_RDWR FUSE open to fail with EACCES\n");
+ return 1;
</file context>
Suggested change
| if (wfd >= 0 || errno != EACCES) { | |
| fprintf(stderr, "expected O_RDWR FUSE open to fail with EACCES\n"); | |
| return 1; | |
| } | |
| if (wfd < 0) | |
| die("open(file O_RDWR)"); | |
| close(wfd); |
elfuse runs entirely user-space on macOS without macFUSE, FUSE-T, FSKit,
or any host filesystem framework. Guest libfuse programs (sshfs,
ntfs-3g, fuse-overlayfs, AppImage runtimes) need the FUSE protocol to
live wholly inside the guest VM, which means /dev/fuse, mount(2)
fstype="fuse", and VFS dispatch from any guest process opening files
under the mountpoint into the daemon's /dev/fuse fd all have to be
emulated locally. This change adds that surface as an opt-in P2
feature.
src/syscall/fuse.{c,h} implements:
- Session, mount, and per-fd state tables protected by a global
fuse_lock outer / per-session lock inner. Sessions are refcounted so
in-flight reads or writes pin the lock against concurrent daemon
exit; mutex and condvar destruction defer until the last ref drops.
- Per-fd alias bindings so dup(), dup3(), and fcntl(F_DUPFD) on a FUSE
fd correctly share the underlying session or file state. Closing one
alias unbinds without tearing the session down; only the last alias
triggers the full cleanup path.
- Per-file slot refcount plus io_in_progress / io_cond to serialize
read() and getdents64() against the offset field the way Linux
f_pos_lock does. lseek() waits on io_in_progress so it cannot clobber
an in-flight read's post-update. pread() and getattr-style calls do
not advance the offset and skip the serialization to match Linux.
- Synchronous FUSE_INIT in sys_mount via fuse_send_init_locked so a
daemon that rejects INIT (negative hdr.error) propagates the errno
back through mount(); on failure the mount slot is uninstalled.
FUSE_INIT version negotiation accepts any minor when the major
matches and tolerates a short init_out from older libfuse.
- Mount tombstoning on daemon death: fuse_fd_cleanup sets
mount->session = NULL but keeps the slot's path metadata intact so a
consumer whose virtual cwd is on this mount still routes lookups
into FUSE space and surfaces -LINUX_ENOTCONN instead of silently
falling through to host-filesystem resolution. sys_mount reclaims
tombstoned slots when a new mount lands at the same path.
- Absolute-path canonicalization via fuse_canonical_abs and the
fuse_join_virtual_path helper so /mnt/fuse/./foo and
/mnt/fuse/sub/../foo route identically with /mnt/fuse/foo, and
/mnt/fuse/../etc escapes FUSE land deterministically.
- O_PATH support: fuse_open_path tracks path_only on the file slot,
skips FUSE_OPEN at allocation time, and ignores access-mode bits
per Linux open(2). Read / readdir / lseek / mmap-refusal all check
path_only and follow Linux semantics. fuse_release_common_locked
bypasses FUSE_RELEASE for O_PATH fds so a daemon never sees a
RELEASE without a prior OPEN.
- fuse_getdents64 hardened: namelen is bounded to Linux NAME_MAX (255),
the reply record is validated against the declared body length, and
the destination buffer is fixed at 280 bytes with explicit checks so
a malicious daemon cannot trigger a stack overflow.
- fuse_dev_read peeks the queue head before validating that the daemon
buffer fits, so a short read leaves the request on the queue for the
next retry instead of silently dropping it.
- Daemon writes are capped at FUSE_FRAME_CAP (8 MiB) and the negotiated
max_write is clamped to the same ceiling at FUSE_INIT time so the
read-reply path cannot negotiate larger than the dev-write path
accepts.
- fuse_resolve_at_path joins relative paths against a FUSE_DIR dirfd
or a FUSE-rooted virtual cwd via fuse_join_virtual_path, plumbed
from path_translate_at when proc-path interception did not fire.
- fuse_fchdir routes guest fchdir on a FUSE_DIR fd to
proc_cwd_set_virtual using the file slot's stored absolute path.
- procfs integration emits live FUSE entries through
fuse_append_mountinfo and fuse_append_mounts so /proc/self/mountinfo
and /proc/mounts list the mount, and /proc/filesystems reports
fuse.
- sys_execve materializes FUSE-backed binaries into a temp file via
fuse_materialize_path so the host ELF loader can read them; the temp
is unlinked on success or failure.
Errors are uniformly in Linux errno space (-LINUX_E*) end to end so
guest-visible returns and syscall-boundary translation stay consistent.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
elfuse runs entirely user-space on macOS without macFUSE, FUSE-T, FSKit, or any host filesystem framework. Guest libfuse programs (sshfs, ntfs-3g, fuse-overlayfs, AppImage runtimes) need the FUSE protocol to live wholly inside the guest VM, which means /dev/fuse, mount(2) fstype="fuse", and VFS dispatch from any guest process opening files under the mountpoint into the daemon's /dev/fuse fd all have to be emulated locally. This change adds that surface as an opt-in P2 feature.
src/syscall/fuse.{c,h} implements:
Errors are uniformly in Linux errno space (-LINUX_E*) end to end so guest-visible returns and syscall-boundary translation stay consistent.
Two test surfaces validate the work:
make checkruns it as a second stage.Summary by cubic
Adds a guest-internal FUSE transport and minimal VFS so libfuse apps run fully inside the VM without host FUSE. Implements
/dev/fuse,mount("fuse"), and routes file ops under the mount to the guest daemon.New Features
/dev/fuse,SYS_mountwithfstype="fuse", and VFS dispatch; no macFUSE or host frameworks.-ENOSYSfor now.fchdiron FUSE dirs./proc/*/mountinfo,/proc/mounts, and/proc/filesystemsincludefuse;execvematerializes FUSE-backed binaries and ELF interpreters to a temp for the host loader.-ENOTCONN; capped frame sizes; validatedgetdents64; short-read-safe device queue; uniform Linux errno.Migration
mount("fuse", "/mnt/fuse", "fuse", 0, NULL)and run a guest daemon on/dev/fuse.make checkruns an Alpine sysroot FUSE test; a unit test (test-fuse-basic) covers basic operations and edge cases.Written for commit aa449a7. Summary will update on new commits. Review in cubic